1. 程式人生 > >java第18天----生產者消費者,Lock鎖,守護執行緒,join()方法

java第18天----生產者消費者,Lock鎖,守護執行緒,join()方法

昨天知識總結:

  • 多執行緒
    • 執行緒的兩種建立方式
      • 建立Thread的子類
      • 執行緒與任務分離
    • 執行緒安全----重點
    • 單例中執行緒安全的應用
    • 執行緒的停止

執行緒

執行緒的通訊

在這裡插入圖片描述

  • 執行緒的通訊:

  • 分析:

  • 需要兩個執行緒—輸入執行緒,和輸出執行緒,

  • 需要兩個任務===輸入任務和輸出任務

  • 需要一份資料

  • 使用喚醒等待機制實現:wait()/notify()/notifyAll()等

  • 實現:印表機列印–不斷地輸入不斷地輸出

  • 分析:需要輸入任務和輸出任務共用一把鎖,保證兩個任務之間是同步的

  • 給兩個任務加一把鎖,可以是this或Object.class

  • 不建議使用Object.class原因:由於Object.class的範圍太大了,會造成不必要的錯誤

  • 使用this最合適,因為他只被兩個執行緒共享

  • 注意:只給一個執行緒加鎖,無法實現兩個執行緒的同步;

	public static void main(String[] args) {
		//1.建立資料類物件
		Des des = new Des();
		//2.建立任務類物件
		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 Input implements Runnable{ Des des; public Input() { super(); // TODO Auto-generated constructor stub } public Input(Des des) { super(); this.des = des; } @Override public void run() { // TODO Auto-generated method stub
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) { super(); this.des = des; } public Output() { super(); // TODO Auto-generated constructor stub } @Override public void run() { // TODO Auto-generated method stub while(true) { synchronized (des) { System.out.println(des); } } } } //建立資料類 class Des{ String name; String sex; @Override public String toString() { return "Des [名字=" + name + ", 性別=" + sex + "]"; } }
  • wait():讓當前的執行緒進入等待狀態,他會被放入一個池子(程式池),失去了搶CPU的能力,等待喚醒,(鎖—相當於給當前的執行緒做了一個標記)
  • notify():讓當前的執行緒從等待執行緒喚醒,相當於從池子中取出執行緒(喚醒的是同一把鎖下的任意一個執行緒)
  • notifyAll():喚醒的是同一把鎖下所有執行緒。
	public static void main(String[] args) {
		//1.建立資料類物件
		Des1 des = new Des1();
		//2.建立任務類物件
		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 Input1 implements Runnable{
	Des1 des;
	
	public Input1() {
		super();
		// TODO Auto-generated constructor stub
	}

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

	@Override
	public void run() {
		// TODO Auto-generated method stub

		int i = 0;
		while(true) {
			if(i == 0) {
				des.setData("鳳姐", "男");
			}else {
				des.setData("芙蓉姐姐", "女");
			}
			i = (i+1)%2;
		}
	}
}
//建立輸出任務
class Output1 implements Runnable{
	Des1 des;
	
	public Output1(Des1 des) {
		super();
		this.des = des;
	}

	public Output1() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public void run() {
		// TODO Auto-generated method stub

		while(true) {
			des.get();
		}

	}
}
//建立資料類
//封裝類的原則:是你的活你做,不是你的活不要做
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) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.name = name;
		this.sex = sex;
//			if(i == 1) {
//				des.name = "鳳姐";
//				des.sex = "男";
//			}else {
//				des.name = "芙蓉姐姐";
//				des.sex = "女";
//			}
//			i = (i+1)%2;
		flag = !flag;
			
		notify();//喚醒輸出執行緒
				//當執行喚醒的時候,線上程池中沒有找到被當前的所標記的執行緒,我們稱為空喚醒,空喚醒對程式沒有影響,程式允許空喚醒		
	}
	//將資料列印到控制檯-處理資料
	public synchronized void get() {
			if(flag == false) {
				try {
					wait();
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
			}
			System.out.println("Des1 [名字=" + name + ", 性別=" + sex + "]");
			flag = !flag;
			notify();
		
	}
	@Override
	public String toString() {
		return "Des1 [名字=" + name + ", 性別=" + sex + "]";
	}
}

生產者消費者

  • 多執行緒的設計模式:生產著消費者
  • 兩類:
  • 1.單生產者單消費者
  • 2.多生產者多消費者

單生產者單消費者

  • 分析:生產執行緒,消費執行緒
  • 生產任務,消費任務
  • 產品
	public static void main(String[] args) {
		//1.建立產品
		Product product= new Product();
		//2.建立生產任務,消費任務
		Producer producer = new  Producer(product);
		Consumer consumer = new Consumer(product);
		//建立生產執行緒,消費執行緒
		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) {
				// TODO Auto-generated catch block
				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 == true) {
			try {
				wait();//讓消費執行緒等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}		
			System.out.println(Thread.currentThread().getName()+ "  生產了"+name+"  產品的數量:"+count+"  產品的價格"+price);
		}
		flag = !flag;
		notify();//喚醒生產執行緒
	}
}
//建立生產任務
class Producer implements Runnable {
	Product product;
	
	public Producer() {
		super();
		// TODO Auto-generated constructor stub
	}

	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() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Consumer(Product product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.consume();
		}
	}
}

