1. 程式人生 > >java 多執行緒-2

java 多執行緒-2

### 七、執行緒生命週期 沒錯,執行緒也是有生命週期的。就好像人類有出生、兒童、青年、中年、晚年、死亡一般。下面是執行緒的生命週期圖: ![](https://img2020.cnblogs.com/blog/1947461/202009/1947461-20200912160101785-638323818.png) ### 八、執行緒的安全問題 所謂執行緒不安全【併發問題】,舉個例子來說,如賣票,會出現重票、錯票等現象,這就是執行緒不安全的。 > 並行:多個CPU同時執行多個任務。比如:多個人同時做不同的事 > > 併發:一個CPU(採用時間片)**同時執行多個任務。比如:秒殺、多個人做同一件事**。 如: ~~~java /** * 建立三個視窗買票,共100張票。用Runnable介面實現 */ public class RunMainRunnable { public static void main(String[] args) { Window window = new Window(); // 建立三個執行緒 Thread win1 = new Thread(window); Thread win2 = new Thread(window); Thread win3 = new Thread(window); // 設定執行緒的名字 win1.setName("視窗一:"); win2.setName("視窗二:"); win3.setName("視窗三:"); // 啟動執行緒 win1.start(); win2.start(); win3.start(); } } class Window implements Runnable{ private int ticket = 100; // 定義100張票 @Override public void run() { while (true) { if (ticket > 0) { try { Thread.sleep(100); // 呼叫此方法,讓效果明顯一點 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"獲取到了第"+ticket+"票"); ticket--; }else{ break; } } } } ~~~ **執行結果:**出現了重票 ![](https://img2020.cnblogs.com/blog/1947461/202009/1947461-20200912160202334-123978959.png) **問題分析:** > 之所以會出現重票、錯票等問題,就是因為出現了執行緒不安全。【假設此時ticket=7】在if判斷中,如當視窗一獲得cpu後,首先判斷ticket>0會真,執行列印語句輸出“視窗一:獲取到了第7票”,此時視窗二獲得cpu【注意:視窗一併未執行“ticket--;”,ticket依然為7】,判斷ticket>0為真,執行列印語句輸出“視窗二:獲取到了第7票”,此時視窗三獲得cpu【注意:視窗二並未執行“ticket--;”,ticket依然為7】,判斷ticket>0為真,執行列印語句輸出“視窗三:獲取到了第7票”。便會出現重票。這在實際生活當中,肯定是不允許的,那我們該如何解決執行緒的安全問題呢? ### 九、同步機制 >在Java中,我們通過同步機制,來解決執行緒的安全問題。 > >**方式一:同步程式碼塊** > >~~~java >synchronized(同步監視器){ > //需要被同步的程式碼 > >} >~~~ > >說明: > >1. 什麼是同步的程式碼? > - 操作共享資料的程式碼,即為需要被同步的程式碼。 >2. 什麼是共享資料? > - 多個執行緒共同操作的變數。比如:ticket就是共享資料。 >3. 什麼是同步監視器 > - 俗稱:鎖。任何一個類的物件,都可以充當鎖。 > - **要求:多個執行緒必須要共用同一把鎖。** > >**方式二:同步方法** > > 如果操作共享資料的程式碼完整的宣告在一個方法中,我們不妨將此方法宣告為同步的。 > >**總結**: > >好處:同步的方式,解決了執行緒的安全問題。 > >壞處:操作同步程式碼時,只能有一個執行緒參與,其他執行緒等待。相當於是一個單執行緒的過程,效率低。 ---侷限性 eg1:方式一:同步程式碼塊 ~~~java /** * 建立三個視窗買票,共100張票。用Runnable介面實現 */ public class RunMainRunnable { public static void main(String[] args) { Window window = new Window(); // 建立三個執行緒 Thread win1 = new Thread(window); Thread win2 = new Thread(window); Thread win3 = new Thread(window); // 設定執行緒的名字 win1.setName("視窗一:"); win2.setName("視窗二:"); win3.setName("視窗三:"); // 啟動執行緒 win1.start(); win2.start(); win3.start(); } } /** * 方式一;同步程式碼塊 */ class Window implements Runnable{ private int ticket = 100; // 定義100張票 private Object object = new Object(); @Override public void run() { while (true) { synchronized (object){ // 同步程式碼塊 if (ticket > 0) { try { Thread.sleep(100); // 呼叫此方法,讓效果明顯一點 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"獲取到了第"+ticket+"票"); ticket--; }else{ break; } } } } } ~~~ eg2:方式二:同步方法 ~~~java /** * 建立三個視窗買票,共100張票。用Runnable介面實現 */ public class RunMainRunnable { public static void main(String[] args) { Window window = new Window(); // 建立三個執行緒 Thread win1 = new Thread(window); Thread win2 = new Thread(window); Thread win3 = new Thread(window); // 設定執行緒的名字 win1.setName("視窗一:"); win2.setName("視窗二:"); win3.setName("視窗三:"); // 啟動執行緒 win1.start(); win2.start(); win3.start(); } } /* 方式二;同步方法 * */ class Window implements Runnable{ private int ticket = 100; // 定義100張票 @Override public void run() { while (true) { show();// 呼叫同步方法 if (ticket == 0) { break; // 用來結束迴圈 } } } // 定義一個私有方法 private synchronized void show() { // 加synchronized關鍵字,使其成為一位同步方法。同步監視器或鎖即為:this if (ticket > 0) { try { Thread.sleep(100); // 呼叫此方法,讓效果明顯一點 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"獲取到了第"+ticket+"票"); ticket--; } }