天天看點

java多線程(三)生産消費模型(wait和notify)一、Object自帶的方法二、生産消費模式

生産消費模型

  • 一、Object自帶的方法
    • 1.wait
    • 2.notify和notifyAll
  • 二、生産消費模式
    • 1.前言
    • 2.uml圖
    • 3.代碼
    • 4.結果
    • 5. 總結

一、Object自帶的方法

1.wait

1.1 wait()方法,在其他線程調用notify和notifyAll方法之前,目前線程等待。

什麼意思了,就是在當某一個線程在執行一個任務時,這個任務是一個方法,方法中有一個wait方法,當線程執行到這句時,就會停止執行,目前線程處于阻塞狀态,但是會釋放對象鎖,意思就是會把鑰匙交出去,這時候其他線程試圖拿到了此對象鎖,進行執行該任務,隻有當執行到了任務裡面的notify和notifyAll方法時,那個處于阻塞狀态的線程才會被喚醒。

1.2 wait(long timeout)這個方法呢,和上一個方法類似,但是不同的一點是,就算沒有其他線程來調用notify方法,當超過了它傳入的時候後,線程也會被喚醒。

2.notify和notifyAll

2.1 notify方法剛才應用上上面講的wait()方法,調用notify方法後,便會喚醒在執行該任務的等待的單個線程。
2.2 notifyAll方法,與上面不同的便是,notify隻會喚醒等待的單個線程,但如果有多個線程都是該任務的等待線程,這時便可以使用notifyAll喚醒是以的等待線程。

二、生産消費模式

1.前言

1.1 為了更好的了解wait和notify方法,我們引入了生産消費模式來加強我們的了解,這兩個方法也通常用于這種模式中,實作的場景。比如一個公司有内網。現在我們的伺服器是在外網中。在外網的伺服器如果有一個使用者登入了,需要通知到内網進行知曉。這時我們便可以建一個中間伺服器來通過生産消費模式來進行監聽,當然java中的EJB,JMS這些消息傳遞也難實作,但這裡主要是講生産消費模式。外網中有一個使用者登入,代表生産了一個資訊,中間伺服器進行接受,然後把這條消息傳遞到内網,實作消費過程。這個列子可能比較抽象,其實生産消費模式,也就是你負責炸爆米花,我負責吃

2.uml圖

2.1 這裡我還是把任務和線程分開來寫,就在Thread裡面寫任務内容更友善,我分開的目的還是要明确一點,線程是線程,任務是任務,線程隻是執行任務的工具。
java多線程(三)生産消費模型(wait和notify)一、Object自帶的方法二、生産消費模式

3.代碼

3.1 Productor為生産類。用來生産産品,queue為我們傳入的倉庫對象,倉庫是有限的,當倉庫的東西滿了之後,便停止生産。調用了wait方法,電源就好比是線程,生産機械就是這個類,用來生産東西,當不需要生産時,拔掉電源就好了。
package com.test;

import java.util.Queue;
import java.util.Random;

public class Productor{

	private Queue<String> queue;
	
	private int maxthing;
	public  Productor(Queue<String> queue,int maxthing){
		this.queue=queue;
		this.maxthing=maxthing;
	}
	
