1 線程間交互
線程間的交互,也就是相互通知,進而達到相互控制,java中線程間的交互要用到java.lang.Object的類的三個方法分別是wait,notify(),notifyAll,三個方法的調用必須在同步環境內調用,也就是線程獲取了對象的鎖後才能調用。
如果線程A持有線程B的對象的鎖,多線程環境下只有當線程A獲取了線程B的鎖後(同步環境下)時,線程A才能調用B的wait,notify(),notifyAll方法。
wait()還有另外兩個重載方法:
void wait(long timeout):等待直到其他線程調用此對象的 notify() 或 notifyAll() ,或者超過timeout時間量。
void wait(long timeout, int nanos):等待直到其他線程調用此對象的 notify() 或 notifyAll() ,或者其他某個線程中斷當前線程,或者已超過某個實際時間量。
wait函數會釋放鎖。
/**
wait函數會立即釋放對象鎖,導致當前的線程等待,直到其他線程調用此對象的 notify() 方法或 notifyAll() 方法,纔會被重新激活。
* Causes the current thread to wait until another thread invokes the
當前線程必須擁有交互對象的monitor(什麼是monitor呢,會在後面講,
這裏大家把monitor理解爲一個對象,每個object只和一個monitor相關聯,一個線程獲取monitor之後,除非他主動釋放
其他線程無法獲取monitor)。
如果當前線程沒有獲取monitor,它將一直等待,直到其他線程調用該對象的notify,notifyAll喚醒當前線程。
既然調用notify和notifyall能夠喚醒線程,所以必定在該對象上存在一個線程鏈,
當監聽到調用了notify時,可以通知該對象上的線程鏈中的線程。
* The current thread must own this object's monitor. The thread
* releases ownership of this monitor and waits until another thread
* notifies threads waiting on this object's monitor to wake up
* either through a call to the {@code notify} method or the
* {@code notifyAll} method. The thread then waits until it can
* re-obtain ownership of the monitor and resumes execution.
* 由於必須要獲取monitor,所以wait調用必須在同步代碼塊內,且當前線程獲取了對應object的monitor,如下所示。
* * This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
* As in the one argument version, interrupts and spurious wakeups are
* possible, and this method should always be used in a loop:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait();
* ... // Perform action appropriate to condition
* }
* </pre>
public final void wait() throws InterruptedException {
wait(0);
}
void notify():喚醒在此對象監視器上等待的單個線程。
void notifyAll():喚醒在此對象監視器上等待的所有線程。
notify()和notifyAll()方法只是喚醒等待該對象的monitor的線程,並不決定哪個線程能夠獲取到monitor。
/**
調用某個對象的notify()方法能夠喚醒一個正在等待這個對象的monitor的線程,如果有多個線程都在等待這個對象的monitor,則只能喚醒其中一個線程;
notify和notifyAll的調用也必須在獲取對象monitor的狀態下,函數會喚醒該對象線程鏈中的一個線程。
* Wakes up a single thread that is waiting on this object's
* monitor. If any threads are waiting on this object, one of them
* is chosen to be awakened. The choice is arbitrary and occurs at
* the discretion of the implementation. A thread waits on an object's
* monitor by calling one of the {@code wait} methods.
* <p>
* The awakened thread will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened thread will
* compete in the usual manner with any other threads that might be
* actively competing to synchronize on this object; for example, the
* awakened thread enjoys no reliable privilege or disadvantage in being
* the next thread to lock this object.
* <p>
* This method should only be called by a thread that is the owner
* of this object's monitor. A thread becomes the owner of the
* object's monitor in one of three ways:
* <ul>
* <li>By executing a synchronized instance method of that object.
* <li>By executing the body of a {@code synchronized} statement
* that synchronizes on the object.
* <li>For objects of type {@code Class,} by executing a
* synchronized static method of that class.
* </ul>
* <p>
*/
public final native void notify();
/**
多個線程可以等待一個對象鎖,當需要喚醒所有線程時,可以調用notifyAll。
調用notifyAll()方法能夠喚醒所有正在等待這個對象的monitor的線程;
notifyAll函數會喚醒所有在等待object monitor的線程,最終能夠獲取monitor的只有一個線程。
* Wakes up all threads that are waiting on this object's monitor. A
* thread waits on an object's monitor by calling one of the
* {@code wait} methods.
* <p>
* The awakened threads will not be able to proceed until the current
* thread relinquishes the lock on this object. The awakened threads
* will compete in the usual manner with any other threads that might
* be actively competing to synchronize on this object; for example,
* the awakened threads enjoy no reliable privilege or disadvantage in
* being the next thread to lock this object.
* <p>
* This method should only be called by a thread that is the owner
* of this object's monitor. See the {@code notify} method for a
* description of the ways in which a thread can become the owner of
* a monitor.
*
* @throws IllegalMonitorStateException if the current thread is not
* the owner of this object's monitor.
* @see java.lang.Object#notify()
* @see java.lang.Object#wait()
*/
public final native void notifyAll();
注意:
在對象上調用wait()方法時,執行該代碼的線程立即放棄它在對象上的鎖。然而調用notify()時,並不會立即釋放其鎖。會在完成同步代碼塊後,釋放鎖,所以調用notify並不會導致立即釋放鎖。
//下面的代碼每次都會先輸出B的值
public class Demo3 {
static Object object = new Object();
public static void main(String[] args) {
new Thread(new ThreadA()).start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new ThreadB()).start();
}
static class ThreadA implements Runnable{
@Override
public void run() {
synchronized (object) {
try {
object.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("ThreadA");
}
}
}
static class ThreadB implements Runnable{
@Override
public void run() {
synchronized (object) {
try {
object.notify();
} catch (Exception e) {
e.printStackTrace();
}
System.out.println("ThreadB");
}
}
}
}
如果調用某個對象的wait()方法,當前線程必須擁有這個對象的monitor(即鎖),因此調用wait()方法必須在同步塊或者同步方法中進行(synchronized塊或者synchronized方法)
調用某個對象的wait()方法能讓當前線程阻塞,並且當前線程必須擁有此對象的monitor(即鎖)
調用某個對象的notify()方法能夠喚醒一個正在等待這個對象的monitor的線程,如果有多個線程都在等待這個對象的monitor,則只能喚醒其中一個線程;
調用notifyAll()方法能夠喚醒所有正在等待這個對象的monitor的線程;
2 生產者消費者
生產者消費者模型最常用的線程交互模型,當倉庫物品爲空時無法消費,當倉庫滿時無法生產。
package com.sync.demo;
import java.util.ArrayList;
import java.util.Random;
public class Demo4 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
Thread con1 = new Thread(new Consumer(list));
Thread con2 = new Thread(new Consumer(list));
Thread con3 = new Thread(new Consumer(list));
Thread pro = new Thread(new Productor(list));
con1.start();
con2.start();
con3.start();
pro.start();
}
static class Consumer implements Runnable{
private ArrayList<String> list;
public Consumer(ArrayList<String> list) {
this.list = list;
}
private void consumer() {
synchronized (list) {
while (list.size() < 1) {
System.out.println("=======倉庫已空,無法消費,請等待======"+list.size());
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(70);
list.remove(list.size() -1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=======消費一個======剩餘======"+Thread.currentThread().getName()+" "+list.size());
list.notify();
}
}
@Override
public void run() {
while (true) {
consumer();
}
}
}
static class Productor implements Runnable{
private ArrayList<String> list;
public Productor(ArrayList<String> list) {
this.list = list;
}
private void productor() {
synchronized (list) {
while (list.size() == 77) {
System.out.println("=======倉庫已滿,停止生產======"+Thread.currentThread().getName()+" "+list.size());
try {
list.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
Thread.sleep(50);
list.add((new Random().nextInt(100))+"pro");
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("=======生產一個======剩餘======"+list.size());
list.notify();
}
}
@Override
public void run() {
while (true) {
productor();
}
}
}
}
結果:
=======生產一個======剩餘======59
=======生產一個======剩餘======60
=======生產一個======剩餘======61
=======生產一個======剩餘======62
=======生產一個======剩餘======63
=======生產一個======剩餘======64
=======生產一個======剩餘======65
=======生產一個======剩餘======66
=======生產一個======剩餘======67
=======生產一個======剩餘======68
=======生產一個======剩餘======69
=======生產一個======剩餘======70
=======生產一個======剩餘======71
=======生產一個======剩餘======72
=======生產一個======剩餘======73
=======生產一個======剩餘======74
=======生產一個======剩餘======75
=======生產一個======剩餘======76
=======生產一個======剩餘======77
=======倉庫已滿,停止生產======Thread-3 77
=======消費一個======剩餘======Thread-0 76
=======消費一個======剩餘======Thread-0 75
=======消費一個======剩餘======Thread-0 74
=======消費一個======剩餘======Thread-0 73
=======消費一個======剩餘======Thread-0 72
=======消費一個======剩餘======Thread-0 71
=======消費一個======剩餘======Thread-0 70
=======消費一個======剩餘======Thread-0 69
=======消費一個======剩餘======Thread-0 68
=======消費一個======剩餘======Thread-0 67
=======消費一個======剩餘======Thread-0 66
=======消費一個======剩餘======Thread-0 65
=======消費一個======剩餘======Thread-0 64
=======消費一個======剩餘======Thread-0 63
=======消費一個======剩餘======Thread-0 62
=======消費一個======剩餘======Thread-0 61
=======消費一個======剩餘======Thread-0 60
=======消費一個======剩餘======Thread-0 59
=======消費一個======剩餘======Thread-0 58
=======消費一個======剩餘======Thread-0 57
=======消費一個======剩餘======Thread-0 56
=======消費一個======剩餘======Thread-0 55
=======消費一個======剩餘======Thread-0 54
=======消費一個======剩餘======Thread-0 53
=======消費一個======剩餘======Thread-0 52
=======消費一個======剩餘======Thread-0 51
=======消費一個======剩餘======Thread-0 50
=======消費一個======剩餘======Thread-0 49
=======消費一個======剩餘======Thread-0 48
=======消費一個======剩餘======Thread-1 47
=======消費一個======剩餘======Thread-1 46
=======消費一個======剩餘======Thread-1 45
=======消費一個======剩餘======Thread-1 44
=======消費一個======剩餘======Thread-1 43
=======消費一個======剩餘======Thread-1 42
=======消費一個======剩餘======Thread-1 41