文章目錄
- 首先了解Thread的API是如何改變執行緒的狀態的
- 假死現象
- 解決
- wait條件改變例外
首先了解Thread的API是如何改變執行緒的狀態的

- 1)新創建一個新的執行緒物件后,再呼叫它的start()方法,系統會為此執行緒分配CPU資源,使其處于Runnable(可運行)狀態,這是一個準備運行的階段,如果執行緒搶占到CPU資源,此執行緒就處于Running(運行)狀態,
- 2)Runnable狀態和Running狀態可相互切換,因為有可能執行緒運行一段時間后,有其他高優先級的執行緒搶占了CPU資源,這時此執行緒就從Running狀態變成Runnable狀態,執行緒進入Runnable狀態大體分為如下5種情況:
-> 呼叫sleep()方法后經過的時間超過了指定的休眠時間,
->執行緒呼叫的阻塞IO已經回傳,阻塞方法執行完畢,
->執行緒成功地獲得了試圖同步的監視器,
->執行緒正在等待某個通知,其他執行緒發出了通知,
->處于掛起狀態的執行緒呼叫了resume恢復方法, - 3 )Blocked是阻塞的意思,例如遇到了一個IO操作,此時CPU處于空閑狀態,可能會轉而把CPU時間片分配給其他執行緒,這時也可以稱為“暫停”狀態,Blocked狀態結束后,進人Runnable狀態,等待系統重新分配資源,出現阻塞的情況大體分為如下5種:
->執行緒呼叫sleep方法,主動放棄占用的處理器資源,
->執行緒呼叫了阻塞式IO方法,在該方法回傳前,該執行緒被阻塞,
->執行緒試圖獲得一個同步監視器,但該同步監視器正被其他執行緒所持有,
->執行緒等待某個通知,
->程式呼叫了suspend方法將該執行緒掛起,此方法容易導致死鎖,盡量避免使用該方法, - 4 ) run()方法運行結束后進入銷毀階段,整個執行緒執行完畢,
每個鎖物件都有兩個佇列,一個是就緒佇列,一個是阻塞佇列,就緒佇列存盤了將要獲得鎖的執行緒,阻塞佇列存盤了被阻塞的執行緒,一個執行緒被喚醒后,才會進入就緒佇列,等待CPU的調度;反之,一個執行緒被wait后,就會進入阻塞佇列,等待下一次被喚醒,
假死現象
- “假死”的現象其實就是執行緒進入 WAITING等待狀態,如果全部執行緒都進人WAITING狀態,則程式就不再執行任何業務功能了,整個專案呈停止狀態,這在使用生產者與消費者模式時經常遇到,
- 服務類
public class ServiceResources {
private List<String> list = new ArrayList<>();
synchronized public void push() throws InterruptedException {
while (list.size() > 0) {
//說明有產品
System.out.println(Thread.currentThread().getName() + " 生產者進入睡眠");
this.wait();
}
System.out.println(Thread.currentThread().getName() + " 生產");
list.add("anything");
this.notify();
}
synchronized public void take() throws InterruptedException {
while (list.size() == 0) {
//說明此時沒有產品
System.out.println(Thread.currentThread().getName() + " 消費者進入睡眠");
this.wait();
}
System.out.println(Thread.currentThread().getName() + " 消費");
list.remove(0);
this.notify();
}
}
- 多執行緒
public class ThreadP extends Thread {
private ServiceResources resources;
public ThreadP(ServiceResources resources, String name) {
this.resources = resources;
this.setName(name);
}
@Override
public void run() {
while (true) {
try {
resources.push();
} catch (InterruptedException e) {
break;
}
}
}
}
public class ThreadC extends Thread {
private ServiceResources resources;
public ThreadC(ServiceResources resources, String name) {
this.resources = resources;
this.setName(name);
}
@Override
public void run() {
while (true) {
try {
resources.take();
} catch (InterruptedException e) {
break;
}
}
}
}
- 運行類
public class Run {
public static void main(String[] args) throws InterruptedException {
ServiceResources resources = new ServiceResources();
//創建多個生產者和多個消費者
ThreadP threadP1 = new ThreadP(resources, "P1");
ThreadP threadP2 = new ThreadP(resources, "P2");
ThreadC threadC1 = new ThreadC(resources, "C1");
ThreadC threadC2 = new ThreadC(resources, "C2");
threadP1.start();
threadP2.start();
threadC1.start();
threadC2.start();
Thread.sleep(5000);
threadP1.interrupt();
threadP2.interrupt();
threadC1.interrupt();
threadC2.interrupt();
//獲得所有執行緒的狀態
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName() + " " + threadArray[i].getState());
}
}
}
- 運行結果

