java 線程死結

來源:互聯網
上載者:User

死結:多個線程同時被阻塞,它們中的一個或者全部都在等待某個資源被釋放。由於線程被無限期地阻塞,因此程式不能正常運行。簡單的說就是:線程死結時,第一個線程等待第二個線程釋放資源,而同時第二個線程又在等待第一個線程釋放資源。 導致死結的根源在於不適當地運用“synchronized”關鍵詞來管理線程對特定對象的訪問

線程間通訊:就是多個線程線上程在操作同一個資源,但是操作的動作不同

下面是兩個線程間的通訊的例子,如果不使用同步,則name和sex可能不同步:

class Res {      //共用的資源String name;String sex;}class Input implements Runnable {   //--------------使用1private Res r;Input(Res r) {this.r = r;}public void run() {int x = 0;while (true) {synchronized (Input.class) {  // 而下面必須也是Input.class 或者是Output.class,總之上下兩個線程鎖必須一致才可以同步if (x == 0) {r.name = "mike";r.sex = "man";} else {r.name = "麗麗";r.sex = "女女女女";}x = (x + 1) % 2;}}}}class Output implements Runnable {private Res r;Output(Res r) {this.r = r;}public void run() {while (true)synchronized (Input.class) {System.out.println(r.name + "----" + r.sex);}}}public class Test {public static void main(String[] args) {Res r = new Res();Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in), t2 = new Thread(out);t1.start();t2.start();}}

結果:麗麗----女女女女
麗麗----女女女女
麗麗----女女女女
mike----man
mike----man

wait(),notify(),notifyAll()

這三個方法用於協調多個線程對共用資料的存取,所以必須在Synchronized語句塊內使用這三個方法。Synchronized用於保護共用資料,但是這樣程式的流程就很不靈活了,此時用這三個方法來靈活控制。 
線程可以調用對象的wait()方法使當前線程暫停執行並釋放對象鎖標誌,進入等待狀態,並且可以調用notify()或者notifyAll()方法通知正在等待的其他線程,然後等待同一個線程鎖的notify,繼續執行wait後未執行完的內容。notify()通知等待隊列中的第一個線程,notifyAll()通知的是等待隊列中的所有線程。 
注意 這三個方法都是java.lang.Ojbect的方法! 為什麼這些操作線程的方法要定義在Object中?因為這些方法在操作同步中線程時,都必須要標識它們所操作線程只有的鎖,只有同一個鎖上的被等待線程,可以被同一個鎖上的notify喚醒。不可以對不同鎖中線程喚醒。也就是說,等待和喚醒必須是同一個鎖,而鎖可以是任意對象,所以可以被任意對象調用的方法定義Object中
 如果A1,A2,A3都在obj.wait(),則B調用obj.notify()只能喚醒A1,A2,A3中的一個。
把上面例子的輸出改為交替輸出:

class Res { // 共用的String name;String sex;boolean flag = false;}class Input implements Runnable {private Res r;Input(Res r) {this.r = r;}public void run() {int x = 0;while (true) {synchronized (r) {if (r.flag)try { // --------介面exception不能拋r.wait();} catch (InterruptedException e) {e.printStackTrace();}if (x == 0) {r.name = "mike";r.sex = "man";} else {r.name = "麗麗";r.sex = "女女女女";}x = (x + 1) % 2;r.flag = true;r.notify();}}}}class Output implements Runnable {private Res r;Output(Res r) {this.r = r;}public void run() {while (true)synchronized (r) {if (!r.flag)try {r.wait();} catch (InterruptedException e) {e.printStackTrace();}System.out.println(r.name + "----" + r.sex);r.flag = false;r.notify();}}}public class Test {public static void main(String[] args) {Res r = new Res();Input in = new Input(r);Output out = new Output(r);Thread t1 = new Thread(in), t2 = new Thread(out);t1.start();t2.start();}}

下面是生產者、消費者問題(多個生產者、消費者),每產生一個產品就消費一個

class Resource {private String name;private int count = 1;private boolean flag = false;public synchronized void set(String name) {while (flag)try {wait();} catch (Exception e) {}this.name = name + "----" + count++;System.out.println(Thread.currentThread().getName() + "----生產者----"+ this.name);flag = true;this.notifyAll();}public synchronized void out() {while (!flag)try {wait();} catch (Exception e) {}System.out.println(Thread.currentThread().getName()+ "----消費者---------" + this.name);flag = false;this.notifyAll();// 由notify換成notifyAll}}class Producer implements Runnable {private Resource res;Producer(Resource res) {this.res = res;}public void run() {while (true)res.set("+商品+");}}class Consumer implements Runnable {private Resource res;Consumer(Resource res) {this.res = res;}public void run() {while (true)res.out();}}class Test {public static void main(String[] args) {Resource r = new Resource();Producer pro = new Producer(r);Consumer con = new Consumer(r);Thread t1 = new Thread(pro),t2 = new Thread(pro),t3 = new Thread(con),t4 = new Thread(con);t1.start();t2.start();t3.start();t4.start();}}

雖然 synchronized 方法和語句的範圍機制使得使用監視器鎖編程方便了很多,而且還協助避免了很多涉及到鎖的常見編程錯誤,但有時也需要以更為靈活的方式使用鎖。jdk1.5中提供了多線程升級解決方案,將同步synchronized替換成現實Lock操作 ,將Object中的wait,notify,notifyAll替換成Condition對象, Lock 實現提供了比使用 synchronized 方法和語句可獲得的更廣泛的鎖定操作。此實現允許更靈活的結構,可以具有差別很大的屬性,可以支援多個相關的
Condition 對象。

Condition 將 Object 監視器方法(wait、notify 和 notifyAll)分解成截然不同的對象,以便通過將這些對象與任意 Lock 實現組合使用,為每個對象提供多個等待 set(wait-set)。其中,Lock 替代了 synchronized 方法和語句的使用,Condition 替代了 Object 監視器方法的使用

以下用lock實現生產者消費者

import java.util.concurrent.locks.*;class Resource {private String name;private int count = 1;private boolean flag = false;private Lock lock = new ReentrantLock();// //private Condition condition_pro = lock.newCondition();// /private Condition condition_con = lock.newCondition();// //public void set(String name) throws InterruptedException {lock.lock();// //try {while (flag)condition_pro.await();// /this.name = name + "----" + count++;System.out.println(Thread.currentThread().getName() + "----生產者----"+ this.name);flag = true;condition_con.signal();// //} finally {lock.unlock();// /}}public void out() throws InterruptedException {lock.lock();// /try {while (!flag)condition_con.await();//System.out.println(Thread.currentThread().getName()+ "----消費者---------" + this.name);flag = false;condition_pro.signal();} finally {lock.unlock();}}}class Producer implements Runnable {private Resource res;Producer(Resource res) {this.res = res;}public void run() {while (true)try {res.set("+商品+");} catch (Exception e) {}}}class Consumer implements Runnable {private Resource res;Consumer(Resource res) {this.res = res;}public void run() {while (true)try {res.out();} catch (Exception e) {}}}class Test {public static void main(String[] args) {Resource r = new Resource();Producer pro = new Producer(r);Consumer con = new Consumer(r);Thread t1 = new Thread(pro),t2 = new Thread(pro),t3 = new Thread(con),t4 = new Thread(con);t1.start();t2.start();t3.start();t4.start();}}

 如何線程中斷:定義迴圈結束標記,因為線程的代碼一般都是迴圈,只要控制了迴圈即可; 特殊情況下,當凍結後,標記無法讀取,使用interrupt()方法,結束線程的凍結狀態,使線程回到運行狀態來,但會產生InterruptedException異常

stop方法已經不再使用

interrupt(): 如果線程在調用 Object 類的 wait()、wait(long)或 wait(long, int) 方法,或者該類的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法過程中受阻,則其中斷狀態將被清除,它還將收到一個 InterruptedException。

class StopThread implements Runnable {private boolean flag = true;public synchronized void run() {while (flag) {try {wait();} catch (InterruptedException e) {// 處理動結狀態System.out.println(Thread.currentThread().getName()+ "----Exception");flag = false;}System.out.println(Thread.currentThread().getName() + "----run");}}public void changeFlag() {flag = false;}}class Test {public static void main(String[] args) {StopThread st = new StopThread();Thread t1 = new Thread(st),t2 = new Thread(st);t1.start();t2.start();int num = 0;while (true) {if (num++ == 60) {// st.changeFlag();t1.interrupt();t2.interrupt();break;}System.out.println(Thread.currentThread().getName() + "-----" + num);}System.out.println("over");}}

後台線程:setDaemon(boolean on)將該線程標記為守護線程或使用者線程。當正在啟動並執行線程都是守護線程時,程式結束,JAVA 虛擬機器退出。 

yield() sleep() wait() 區別

sleep() 
 
  使當前線程暫停執行一段時間,讓其他線程有機會繼續執行,只是輪到它執行時,它不執行run方法只是在那暫停了對cpu的調度沒影響,但它並不釋放對象鎖。也就是如果           有Synchronized同步塊,其他線程仍然不同訪問共用資料。
join()  t.join(),正在啟動並執行線程在t線程運行結束後執行,也就是等待調用該方法的線程執行完畢後再往下繼續執行,如果有多個線程,不影響其它線程。
yield()   與sleep()類似,只是不能由使用者指定暫停多長時間 
wait()  wait()後,線程會釋放掉它所佔有的“鎖標誌”,從而使線程所在對象中的其它synchronized資料可被別的線程使用

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.