1. 程式人生 > >java多執行緒:執行緒的通訊、喚醒等待機制、生產消費者模式、Lock

java多執行緒:執行緒的通訊、喚醒等待機制、生產消費者模式、Lock

執行緒的通訊:印表機列印–不斷的輸入輸出

package com.qianfeng.test;
/*
 * 執行緒的通訊:
 * 分析:
 * 需要兩個執行緒--輸入執行緒和輸出執行緒
 * 需要兩個任務--輸入任務和輸出任務
 * 需要一份資料
 * 
 * 實現:印表機列印--不斷的輸入不斷的輸出
 * 
 */
public class Demo2 {

	public static void main(String[] args) {
		
		//1.建立資料類物件
		Des des =new Des();
		//建立任務類物件並繫結資料
		Input input = new Input(des);
		Output output = new Output(des);
		//3.建立執行緒
		Thread inputThread = new Thread(input);
		Thread outputThread = new Thread(output);
		//4.開啟執行緒
		inputThread.start();
		outputThread.start();
		
	}
}

//建立資料類
class Des{
	String name;
	String sex;
}

//建立輸入任務
class Input implements Runnable{

	Des des;
	public Input(Des des){
		this.des=des;
	}
	
	public void run() {
		/*
		 * 分析:需要輸入任務與輸出任務共用一把鎖,保證兩個任務之間是同步的。
		 * 給兩個任務加一把鎖,鎖可以是des或Object.class
		 * 
		 * 不建議使用Object.class,因為Object.class應用範圍太大,其它地方可能也會用到。
		 * 使用des最合適,因為它只被兩個執行緒共享。
		 * 
		 * 注意:只給一個任務加鎖無法實現兩個執行緒的同步。
		 */
		int i = 0;
		while(true){
			synchronized (des) {
				if (i==1) {
					des.name = "鳳姐";
					des.sex = "男" ;
				}else {
					des.name = "芙蓉姐姐";
					des.sex = "女" ;
				}
				i=(i+1)%2;
			}
		}
	}
}

//建立輸出任務
class Output implements Runnable{

	Des des;
	public Output(Des des){
		this.des=des;
	}
	
	public void run() {
		while(true){
			synchronized (des) {
				System.out.println("姓名:"+des.name+"  性別"+des.sex);
			}
		}
	}
}

喚醒等待機制,wait()/notify()/notifyAll()

package com.qianfeng.test;

/*
 * 執行緒的通訊:
 * 分析:
 * 需要兩個執行緒--輸入執行緒和輸出執行緒
 * 需要兩個任務--輸入任務和輸出任務
 * 需要一份資料
 * 
 * 實現:印表機列印--一次輸入一次輸出
 * 
 * 使用喚醒等待機制實現:wait()/notify()/notifyAll()等
 * wait():讓當前的執行緒進入等待的狀態,他會被放入一個池子(執行緒池),失去了搶cpu的能力,等待喚醒。(鎖--相當於給當前執行緒做了一個標記)
 * notify():讓當前的執行緒從等待執行緒喚醒,相當於從池子中取出執行緒。(喚醒的是同一把鎖下的任意一個執行緒)
 * notifyAll():喚醒的是同一把鎖下的所有執行緒。
 */
public class Demo3 {

	public static void main(String[] args) {

		// 1.建立資料類物件
		Des1 des = new Des1();
		// 建立任務類物件並繫結資料
		Input1 input = new Input1(des);
		Output1 output = new Output1(des);
		// 3.建立執行緒
		Thread inputThread = new Thread(input);
		Thread outputThread = new Thread(output);
		// 4.開啟執行緒
		inputThread.start();
		outputThread.start();

	}
}

// 建立資料類
// 封裝類的原則:是你的活你做,不是你的活不要做。
class Des1 {
	String name;
	String sex;
	boolean flag = false;// 建立一個標識,控制喚醒與等待的切換。

