1. 程式人生 > >java多執行緒--練習題總結

java多執行緒--練習題總結

目錄

 

練題1:編寫程式實現,子執行緒迴圈3次,接著主執行緒迴圈5次,接著再子執行緒迴圈3次,主執行緒迴圈5次,如此反覆,迴圈3次.

練習題2:設計四個執行緒,其中兩個執行緒每次對變數i加1,另外兩個執行緒每次對i減1.

練習題3:自己編寫程式碼,實現生產者-消費者模型功能.內容自由發揮,只需要表達思想.

練習題4:現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行?


練題1:編寫程式實現,子執行緒迴圈3次,接著主執行緒迴圈5次,接著再子執行緒迴圈3次,主執行緒迴圈5次,如此反覆,迴圈3次.

第一種實現方式:使用synchronized關鍵字

package com.lianxi;

public class ThreadMain2 {
 
	public static void main(String[] args) { 
		final ThreadFunction2 f2 = new ThreadFunction2();
		
		// 子執行緒迴圈3次
		new Thread(new Runnable(){
			public void run(){
				for(int i=0;i<3;i++){
					f2.subFunction();
				}
			}
		}).start();
		
		// 主執行緒迴圈3次
		for(int i=0;i<3;i++){
			f2.mainFunction();
		} 
	}

}
package com.lianxi;

// 編寫功能類,實現子執行緒和主執行緒的功能
public class ThreadFunction2 {
	
	private boolean flag = false;
	
	// 主執行緒要實現的功能
	public synchronized void mainFunction(){
		while(!flag){
			try {
				this.wait();
			} catch (InterruptedException e) { 
				e.printStackTrace();
			}
		}
		for(int i=0;i<5;i++){
			System.out.println("mainFunction"+i);
		} 
		this.notify();
		flag = false;
	}
	
	// 子執行緒要實現的功能
	public synchronized void subFunction(){
		while(flag){
			try {
				this.wait();
			} catch (InterruptedException e) { 
				e.printStackTrace();
			} 
		}
		for(int i=0;i<3;i++){
			System.out.println("subFunction"+i);
		} 
		this.notify();
		flag = true;
	}

}

輸出結果:

subFunction0
subFunction1
subFunction2
mainFunction0
mainFunction1
mainFunction2
mainFunction3
mainFunction4
subFunction0
subFunction1
subFunction2
mainFunction0
mainFunction1
mainFunction2
mainFunction3
mainFunction4
subFunction0
subFunction1
subFunction2
mainFunction0
mainFunction1
mainFunction2
mainFunction3
mainFunction4

第二種實現方式:使用 lock 鎖和 Condition 介面

package com.lianxi;

public class ThreadMain {
 
	public static void main(String[] args) { 
		final ThreadFunction f = new ThreadFunction();
		
		// 子執行緒迴圈3次
		new Thread(new Runnable(){
			public void run(){
		        for(int i=0;i<3;i++){
		        	f.subFunction();
		        }
			}
		}).start();
		
		// 主執行緒迴圈3次
		for(int i=0;i<3;i++){
        	f.mainFunction();
        }

	}

}
package com.lianxi;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

// 編寫功能類,實現子執行緒和主執行緒的功能
public class ThreadFunction extends Thread{
	
	private boolean flag = false;
	Lock lock = new ReentrantLock();
	Condition con = lock.newCondition();
	
    // 主執行緒要實現的功能
	public void mainFunction(){ 
		System.out.println("1.主執行緒開始"+" -- flag="+flag);
		lock.lock();
		try{ 
			while(!flag){ 
				try {
					System.out.println("2.主執行緒等待"+" -- flag="+flag);
					con.await();   // 使當前執行緒加入 await() 等待佇列中,並釋放當鎖,當其他執行緒呼叫signal()會重新請求鎖。與Object.wait()類似。
				} catch (InterruptedException e) { 
					e.printStackTrace();
				} 
			}  
			System.out.println("7.主執行緒開始迴圈5次"+" -- flag="+flag);
			for(int i=0;i<5;i++){ 
				System.out.println("mainFunction"+i+" -- flag="+flag); 
			}
			flag = false;
			System.out.println("8.喚醒子執行緒"+" -- flag="+flag);
			con.signal();  // 喚醒一個在 await()等待佇列中的執行緒。與Object.notify()相似
		}finally{
			lock.unlock(); 
		}
		 
		
		
	}
	
    // 子執行緒要實現的功能
	public void subFunction(){ 
		System.out.println("3.子執行緒開始"+" -- flag="+flag);
		lock.lock(); 
		try{
			while(flag){
				try {
					System.out.println("6.子執行緒等待"+" -- flag="+flag);
					con.await();  // 使當前執行緒加入 await() 等待佇列中,並釋放當鎖,當其他執行緒呼叫signal()會重新請求鎖。與Object.wait()類似。
				} catch (InterruptedException e) { 
					e.printStackTrace();
				}  
			}
			System.out.println("4.子執行緒開始迴圈3次"+" -- flag="+flag);
			for(int i=0;i<3;i++){
				System.out.println("subFunction"+i+" -- flag="+flag);
			}
			flag = true;
			System.out.println("5.喚醒主執行緒"+" -- flag="+flag);
			con.signal(); // 喚醒一個在 await()等待佇列中的執行緒。與Object.notify()相似
		}finally{
			lock.unlock();  
		}  
	} 
}

