線程通信問題--生產者和消費者問題
阿新 • • 發佈:2019-04-19
同步方法 obj 包含 not tac 問題: err 為我 方法
一、問題引入:首先實現一個線程通信的實例,使用兩個線程交替打印輸出100以內的數字。
代碼實現如下:
1 package com.baozi.exer; 2 3 public class CommunicationTest { 4 public static void main(String[] args) { 5 Number number = new Number(); 6 Thread t1 = new Thread(number); 7 Thread t2 = new Thread(number);8 t1.setName("線程1"); 9 t2.setName("線程2"); 10 t1.start(); 11 t2.start(); 12 } 13 14 } 15 16 class Number implements Runnable { 17 private int number = 1; 18 19 @Override 20 public void run() { 21 while (true) { 22 synchronized(this) { 23 notify(); 24 if (number <= 100) { 25 try { 26 Thread.sleep(100); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 System.out.println(Thread.currentThread().getName() + ":" + number);31 number++; 32 try { 33 wait(); 34 } catch (InterruptedException e) { 35 e.printStackTrace(); 36 } 37 } else { 38 break; 39 } 40 } 41 } 42 } 43 }
wait()、notify()、notifyAll()三個方法的介紹:
- wait():某個對象調用wait()方法能讓當前線程阻塞,並且當前線程還會釋放所擁有的鎖
- 調用某個對象的notify()方法能夠喚醒一個正在等待這個對象的monitor的線程,如果有多個線程都在等待這個對象的monitor,則只能喚醒其中一個線程;
- 調用notifyAll()方法能夠喚醒所有正在等待這個對象的monitor的線程;
備註:wait()、notify()、notifyAll()三個方法都是定義在同步代碼塊或者是同步方法中的,並且都是同步代碼塊或者同步方法的同步監視器進行調用這些方法。
二、為什麽這三個方法都是定義在Object類中,而不是定義在Thread類中?
這三個方法都是定義在Object類中,因為我們知道,這三個方法都是存在於同步代碼塊或者同步方法中的,而且必須是該代碼塊的同步監視器進行調用這些方法,那麽因為同步監視器是任何一個類的對象都可以充當,如果要保證任何一個類的對象都有這三個方法並且能進行調用,就只能把這三個方法方法所有類的父類--Object類中。
三、Sleep()方法和Wait()方法有什麽異同?
相同點:一旦執行這兩個方法,都會使當前線程進入阻塞狀態
不同點:
- 兩個方法聲明的位置不同:sleep()方法是在Thread類中聲明為一個靜態方法,而wait()方法是定義在Object類中
- 兩個方法的使用位置不同:sleep可以在任何位置使用,但是wait()方法只能在同步代碼塊或者是同步方法中使用
- sleep()方法執行完之後雖然會使當前線程進入阻塞狀態,但是並不會使當前線程釋放所擁有的鎖,但是wait()方法不但會讓當前的線程進入阻塞狀態,還會是當前的線程釋放擁有的鎖。
四、經典的線程通信問題:生產者和消費者問題
1 package com.baozi.java2; 2 3 /** 4 * 線程通信的應用:生產者消費者問題 5 * 分析: 6 * 1、是否是多線程的問題:是多線程問題,至少要包含生產者線程和消費者線程 7 * 2、是否有共享數據的問題?有共享數據:產品(這裏只用了一個產品的個數進行表示) 8 * 3、如何解決線程的安全問題:使用同步機制 9 * 4、如何解決線程之間的通信問題?wait()和notify()/notifyAll() 10 */ 11 //店員類 12 class Clerk { 13 private int productNumber = 0; 14 //生產產品 15 public synchronized void produceProductor() { 16 if(productNumber<20){ 17 productNumber++; 18 System.out.println(Thread.currentThread().getName()+":開始生產第"+productNumber+"個產品"); 19 //只要生產者生產出至少一個產品,就能喚醒消費者線程進行消費 20 notify(); 21 }else{ 22 //當產品的個數大於20的時候,生產者線程就要進入阻塞狀態,等待消費者線程消費產品之後在進行生產 23 try { 24 wait(); 25 } catch (InterruptedException e) { 26 e.printStackTrace(); 27 } 28 } 29 } 30 //消費產品 31 public synchronized void consumerProduct() { 32 if(productNumber>0){ 33 System.out.println(Thread.currentThread().getName()+":開始消費第"+productNumber+"個產品"); 34 productNumber--; 35 //只要消費者線程進行了一次消費,就能喚醒生產者線程進行生產 36 notify(); 37 }else{ 38 //當產品的個數小於等於0的時候,消費者線程就要進入阻塞狀態,等待生產者生產出產品之後在進行消費 39 try { 40 wait(); 41 } catch (InterruptedException e) { 42 e.printStackTrace(); 43 } 44 } 45 } 46 } 47 48 //生產者類 49 class Productor extends Thread { 50 private Clerk clerk; 51 52 public Productor(Clerk clerk) { 53 this.clerk = clerk; 54 } 55 56 @Override 57 public void run() { 58 System.out.println(Thread.currentThread().getName() + ":開始生產產品....."); 59 while (true) { 60 try { 61 Thread.sleep(100); 62 } catch (InterruptedException e) { 63 e.printStackTrace(); 64 } 65 clerk.produceProductor(); 66 } 67 } 68 } 69 70 //消費者類 71 class Consumer extends Thread { 72 private Clerk clerk; 73 74 public Consumer(Clerk clerk) { 75 this.clerk = clerk; 76 } 77 78 @Override 79 public void run() { 80 System.out.println(Thread.currentThread().getName() + ":開始消費產品....."); 81 while (true) { 82 try { 83 Thread.sleep(100); 84 } catch (InterruptedException e) { 85 e.printStackTrace(); 86 } 87 clerk.consumerProduct(); 88 } 89 } 90 } 91 //測試類,創建一個生產者生產商品,創建一個消費者消費產品 92 public class ProductorTest { 93 public static void main(String[] args) { 94 Clerk clerk = new Clerk(); 95 Thread p1 = new Productor(clerk); 96 p1.setName("生產者1"); 97 Thread c1 = new Consumer(clerk); 98 c1.setName("消費者1"); 99 p1.start(); 100 c1.start(); 101 } 102 }
線程通信問題--生產者和消費者問題