	// 獲取資料--處理輸入
	public synchronized void setData(String name, String sex) {
		if (flag == true) {
			try {// 讓輸入執行緒等待,當flag值為true的時候
					// 在執行程式碼的時候,這裡對應的是哪個執行緒,鎖物件操作的就是哪個執行緒。
				wait();// 只要一執行wait,執行緒會立刻停在這裡,等待下次喚醒。
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}

		this.name = name;
		this.sex = sex;

		flag = !flag;
		//// 喚醒輸出執行緒。
		// 當執行喚醒的時候,線上程池中沒有找到被當前的鎖標記的執行緒,我們稱為空喚醒,空喚醒對程式沒有影響,程式允許空喚醒。
		notify();

	}

	// 將資料列印到控制檯--處理輸出
	public synchronized void getData() {
		if (flag == false) {
			try {
				wait();
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		System.out.println("姓名:" + name + "  性別" + sex);
		flag = !flag;
		notify();
	}

}

// 建立輸入任務
class Input1 implements Runnable {

	Des1 des;
	public Input1(Des1 des) {
		this.des = des;
	}

	public void run() {
		
		int i = 0;
		while (true) {
			if (i == 1) {
				des.setData("鳳姐", "男");
			} else {
				des.setData("芙蓉姐姐", "女");
			}
			i = (i + 1) % 2;
		}
	}
}

// 建立輸出任務
class Output1 implements Runnable {

	Des1 des;
	public Output1(Des1 des) {
		this.des = des;
	}

	public void run() {
		while(true){
			des.getData();
		}

	}
}

多執行緒設計模式:單生產者單消費者

package com.qianfeng.test;
/*
 * 多執行緒的設計模式:生產者消費者
 * 兩類:
 * 1.單生產者單消費者
 * 2.多生產者多消費者
 * 
 * 先研究單生產者單消費者
 * 分析:
 * 生產執行緒,消費執行緒
 * 生產任務,消費任務
 * 產品
 */
public class Demo4 {
	public static void main(String[] args) {
		//1.建立產品
		Product product = new Product();
		//2.建立生產任務,消費任務
		Producer producer = new Producer(product);
		Consumer consumer = new Consumer(product);
		
		//3.建立生產執行緒,消費執行緒
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(consumer);
		
		//4.開啟執行緒
		t1.start();
		t2.start();
	}
}

//建立產品類
class Product{
	String name;
	double price;
	int count;
	
	boolean flag = false;//用來在喚醒與等待之間進行切換
	
	//為生產準備資料
	public synchronized void setProduce(String name,double price){
		if (flag == true) {
			try {
				wait();//讓生產執行緒等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		this.name = name;
		this.price = price;
		
		System.out.println(Thread.currentThread().getName()+"  生產了:"+name+"  產品的數量:"+count+"  產品的價格:"+price);
		count++;
		
		flag = ! flag;
		notify();//喚醒消費執行緒
	}
	//為消費準備資料
	public  synchronized void consume() {
		if (flag == false) {
			try {
				wait();//讓消費執行緒等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println(Thread.currentThread().getName()+"  消費了:"+name+"  產品的數量:"+count+"  產品的價格:"+price);
		
		flag = ! flag;
		notify();//喚醒生產執行緒
	}
}

//建立生產任務
class Producer implements Runnable{
	Product product;
	public Producer(Product product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
	}
}
//建立消費任務
class Consumer implements Runnable{
	Product product;
	public Consumer(Product product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.consume();
		}
	}
}

多執行緒設計模式:多生產者多消費者

package com.qianfeng.test;
/*
 * 多執行緒的設計模式:生產者消費者
 * 兩類:
 * 1.單生產者單消費者
 * 2.多生產者多消費者
 * 
 * 研究多生產者多消費者
 * 分析:
 * 生產執行緒,消費執行緒有多個
 * 生產任務,消費任務各有一個
 * 產品一個
 */
public class Demo5 {
	public static void main(String[] args) {
		//1.建立產品
		Product1 product = new Product1();
		//2.建立生產任務,消費任務
		Producer1 producer = new Producer1(product);
		Consumer1 consumer = new Consumer1(product);
		
		//3.建立生產執行緒,消費執行緒
		Thread t0 = new Thread(producer);
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(consumer);
		Thread t3 = new Thread(consumer);
		
		//4.開啟執行緒
		t0.start();
		t1.start();
		t2.start();
		t3.start();
		
	}
}

//建立產品類
class Product1{
	String name;
	double price;
	int count;
	
	boolean flag = false;//用來在喚醒與等待之間進行切換
	
	//為生產準備資料
	public synchronized void setProduce(String name,double price){
		while (flag == true) {
			try {
				wait();//讓生產執行緒等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		this.name = name;
		this.price = price;
		
		System.out.println(Thread.currentThread().getName()+"  生產了:"+name+"  產品的數量:"+count+"  產品的價格:"+price);
		count++;
		
		flag = ! flag;
		//notify();//喚醒消費執行緒
		notifyAll();
	}
	//為消費準備資料
	public  synchronized void consume() {
		while (flag == false) {
			try {
				wait();//讓消費執行緒等待
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
		}
		
		System.out.println(Thread.currentThread().getName()+"  消費了:"+name+"  產品的數量:"+count+"  產品的價格:"+price);
		
		flag = ! flag;
		//notify();//喚醒生產執行緒
		notifyAll();
	}
}

//建立生產任務
class Producer1 implements Runnable{
	Product1 product;
	public Producer1(Product1 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
	}
}
//建立消費任務
class Consumer1 implements Runnable{
	Product1 product;
	public Consumer1(Product1 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.consume();
		}
	}
}

Lock,Condition。Lock與synchronized的比較

package com.qianfeng.test;

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

/*
 * Lock:
 * 比較synchronized和Lock
 * 1.synchronized:從jdk1.0開始使用----隱式同步
 * 	synchronized(鎖物件){//獲取鎖   我們將這裡的鎖稱為鎖旗艦或者監聽器
 * 		同步的程式碼
 * }//釋放鎖
 * 2.Lock:從jdk1.5開始使用----顯示同步
 * 原理:Lock是介面,我們要通過他的子類工作
 * 具體的工作流程:
 * 	首先呼叫lock的lock()方法,獲取鎖
 *      進行同步操作的程式碼
 *  呼叫lock的unlock()方法,釋放鎖
 *  
 *  使用場景總結:
 *  1.當進行多生產者的消費者的時候,使用lock,其他的使用synchronized
 *  
 *  使用效率上lock比synchronized高.
 */

public class Demo6 {
	public static void main(String[] args) {
		//1.建立產品
		Product2 product = new Product2();
		//2.建立生產任務,消費任務
		Producer2 producer = new Producer2(product);
		Consumer2 consumer = new Consumer2(product);
		
		//3.建立生產執行緒,消費執行緒
		Thread t0 = new Thread(producer);
		Thread t1 = new Thread(producer);
		Thread t2 = new Thread(consumer);
		Thread t3 = new Thread(consumer);
		
		//4.開啟執行緒
		t0.start();
		t1.start();
		t2.start();
		t3.start();
		
	}
}

//建立產品類
class Product2{
	String name;
	double price;
	int count;
	
	boolean flag = false;//用來在喚醒與等待之間進行切換
	
	//建立Lock的物件
	Lock lock = new ReentrantLock();
	
	//建立用於生產執行緒的Condition物件
	Condition proCon = lock.newCondition();
	//建立用於消費執行緒的Condition物件
	Condition conCon = lock.newCondition();
	
	//為生產準備資料
	public  void setProduce(String name,double price){
		try {
			lock.lock();//獲取鎖
			while (flag == true) {
				try {
					//wait();//讓生產執行緒等待
					proCon.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			this.name = name;
			this.price = price;
			
			System.out.println(Thread.currentThread().getName()+"  生產了:"+name+"  產品的數量:"+count+"  產品的價格:"+price);
			count++;
			
			flag = ! flag;
			//notify();//喚醒消費執行緒
			//notifyAll();
			conCon.signal();
		
		} finally {//必須執行的程式碼
			lock.unlock();//釋放鎖
		}
		
	}
	//為消費準備資料
	public   void consume() {
	    try {
			lock.lock();
			while (flag == false) {
				try {
					//wait();//讓消費執行緒等待
					conCon.await();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			
			System.out.println(Thread.currentThread().getName()+"  消費了:"+name+"  產品的數量:"+count+"  產品的價格:"+price);
			
			flag = ! flag;
			//notify();//喚醒生產執行緒
			//notifyAll();
			proCon.signal();
		} finally {
			lock.unlock();
		}
		
	}
}

//建立生產任務
class Producer2 implements Runnable{
	Product2 product;
	public Producer2(Product2 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.setProduce("bingbing", 10);
		}
	}
}
//建立消費任務
class Consumer2 implements Runnable{
	Product2 product;
	public Consumer2(Product2 product) {
		super();
		this.product = product;
	}
	public void run() {
		while (true) {
			product.consume();
		}
	}
}

守護執行緒,setDaemon。

package com.qianfeng.test;
/*
 * 守護執行緒:相當於後臺執行緒,依賴於前臺執行緒。正常情況下,當前臺執行緒結束的時候,不管後臺執行緒有沒有結束,都會立即結束。
 * 典型的守護執行緒:垃圾回收執行緒。
 * 
 */
public class Demo7 {

	public static void main(String[] args) {
		
		Test test = new Test();
		Thread thread = new Thread(test);
		/*
		 * 當程式呼叫setDaemon的時候,並且將引數設定為true,他就變成了守護執行緒。
		 * 注意:這個方法一定要在start方法之前呼叫。
		 */
		thread.setDaemon(true);
		thread.start();
		
		int i = 0;
		while(true){
			if (i++ == 10 ) {
				System.out.println(Thread.currentThread().getName()+" i"+i);
				break;//主執行緒結束
			}
		}
	}
}

class Test implements Runnable{

	public void run() {
		while(true){
			System.out.println("守護執行緒");
		}
		
	}
}

join方法

package com.qianfeng.test;
/*
 * join()方法:
 * 原理:程式一旦呼叫了join方法,他的優先順序會高於主執行緒。意思是說,主執行緒會等當前執行緒執行完後再去執行。
 * 注意:這個執行緒的優先順序只比main高,對其它的執行緒沒有影響。
 * 
 */
public class Demo8 {

	public static void main(String[] args) {
		
		Dog dog = new Dog();
		Thread thread0 = new Thread(dog);
		Thread thread1 = new Thread(dog);
		thread0.start();
		thread1.start();
		
		/*
		 * 當執行緒開始工作後,讓thread0呼叫join()方法,它的優先順序會高於main執行緒。
		 * 注意:join()方法必須線上程開始工作後執行
		 */
		try {
			thread0.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+" i"+i);
		}
		
	}
}

class Dog implements Runnable{

	public void run() {
		for (int i = 0; i < 10; i++) {
			System.out.println(Thread.currentThread().getName()+" i"+i);
		}
		
	}
	
}