線程間交互(wait notify)

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 (&lt;condition does not hold&gt;)
     *             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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章