- 從列印的資訊來看,呈假死狀態的行程中所有的執行緒都呈WAITING狀態,為什么會出現這樣的情況呢?在代碼中已經用了wait/notify啊?
- 在代碼中確實已經通過wait/notify進行通信了,但不保證notify喚醒的是異類,也許是同類,比如“生產者”喚醒“生產者”,或“消費者”喚醒“消費者”這樣的情況,如果按這樣情況運行的比率積少成多,就會導致所有的執行緒都不能繼續運行下去,大家都在等待,都呈WAITING狀態,程式最后也就呈“假死”狀態,不能繼續運行下去了,
解決
- 解決“假死”的情況很簡單,將P.java和C.java檔案中的notifyO改成notifyAll0)方法即可,它的原理就是不光通知同類執行緒,也包括異類,這樣就不至于出現假死的狀態了,程式會一直運行下去,
wait條件改變例外
- 這個問題多見與少生產多消費中, 簡單來說就是生產趕不上消費, 導致在消費的時候發生例外
- 服務類
public class ServiceResources {
private List<String> list = new ArrayList<>();
synchronized public void push() throws InterruptedException {
if (list.size() > 0) {
//說明有產品
System.out.println(Thread.currentThread().getName() + " 生產者進入睡眠");
this.wait();
}
System.out.println(Thread.currentThread().getName() + " 生產");
list.add("anything");
this.notifyAll();
}
synchronized public void take() throws InterruptedException {
if (list.size() == 0) {
//說明此時沒有產品
System.out.println(Thread.currentThread().getName() + " 消費者進入睡眠");
this.wait();
}
System.out.println(Thread.currentThread().getName() + " 消費");
list.remove(0);
this.notifyAll();
}
}
- 運行類
public class Run {
public static void main(String[] args) throws InterruptedException {
ServiceResources resources = new ServiceResources();
//創建一個生產者和多個消費者
ThreadP threadP1 = new ThreadP(resources, "P1");
ThreadC threadC1 = new ThreadC(resources, "C1");
ThreadC threadC2 = new ThreadC(resources, "C2");
ThreadC threadC3 = new ThreadC(resources, "C3");
ThreadC threadC4 = new ThreadC(resources, "C4");
ThreadC threadC5 = new ThreadC(resources, "C5");
threadP1.start();
threadC1.start();
threadC2.start();
threadC3.start();
threadC4.start();
threadC5.start();
Thread.sleep(5000);
//獲得所有執行緒的狀態
Thread[] threadArray = new Thread[Thread.currentThread().getThreadGroup().activeCount()];
Thread.currentThread().getThreadGroup().enumerate(threadArray);
for (int i = 0; i < threadArray.length; i++) {
System.out.println(threadArray[i].getName() + " " + threadArray[i].getState());
}
}
}
比問題的出現就是因為在服務類中使用了if陳述句作為條件判斷,代碼如下:
if (list.size() == 0) {
//說明此時沒有產品
System.out.println(Thread.currentThread().getName() + " 消費者進入睡眠");
this.wait();
}
因為條件發生改變時并沒有得到及時的回應,所以多個呈wait狀態的執行緒被喚醒,繼而執行list.remove(0)代碼而出現例外,解決這個辦法是,將if改成while陳述句即可,
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/160244.html
標籤:其他
上一篇:Eclipse 教程
