天天看點

同步機制中notify()與wait()--實作A,B,C循環列印

關于同步與異步機制,聽過很多通俗的解釋。

日常生活中其實也有很多這樣的例子,比如吃飯的時候

同步:就是你通知我去吃飯,我聽到了就和你一起去,如果我沒有回應,你就會一直喊,直到我有反映了為止。

異步:你通知我一聲後,不管我有沒有回應,你自己就去吃飯去了。

在通訊當中:

同步:發送方發出資料後,等接收方發回響應以後才發下一個資料包的通訊方式。 

異步:發送方發出資料後,不等接收方發回響應,接着發送下個資料包的通訊方式。

java在實作同步機制的過程中,主要用到了兩種方法,一種是同步方法,一種是同步代碼塊。兩種都用到了synchronized關鍵字。

同步方法:

public void sychronized study() {
        System.out.println("studying");
}
           

同步代碼塊:

(sychronized){
     ......
}
           

說到這裡,下面說一下在同步代碼塊中使用notify(),以及wait()來實作一個經典的線程列印問題--利用三個線程A,B,C分别各自循環列印A,B,C.

關于notify()以及wait(),這裡叙述一下。

同步機制中notify()與wait()--實作A,B,C循環列印

我們可以看到,wait()與notify()同屬于object類的方法,由于object被所有類所繼承,是以這兩個方法也會被所有類所繼承。而且,我們看源碼,關于兩個方法,

public final native void notify();
           
public final void wait() throws InterruptedException {
        wait(0);
    }
           

兩個方法都有final修飾,則在子類中不能夠被重寫。

wait 方法:

使目前線程一直處于等待的狀态,直到另一個線程在調用了notify()或者notifyAll()方法。

目前線程擁有目前對象的鎖,即wait()方法應該在sychronized{}方法或者代碼塊中。調用wait()方法後,釋放對目前對象鎖的擁有。

notify()方法:

喚醒處于等待狀态的線程,這裡應該注意是,如果有多個線程處于等待的狀态,那麼調用一次notify()方法後,具體釋放的是哪一個線程是不确定的,執行的過程是java虛拟機來實作的。

跟wait方法一樣,notify也是必須放在synchronized方法或synchronized塊中。

關于wait()與notify()方法,利用三個線程循環列印A,B,C是很具有代表性的。

public class MyThread {
	public static void main(String[] args) throws Exception {
		Object A = new Object();
		Object B = new Object();
		Object C = new Object();

		Thread t1 = new Thread(new Print(A, B), "A");
		Thread t2 = new Thread(new Print(B, C), "B");
		Thread t3 = new Thread(new Print(C, A), "C");
                
                //Thread.sleep(1)為了讓列印的時候依次執行
		t1.start();
		Thread.sleep(1);
		t2.start();
		Thread.sleep(1);
		t3.start();

	}
}

class Print implements Runnable {

	private Object self;
	private Object next;

	public Print(Object self, Object next) throws InterruptedException {
		this.self = self;
		this.next = next;

	}

	public void run() {
		for (int i = 0; i < 10; i++) {
			synchronized (self) {
				synchronized (next) {
					System.out.println(Thread.currentThread().getName());
					next.notify();
				}
				try {
					if (i == 9) {
						return; // 當i == 9 即最後一次循環, 将直接退出 不再進行等待
					}
					self.wait();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		}

	}
}
           

列印的結果為ABC ABC ABC ABC ABC ABC ABC ABC ABC ABC(當然,這裡未顯示換行符)

現在簡單的看一下程式執行的過程。

首先線程A(這裡為線程的名字)調用start方法,在run方法中,self為A,next為B,列印一下Thread.CurrentThread.getName()為A.然後執行self.wait(),即此時A處于阻塞狀态。然後線程B(線程的名字)進入run方法,self為B,next為C,然後先列印一下B,再執行next.notify()方法,此時把之前處于阻塞狀态的線程A不再阻塞。當執行下面的self.wait的時候,此時線程B就處于阻塞狀态了,而線程A,C處于就緒狀态,等待處理機的排程。然後線程C進入run方法,self為C,next為A,列印一下C,next.notify使之前處于阻塞的B處于就緒狀态,然後 C再進入等待的狀态,依次下去。

繼續閱讀