	public  void pro(){
		while (true) {
			synchronized(queue){
				while (queue.size()== maxthing) {
					System.out.println("倉庫已滿"+
							Thread.currentThread().getName()+"進入wait狀态");
					try {
						queue.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				//倉庫沒滿,可繼續生産
				System.out.println(Thread.currentThread().getName()+"生産一個,倉庫總量:"+queue.size());
				queue.offer("爆米花");
				queue.notifyAll();
				try {
	                Thread.sleep(new Random().nextInt(1000));
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
			}
		}
	}

}

           
3.2 Customer為我們的消費類,當倉庫沒有物品時候,我們就無法消費,便隻有等待倉庫有物品時來喚醒我們目前的等待線程來消費。
package com.test;

import java.util.Queue;
import java.util.Random;

public class Customer {
	
	private Queue<String> queue;
	
	public  Customer(Queue<String> queue,int maxthing){
		this.queue=queue;
	}
	
	public  void cus(){
		while(true){
			synchronized(queue){
				while (queue.size()== 0) {
					System.out.println("倉庫已空"+
							Thread.currentThread().getName()+"進入wait狀态");
					try {
						queue.wait();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				//倉庫沒空,可消費
				System.out.println(Thread.currentThread().getName()+"消費一個,倉庫總量:"+queue.size());
				queue.poll();
				queue.notifyAll();
				try {
	                Thread.sleep(new Random().nextInt(1000));
	            } catch (InterruptedException e) {
	                e.printStackTrace();
	            }
			}
		}
	}
}

           
3.3 ThreadProduct生産線程,用來執行我們的生産任務。
package com.test;

public class ThreadProduct extends Thread{

	private Productor productor;
	
	public ThreadProduct(String name,Productor productor) {
		super(name);
		this.productor=productor;
	}
	
	@Override
	public void run() {
		productor.pro();
	}
}

           
3.4 ThreadCustom消費線程,用來執行我們的消費任務。
package com.test;

public class ThreadCustom extends Thread{
	
	private Customer customer;
	
	public ThreadCustom(String name,Customer customer) {
		super(name);
		this.customer=customer;
	}
	
	@Override
	public void run() {
		customer.cus();
	}
}

           
3.5 Test4測試類
package com.test;

import java.util.LinkedList;
import java.util.Queue;

public class Test4 {
	
	private static int MAXTHING=10;
	
	private static Queue<String> queue=new LinkedList<String>();
	
	public static void main(String[] args) {
		Productor productor=new Productor(queue, MAXTHING);
		Customer customer=new Customer(queue, MAXTHING);
		Thread pr1=new ThreadProduct("pr1",productor);
		Thread pr2=new ThreadProduct("pr2",productor);
		Thread cu1=new ThreadCustom("cu1",customer);
		Thread cu2=new ThreadCustom("cu2",customer);
		pr1.start();
		pr2.start();
		cu1.start();
		cu2.start();
	}
}

           

4.結果

4.1 運作結果:

pr1生産一個,倉庫總量:0

pr1生産一個,倉庫總量:1

pr1生産一個,倉庫總量:2

pr1生産一個,倉庫總量:3

pr1生産一個,倉庫總量:4

pr1生産一個,倉庫總量:5

pr1生産一個,倉庫總量:6

pr1生産一個,倉庫總量:7

pr1生産一個,倉庫總量:8

pr1生産一個,倉庫總量:9

倉庫已滿pr1進入wait狀态

cu2消費一個,倉庫總量:10

cu2消費一個,倉庫總量:9

cu2消費一個,倉庫總量:8

cu2消費一個,倉庫總量:7

cu2消費一個,倉庫總量:6

cu2消費一個,倉庫總量:5

cu2消費一個,倉庫總量:4

cu2消費一個,倉庫總量:3

4.2 結果分析,不出意外的話,我們的程式會一直運作下去,不停的生産,不停的消費。

5. 總結

5.1 wait和notify都是屬于object的方法,是以我們任何類都可以去調用這兩個方法。
5.2 額外說一下,sleep方法也可以是線程休眠,但是sleep方法是線程獨有了,就好比一種是主動,一種是被動,就好比,你在公司上班,老闆是線程,你是任務,老闆叫你去哪,你就去哪,調用wait方法,是你自己辭職,不想讓老闆指揮你了,而調用sleep方法,是老闆炒你鱿魚,他不想指揮你了,雖然最種你都不會被别人指揮,結果相同,但過程不一樣。sleep不會釋放鎖,wait會釋放鎖。你主動辭職了,證明你有能力,想去更好的地方發展,其他老闆也都很願意要你,你可以去其他公司。但是老闆炒你鱿魚,代表你真的不行,其他公司也不願收留你。列子雖然舉得不恰當,但大概是這意思。其實并不複雜,你多看兩遍,多了解下,就很容易區分。當然線程還有其他很多方法,這裡就不細講了,主要在于進步,而不要想什麼一天就搞懂。以前我有個室友,期末的時候看了兩個小時高數書,他說他會了,結果挂了,人家一學期都學不懂的,你又怎麼可能短時間學會,後來我們就經常比喻,你以為你是兩個小時學完高數哦。扯多了,大概就是這意思。其實我也是個初入網際網路的菜鳥,邊學習,邊寫筆記。不要怕自己講錯了,不講你永遠不知道自己對不對,講出來了,你好歹知道自己哪裡不對,該怎麼改。希望各位大神指出錯誤之處或不足之處,一定改之。