多執行緒:執行緒之間的協作(join、wait、notify、notifyAll、await、signal、signalAll)
阿新 • • 發佈:2019-01-14
當多個執行緒可以一起工作去解決某個問題時,如果某些部分必須在其它部分之前完成,那麼就需要對執行緒進行協調。
join()
線上程中呼叫另一個執行緒的 join() 方法,會將當前執行緒掛起,而不是忙等待,直到目標執行緒結束。
對於以下程式碼,雖然 b 執行緒先啟動,但是因為在 b 執行緒中呼叫了 a 執行緒的 join() 方法,b 執行緒會等待 a 執行緒結束才繼續執行,因此最後能夠保證 a 執行緒的輸出先於 b 執行緒的輸出。
public class JoinExample { private class A extends Thread { @Override public void run() { System.out.println("A"); } } private class B extends Thread { private A a; B(A a) { this.a = a; } @Override public void run() { try { a.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("B"); } } public void test() { A a = new A(); B b = new B(a); b.start(); a.start(); } }
public static void main(String[] args) { JoinExample example = new JoinExample(); example.test(); }
A
B
wait() notify() notifyAll()
呼叫 wait() 使得執行緒等待某個條件滿足,執行緒在等待時會被掛起,當其他執行緒的執行使得這個條件滿足時,其它執行緒會呼叫 notify() 或者 notifyAll() 來喚醒掛起的執行緒。
它們都屬於 Object 的一部分,而不屬於 Thread。
只能用在同步方法或者同步控制塊中使用,否則會在執行時丟擲 IllegalMonitorStateException。
使用 wait() 掛起期間,執行緒會釋放鎖。這是因為,如果沒有釋放鎖,那麼其它執行緒就無法進入物件的同步方法或者同步控制塊中,那麼就無法執行 notify() 或者 notifyAll() 來喚醒掛起的執行緒,造成死鎖。
public class WaitNotifyExample { public synchronized void before() { System.out.println("before"); notifyAll(); } public synchronized void after() { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("after"); } }
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); WaitNotifyExample example = new WaitNotifyExample(); executorService.execute(() -> example.after()); executorService.execute(() -> example.before()); }
before after
wait() 和 sleep() 的區別
- wait() 是 Object 的方法,而 sleep() 是 Thread 的靜態方法;
- wait() 會釋放鎖,sleep() 不會。
await() signal() signalAll()
java.util.concurrent 類庫中提供了 Condition 類來實現執行緒之間的協調,可以在 Condition 上呼叫 await() 方法使執行緒等待,其它執行緒呼叫 signal() 或 signalAll() 方法喚醒等待的執行緒。
相比於 wait() 這種等待方式,await() 可以指定等待的條件,因此更加靈活。
使用 Lock 來獲取一個 Condition 物件。
public class AwaitSignalExample { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); public void before() { lock.lock(); try { System.out.println("before"); condition.signalAll(); } finally { lock.unlock(); } } public void after() { lock.lock(); try { condition.await(); System.out.println("after"); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } }
public static void main(String[] args) { ExecutorService executorService = Executors.newCachedThreadPool(); AwaitSignalExample example = new AwaitSignalExample(); executorService.execute(() -> example.after()); executorService.execute(() -> example.before()); }
before after