線程通信
線程間通信的模型有兩種:共享內存和消息傳遞;
(1)共享內存
使用 volatile 關鍵字來實現線程間相互通信是使用共享內存的思想,大致意思就是多個線程同時監聽一個變量,當這個變量發生變化的時候 ,線程能夠感知並執行相應的業務。 volatile 關鍵字對於保證操作的原子性具有非常大的幫助,但是需要注意的是,volatile 關鍵字並不能替代鎖,它也無法保證一些複合操作的原子性。
(2)消息傳遞
Object類提供了線程間通信的方法:wait()、notify()、notifyaAll(),它們是多線程通信的基礎。
wait():等待,一旦一個線程執行到wait(),就釋放當前的鎖。
notify()、notifyaAll():隨機喚醒wait()的一個或所有的線程。
注意: wait和 notify必須配合synchronized使用。必須包含在對應的synchronized語句中。
案例
下面通過以下案例,使用上述兩種方式實現線程之間的通信。
案例:使用兩個線程交替打印1-100。
(方式一)使用 volatile 關鍵字
package com.test.communication;
/**
* 通過關鍵字volatile,實現使用兩個線程交替打印1-100
*
* @author Anna.
*/
public class ThreadCommunicationTest01 {
volatile static int num = 1;
volatile static boolean flag = true;
public static void main(String[] args) {
Thread th = new Thread(new Runnable(){
@Override
public void run() {
while (true) {
if (flag) {
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
flag = !flag;
}
}
}
}
});
Thread th2 = new Thread(new Runnable(){
@Override
public void run() {
while (true) {
if (!flag) {
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
flag = !flag;
}
}
}
}
});
th.setName("甲");
th2.setName("乙");
th.start();
th2.start();
}
}
打印結果:
如果去掉關鍵字,那麼線程甲執行完成後,線程乙無法得知,flag的值發生改變,那麼打印結果如下:
(方式二)使用 wait()、notify()、notifyaAll()消息傳遞的方式
package com.test.communication;
/**
* 使用 wait()、notify()、notifyaAll()消息傳遞的方式,實現使用兩個線程交替打印1-100
*
* @author Anna.
*/
public class ThreadCommunicationTest02 {
public static void main(String[] args) {
PrintNum pm = new PrintNum(1);
Thread th = new Thread(pm);
Thread th2 = new Thread(pm);
th.setName("甲");
th2.setName("乙");
th.start();
th2.start();
}
}
class PrintNum implements Runnable{
int num;
public PrintNum(int num){
this.num = num;
}
@Override
public synchronized void run() {
while (true) {
notify();
if (num <= 100) {
System.out.println(Thread.currentThread().getName() + ":" + num);
num++;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
打印結果:
如果去掉synchronized關鍵字,將拋出IllegalMonitorStateException異常,如下: