<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                企業??AI智能體構建引擎,智能編排和調試,一鍵部署,支持知識庫和私有化部署方案 廣告
                [TOC] > 多線程的同步問題指的是多個線程同時修改一個數據的時候,可能導致的問題 > 多線程的問題,又叫Concurrency 問題 # 演示同步問題 假設蓋倫有10000滴血,并且在基地里,同時又被對方多個英雄攻擊 就是 **有多個線程在減少蓋倫的hp** 同時又有 **多個線程在恢復蓋倫的hp** 假設線程的數量是一樣的,并且每次改變的值都是1,那么所有線程結束后,蓋倫應該還是10000滴血。 但是。。。 > 注意: 不是每一次運行都會看到錯誤的數據產生,多運行幾次,或者增加運行的次數 ``` package multiplethread; import charactor.Hero4; public class TestThread8 { public static void main(String[] args) { Hero4 garen = new Hero4(); garen.name = "蓋倫"; garen.hp = 10000; System.out.printf("蓋倫的初始血量是 %.0f%n", garen.hp); // 多線程同步問題指的是多個線程同時修改一個數據的時候,導致的問題 // 假設蓋倫有10000滴血,并且在基地里,同時又被對方多個英雄攻擊 // 用JAVA代碼來表示,就是有多個線程在減少蓋倫的hp // 同時又有多個線程在恢復蓋倫的hp // n個線程增加蓋倫的hp int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { garen.recover(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } // n個線程減少蓋倫的hp for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { garen.hurt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } // 等待所有增加線程結束 for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 等待所有減少線程結束 for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } // 代碼執行到這里,所有增加和減少線程都結束了 // 增加和減少線程的數量是一樣的,每次都增加,減少1. // 那么所有線程都結束后,蓋倫的hp應該還是初始值 // 但是事實上觀察到的是: System.out.printf("%d個增加線程和%d個減少線程結束后%n蓋倫的血量變成了 %.0f%n", n, n, garen.hp); } } ``` # 分析同步問題產生的原因 1. 假設**增加線程**先進入,得到的hp是10000 2. 進行增加運算 3. 正在做增加運算的時候,**還沒有來得及修改hp的值,減少線程**來了 4. 減少線程得到的hp的值也是10000 5. 減少線程進行減少運算 6. 增加線程運算結束,得到值10001,并把這個值賦予hp 7. 減少線程也運算結束,得到值9999,并把這個值賦予hp hp,最后的值就是9999 雖然經歷了兩個線程各自增減了一次,本來期望還是原值10000,但是卻得到了一個9999 這個時候的值9999是一個錯誤的值,在業務上又叫做**臟數據** ![](https://box.kancloud.cn/262719abc206df9a8e770f17067cfc02_869x397.png) # synchronized 同步對象概念 解決上述問題之前,先理解 **synchronized**關鍵字的意義 如下代碼: ``` Object someObject =new Object(); synchronized (someObject){ //此處的代碼只有占有了someObject后才可以執行 } ``` **synchronized表示當前線程,獨占 對象 someObject** 當前線程**獨占** 了對象someObject,如果有**其他線程試圖占有對象**someObject,**就會等待**,直到當前線程釋放對someObject的占用。 someObject 又叫同步對象,所有的對象,都可以作為同步對象 為了達到同步的效果,必須使用同一個同步對象 **釋放同步對象**的方式: synchronized 塊自然結束,或者有異常拋出 ![](https://box.kancloud.cn/40f16ecf368c14206c48a4dd67c3d5f6_679x230.png) ``` package multiplethread; import java.text.SimpleDateFormat; import java.util.Date; public class TestThread10 { public static String now() { return new SimpleDateFormat("HH:mm:ss").format(new Date()); } public static void main(String[] args) { final Object someObject = new Object(); Thread t1 = new Thread() { public void run() { try { System.out.println(now() + " t1 線程已經運行"); System.out.println(now() + this.getName() + " 試圖占有對象:someObject"); synchronized (someObject) { System.out.println(now() + this.getName() + " 占有對象:someObject"); Thread.sleep(5000); System.out.println(now() + this.getName() + " 釋放對象:someObject"); } System.out.println(now() + " t1 線程結束"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t1.setName(" t1"); t1.start(); Thread t2 = new Thread() { public void run() { try { System.out.println(now() + " t2 線程已經運行"); System.out.println(now() + this.getName() + " 試圖占有對象:someObject"); synchronized (someObject) { System.out.println(now() + this.getName() + " 占有對象:someObject"); Thread.sleep(5000); System.out.println(now() + this.getName() + " 釋放對象:someObject"); } System.out.println(now() + " t2 線程結束"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t2.setName(" t2"); t2.start(); } } ``` # 使用synchronized 解決同步問題 所有需要修改hp的地方,有要**建立在占有someObject的基礎上**。 而對象 someObject在同一時間,只能被一個線程占有。 間接地,**導致同一時間,hp只能被一個線程修改**。 ``` package multiplethread; import charactor.Hero4; public class TestThread11 { public static void main(String[] args) { Object someObject = new Object(); Hero4 garen = new Hero4(); garen.name = "蓋倫"; garen.hp = 10000; System.out.printf("蓋倫的初始血量是 %.0f%n", garen.hp); int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { // 任何線程要修改hp的值,必須先占用someObject synchronized (someObject) { garen.recover(); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { // 任何線程要修改hp的值,必須先占用someObject synchronized (someObject) { garen.hurt(); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d個增加線程和%d個減少線程結束后%n蓋倫的血量是 %.0f%n", n, n, garen.hp); } } ``` # 使用hero對象作為同步對象 既然任意對象都可以用來作為同步對象,而所有的線程訪問的都是同一個hero對象,**索性就使用garen來作為同步對象** 進一步的,對于Hero的hurt方法,加上: ``` synchronized (this) { } ``` 表示當期對象為同步對象,即也是garen為同步對象 ``` package multiplethread; import charactor.Hero5; public class TestThread12 { public static void main(String[] args) { Hero5 garen = new Hero5(); garen.name = "蓋倫"; garen.hp = 10000; System.out.printf("蓋倫的初始血量是 %.0f%n", garen.hp); int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { // 使用garen作為synchronized synchronized (garen) { garen.recover(); } try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { // 使用garen作為synchronized // 在方法hurt中有synchronized(this) garen.hurt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d個增加線程和%d個減少線程結束后%n蓋倫的血量是 %.0f%n", n, n, garen.hp); } } ``` # 在方法前,加上修飾符synchronized 在recover前,直接加上synchronized ,其所對應的同步對象,就是this 和hurt方法達到的效果是一樣 外部線程訪問garen的方法,就不需要額外使用synchronized 了 ``` package multiplethread; import charactor.Hero6; public class TestThread13 { public static void main(String[] args) { Hero6 garen = new Hero6(); garen.name = "蓋倫"; garen.hp = 10000; System.out.printf("蓋倫的初始血量是 %.0f%n", garen.hp); int n = 10000; Thread[] addThreads = new Thread[n]; Thread[] reduceThreads = new Thread[n]; for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { // recover自帶synchronized garen.recover(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); addThreads[i] = t; } for (int i = 0; i < n; i++) { Thread t = new Thread() { public void run() { // hurt自帶synchronized garen.hurt(); try { Thread.sleep(100); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); reduceThreads[i] = t; } for (Thread t : addThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } for (Thread t : reduceThreads) { try { t.join(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } System.out.printf("%d個增加線程和%d個減少線程結束后%n蓋倫的血量是 %.0f%n", n, n, garen.hp); } } ``` # 線程安全的類 如果一個類,其**方法都是有synchronized修飾的**,那么該類就叫做**線程安全的類 ** 同一時間,只有一個線程能夠進入 **這種類的一個實例** 的去修改數據,進而保證了這個實例中的數據的安全(不會同時被多線程修改而變成臟數據) 比如StringBuffer和StringBuilder的區別 StringBuffer的方法都是有synchronized修飾的,StringBuffer就叫做線程安全的類 而StringBuilder就不是線程安全的類 ``` ```
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看