輸出結果:

1.主執行緒開始 -- flag=false
2.主執行緒等待 -- flag=false
3.子執行緒開始 -- flag=false
4.子執行緒開始迴圈3次 -- flag=false
subFunction0 -- flag=false
subFunction1 -- flag=false
subFunction2 -- flag=false
5.喚醒主執行緒 -- flag=true
3.子執行緒開始 -- flag=true
7.主執行緒開始迴圈5次 -- flag=true
mainFunction0 -- flag=true
mainFunction1 -- flag=true
mainFunction2 -- flag=true
mainFunction3 -- flag=true
mainFunction4 -- flag=true
8.喚醒子執行緒 -- flag=false
1.主執行緒開始 -- flag=false
2.主執行緒等待 -- flag=false
4.子執行緒開始迴圈3次 -- flag=false
subFunction0 -- flag=false
subFunction1 -- flag=false
subFunction2 -- flag=false
5.喚醒主執行緒 -- flag=true
3.子執行緒開始 -- flag=true
6.子執行緒等待 -- flag=true
7.主執行緒開始迴圈5次 -- flag=true
mainFunction0 -- flag=true
mainFunction1 -- flag=true
mainFunction2 -- flag=true
mainFunction3 -- flag=true
mainFunction4 -- flag=true
8.喚醒子執行緒 -- flag=false
1.主執行緒開始 -- flag=false
2.主執行緒等待 -- flag=false
4.子執行緒開始迴圈3次 -- flag=false
subFunction0 -- flag=false
subFunction1 -- flag=false
subFunction2 -- flag=false
5.喚醒主執行緒 -- flag=true
7.主執行緒開始迴圈5次 -- flag=true
mainFunction0 -- flag=true
mainFunction1 -- flag=true
mainFunction2 -- flag=true
mainFunction3 -- flag=true
mainFunction4 -- flag=true
8.喚醒子執行緒 -- flag=false

Lock和synchronized的選擇

  總結來說,Lock和synchronized有以下幾點不同:

  1)Lock是一個介面,而synchronized是Java中的關鍵字,synchronized是內建的語言實現;

  2)synchronized在發生異常時,會自動釋放執行緒佔有的鎖,因此不會導致死鎖現象發生;而Lock在發生異常時,如果沒有主動通過unLock()去釋放鎖,則很可能造成死鎖現象,因此使用Lock時需要在finally塊中釋放鎖;

  3)Lock可以讓等待鎖的執行緒響應中斷,而synchronized卻不行,使用synchronized時,等待的執行緒會一直等待下去,不能夠響應中斷;

  4)通過Lock可以知道有沒有成功獲取鎖,而synchronized卻無法辦到。

  5)Lock可以提高多個執行緒進行讀操作的效率。

  在效能上來說,如果競爭資源不激烈,兩者的效能是差不多的,而當競爭資源非常激烈時(即有大量執行緒同時競爭),此時Lock的效能要遠遠優於synchronized。所以說,在具體使用時要根據適當情況選擇。

 

 

練習題2:設計四個執行緒,其中兩個執行緒每次對變數i加1,另外兩個執行緒每次對i減1.

package com.lianxi;

// 面試題2:設計四個執行緒,其中兩個執行緒每次對變數i加1,另外兩個執行緒每次對i減1.
public class ThreadTest { 
      
    public static void main(String[] args){ 
    	
    	final ThreadTestFunction f = new ThreadTestFunction();
    	
    	Thread t1 = new Thread(new Runnable(){
    		public void run(){
    			f.add();
    		} 
    	});
    	
    	Thread t2 = new Thread(new Runnable(){
    		public void run(){
    			f.add();
    		} 
    	});
    	
    	Thread t3 = new Thread(new Runnable(){
    		public void run(){
    			f.sub();;
    		} 
    	});
    	
    	Thread t4 = new Thread(new Runnable(){
    		public void run(){
    			f.sub();
    		} 
    	}); 
    	 
    	t1.start();
    	t2.start();
    	t3.start();
    	t4.start();
    }  
}
package com.lianxi;

public class ThreadTestFunction { 
	
	private int i=0;
	
	public synchronized void add(){
		i++;
		System.out.println(Thread.currentThread().getName()+"add:i="+i);
	}
	
	public synchronized void sub(){
		i--;
		System.out.println(Thread.currentThread().getName()+"sub:i="+i);
	}
}

輸出結果:

Thread-0add:i=1
Thread-3sub:i=0
Thread-2sub:i=-1
Thread-1add:i=0

練習題3:自己編寫程式碼,實現生產者-消費者模型功能.內容自由發揮,只需要表達思想.

package com.lianxi;

public class ThreadProductTest {

