生産消費模型
- 一、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裡面寫任務内容更友善,我分開的目的還是要明确一點,線程是線程,任務是任務,線程隻是執行任務的工具。
![](https://img.laitimes.com/img/__Qf2AjLwojIjJCLyojI0JCLiAzNfRHLGZkRGZkRfJ3bs92YsYTMfVmepNHLyEleNhXVU1UMNpHW4Z0MMBjVtJWd0ckW65UbM5WOHJWa5kHT20ESjBjUIF2X0hXZ0xCMx81dvRWYoNHLrdEZwZ1Rh5WNXp1bwNjW1ZUba9VZwlHdssmch1mclRXY39CXldWYtlWPzNXZj9mcw1ycz9WL49zZuBnLyYzN5UDOzEjMwITNwkTMwIzLc52YucWbp5GZzNmLn9Gbi1yZtl2Lc9CX6MHc0RHaiojIsJye.png)
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會釋放鎖。你主動辭職了,證明你有能力,想去更好的地方發展,其他老闆也都很願意要你,你可以去其他公司。但是老闆炒你鱿魚,代表你真的不行,其他公司也不願收留你。列子雖然舉得不恰當,但大概是這意思。其實并不複雜,你多看兩遍,多了解下,就很容易區分。當然線程還有其他很多方法,這裡就不細講了,主要在于進步,而不要想什麼一天就搞懂。以前我有個室友,期末的時候看了兩個小時高數書,他說他會了,結果挂了,人家一學期都學不懂的,你又怎麼可能短時間學會,後來我們就經常比喻,你以為你是兩個小時學完高數哦。扯多了,大概就是這意思。其實我也是個初入網際網路的菜鳥,邊學習,邊寫筆記。不要怕自己講錯了,不講你永遠不知道自己對不對,講出來了,你好歹知道自己哪裡不對,該怎麼改。希望各位大神指出錯誤之處或不足之處,一定改之。