執行緒之間的協作
當多個執行緒可以一起去解決某個問題時,如果某些部分必須在其他部分之前完成,那麼就需要對執行緒進行協調。
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。
只能用在同步方法或者同步控制塊中使用,否則會在執行時丟擲IllegalMonitorStateExeception
。
使用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