	/**
	 * 面試題3:自己編寫程式碼,實現生產者-消費者模型功能.內容自由發揮,只需要表達思想.
	 * 程式碼中,自定義一個學生類,有name和age屬性,屬於共享物件,
	 * 生產者負責為studnet物件賦值,消費者負責打印出student物件的name和age的值,
	 * 當生產者賦值完以後通知消費者來列印,消費者列印完以後,通知生產者重新設定.
	 */
	public static void main(String[] args) { 
		
		final ThreadStudent s = new ThreadStudent();
		
		// 模擬生產者執行緒類
		Thread inputThread = new Thread(new Runnable(){
			public void run(){ 
				int num = 10;
				while(num>0){
					if(num % 2 == 0){
						s.set("劉德華", 56); 
					}else{
						s.set("倉木麻衣", 36); 
					}
					num--;
				}  
			}
		},"生產者");
		
		// 模擬消費者執行緒類
		Thread outputThread = new Thread(new Runnable(){
			public void run(){  
				int num = 10;
				while(num>0){
					s.get();
					num--;
				}  
			}
		},"消費者");
		
		inputThread.start();
		outputThread.start();
	}
	 

}
package com.lianxi;

//學生實體類作為共享資源
public class ThreadStudent { 

	public String name;  // 姓名
	public int age;  // 年齡
	public boolean flag = false;  // 標記變數,判斷當前學生物件是否已建立賦值好
	
	//生產者的功能  ,為studnet物件賦值
	public synchronized void set(String name,int age){ 
		if(flag){
			try {
				this.wait();
			} catch (InterruptedException e) { 
				e.printStackTrace();
			}
		} 
		
		this.name = name;
		this.age = age;
		System.out.println(Thread.currentThread().getName()+"  student:name="+name+",age="+age+" -- flag="+flag);
		this.flag = true;
		this.notify(); // 喚醒 消費者執行緒 
		
	}
	
	// 消費者的功能,列印sutdent物件的內容
	public synchronized void get(){ 
		if(!flag){
			try {
				this.wait();
			} catch (InterruptedException e) { 
				e.printStackTrace();
			} 
		}
		System.out.println(Thread.currentThread().getName()+"  student:name="+name+",age="+age+" -- flag="+flag);
		
		this.flag = false;
		this.notify(); // 喚醒 生產者執行緒
		
	}

}

輸出結果:

生產者  student:name=劉德華,age=56 -- flag=false
消費者  student:name=劉德華,age=56 -- flag=true
生產者  student:name=倉木麻衣,age=36 -- flag=false
消費者  student:name=倉木麻衣,age=36 -- flag=true
生產者  student:name=劉德華,age=56 -- flag=false
消費者  student:name=劉德華,age=56 -- flag=true
生產者  student:name=倉木麻衣,age=36 -- flag=false
消費者  student:name=倉木麻衣,age=36 -- flag=true
生產者  student:name=劉德華,age=56 -- flag=false
消費者  student:name=劉德華,age=56 -- flag=true
生產者  student:name=倉木麻衣,age=36 -- flag=false
消費者  student:name=倉木麻衣,age=36 -- flag=true
生產者  student:name=劉德華,age=56 -- flag=false
消費者  student:name=劉德華,age=56 -- flag=true
生產者  student:name=倉木麻衣,age=36 -- flag=false
消費者  student:name=倉木麻衣,age=36 -- flag=true
生產者  student:name=劉德華,age=56 -- flag=false
消費者  student:name=劉德華,age=56 -- flag=true
生產者  student:name=倉木麻衣,age=36 -- flag=false
消費者  student:name=倉木麻衣,age=36 -- flag=true

練習題4:現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行?

package com.lianxi;

import java.util.Date;

public class ThreadSortTest {

	/**
	 * 現在有T1、T2、T3三個執行緒,你怎樣保證T2在T1執行完後執行,T3在T2執行完後執行?
	 */
	public static void main(String[] args) { 
		
		Thread t1 = new Thread(new Runnable(){
			public void run(){
				System.out.println(Thread.currentThread().getName()+" -- "+System.currentTimeMillis());
			} 
		},"t1");
		
		Thread t2 = new Thread(new Runnable(){
			public void run(){
				System.out.println(Thread.currentThread().getName()+" -- "+System.currentTimeMillis());
			} 
		},"t2");
		
		Thread t3 = new Thread(new Runnable(){
			public void run(){
				System.out.println(Thread.currentThread().getName()+" -- "+System.currentTimeMillis());
			} 
		},"t3");
		
		t1.start();
		try {
			t1.join();  // t1.join()需要等t1.start()執行之後執行才有效果
		} catch (InterruptedException e) { 
			e.printStackTrace();
		}
		t2.start();
		try {
			t2.join();  // t2.join()需要等t2.start()執行之後執行才有效果
		} catch (InterruptedException e) { 
			e.printStackTrace();
		}
		t3.start();

	}

}

輸出結果:

t1 -- 1540456352889
t2 -- 1540456352889
t3 -- 1540456352890

轉載請註明出處:https://blog.csdn.net/yuzongtao/article/details/83378538