多生產者多消費者

在這裡插入圖片描述

  • 分析:生產執行緒,消費執行緒有多個
  • 生產任務,消費任務各有一個
  • 產品一個
	public static void main(String[] args) {
		//1.建立產品
		Product1 product = new Product1();
		//建立生產任務,消費任務
		Producer1 producer1 = new Producer1(product);
		Consumer1 consumer1 = new Consumer1(product);
		//3.建立生產執行緒,消費執行緒
		Thread t0 = new Thread(producer1);
		Thread t1 = new Thread(producer1);
		Thread t2 = new Thread(consumer1);
		Thread t3 = new Thread(consumer1);
		//開啟執行緒
		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) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		this.name = name;
		this.price = price;
		count += 1; 
		System.out.println(Thread.currentThread().getName()+ "  生產了"+name+"  產品的數量:"+count+"  產品的價格"+price);
		
		flag = !flag;
		notifyAll();
	}
	//為消費者準備資料
	public synchronized void consume() {
		while(flag == false) {
			try {
				wait();//讓消費執行緒等待
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}		
		System.out.println(Thread.currentThread().getName()+ "  消費了"+name+"  產品的數量:"+count+"  產品的價格"+price);
		flag = !flag;
		notifyAll();

	}
}
//建立生產任務
class Producer1 implements Runnable {
	Product1 product;
	
	public Producer1() {
		super();
		// TODO Auto-generated constructor stub
	}

	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() {
		super();
		// TODO Auto-generated constructor stub
	}

	public Consumer1(Product1 product) {
		super();
		this.product = product;
	}

	public void run() {
		while(true) {
			product.consume();
		}
	}
}

Lock鎖

  • 比較synchronized和Lock
  • 1.synchronized:從jdk1.0開始使用—隱式同步
  • synchronized(鎖物件){//獲取鎖 我們簡稱這裡的鎖成為鎖旗艦或者監聽器
  •  同步程式碼塊
    
  • }//釋放鎖
  • 2.Lock:從jdk1.5開始使用—顯示同步
  • 原理:Lock是介面,我們要通過他的子類工作
  • 首先呼叫lock的lock()方法,獲取鎖
  •  進行同步操作的程式碼
    
  • 呼叫lock的unlock()方法,釋放鎖
  • 使用場景總結:當使用多生產者多消費者的時候,使用lock,其他的使用synchronized
  • 使用效率上lock比synchronized高。 在這裡插入圖片描述
public static void main(String[] args) {
		//1.建立產品
		Product1 product = new Product1();
		//建立生產任務,消費任務
		Producer1 producer1 = new Producer1(product);
		Consumer1 consumer1 = new Consumer1(product);
		//3.建立生產執行緒,消費執行緒
		Thread t0 = new Thread(producer1);
		Thread t1 = new Thread(producer1);
		Thread t2 = new Thread(consumer1);
		Thread t3 = new Thread(consumer1);
		//開啟執行緒
		t0.start();
		t1.start();
		t2.start();
		t3.start();
	}
}
//建立產品類
class Product2{
	String name;
	double price;
	int count;
	//建立Lock的物件
	Lock lock = new ReentrantLock();
	boolean flag = false;//用來喚醒與等待之間切換
	//建立用於生產者執行緒的Condition物件
	Condition conditionProducer = lock.newCondition();
	//建立用於消費者執行緒的Condition物件
	Condition conditionConsumer = lock.newCondition();
	
	//為生產者準備資料
	public  void setProduce(String name,double price) {
		try {
			lock.unlock();
			while(flag == true) {
				try {
					conditionProducer.await();
				} catch (InterruptedException e) {