此文是對《JAVA多線程編程核心技術》的一點總結,如果想要了解具體細節可以去看原書。
第三章 線程間通信
使用wait/notify實現線程間的通信
- 方法wait()的作用是使當前執行代碼的線程進行等待,wait()方法是Object類的方法,該方法用來將當前線程置入“預執行隊列”中,並且在wait()所在的代碼行處停止執行,直到接到通知或中斷爲止。在調用wait()之前,線程必須獲得該對象的對象級別鎖,即只能在同步方法或同步塊中調用wait()方法。執行wait()方法後,當前線程釋放鎖。
- 如果調用wait()時沒有持有適當的鎖,則拋出IllegalMonitorStateException,它是RuntimeException的一個子類,因此,不需要try-catch語句捕獲異常。
- 方法notify()也要在同步方法或同步代碼塊中調用,即在調用前,線程也必須獲得該對象的對象級別鎖。
- notify()用來通知那些可能等待該對象的對象鎖的其他線程,如果有多個線程等待,則由線程規劃器隨機挑選出一個呈wait狀態的線程,對其發出通知notify,並使它等待獲取該對象的對象鎖
- 在執行notify()方法後,不會馬上釋放該對象鎖,要等到執行notify()方法的線程將程序執行完,當前線程纔會釋放鎖。
線程狀態
- 生產者/消費者模式的實現
創建存儲值的對象
public class MyStack {
private List list = new ArrayList<>();
synchronized public void push() {
try{
while (list.size() == 1) {
this.wait();
}
list.add("anyString=" + Math.random());
this.notify();
System.out.println("push=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized public String pop() {
String returnValue = "";
try {
while (list.size() == 0) {
System.out.println("pop操作中的:" + Thread.currentThread().getName() + " 線程呈wait狀態");
this.wait();
}
returnValue = "" + list.get(0);
list.remove(0);
this.notify();
System.out.println("pop=" + list.size());
} catch (InterruptedException e) {
e.printStackTrace();
}
return returnValue;
}
}
生產者
/**
* 生產者
*/
public class P {
private MyStack myStack;
public P(MyStack myStack) {
this.myStack = myStack;
}
public void pushService() {
myStack.push();
}
}
生產者線程
public class P_Thread extends Thread {
private P p;
public P_Thread(P p) {
this.p = p;
}
@Override
public void run() {
while (true) {
p.pushService();
}
}
}
消費者
/**
* 消費者
*/
public class C {
private MyStack myStack;
public C(MyStack myStack) {
this.myStack = myStack;
}
public void popService() {
System.out.println("pop=" + myStack.pop());
}
}
消費者線程
public class C_Thread extends Thread {
private C r;
public C_Thread(C r) {
this.r = r;
}
@Override
public void run() {
while (true) {
r.popService();
}
}
}
啓動類
public class Run {
public static void main(String[] args) {
MyStack myStack = new MyStack();
P p1 = new P(myStack);
P p2 = new P(myStack);
P p3 = new P(myStack);
P_Thread pThread1 = new P_Thread(p1);
P_Thread pThread2 = new P_Thread(p2);
P_Thread pThread3 = new P_Thread(p3);
pThread1.start();
pThread2.start();
pThread3.start();
C r1 = new C(myStack);
C r2 = new C(myStack);
C r3 = new C(myStack);
C_Thread cThread1 = new C_Thread(r1);
C_Thread cThread2 = new C_Thread(r2);
C_Thread cThread3 = new C_Thread(r3);
cThread1.start();
cThread2.start();
cThread3.start();
}
}
- 方法join的使用
方法join的作用是使所屬的線程對象x正常執行run()方法中的任務,而使當前線程z進行無限期的阻塞,等待線程銷燬後再執行線程z後面的代碼。 - ThreadLocal類的使用
ThreadLocal類主要解決的是每個線程綁定自己的值,可以將ThreadLocal類比喻成全局存放數據的盒子,盒子中可以存儲每個線程的私有數據。