<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>

                ThinkChat2.0新版上線,更智能更精彩,支持會話、畫圖、視頻、閱讀、搜索等,送10W Token,即刻開啟你的AI之旅 廣告
                # JAVA之旅(十三)——線程的安全性,synchronized關鍵字,多線程同步代碼塊,同步函數,同步函數的鎖是this * * * > 我們繼續上個篇幅接著講線程的知識點 ## 一.線程的安全性 > 當我們開啟四個窗口(線程)把票陸陸續續的賣完了之后,我們要反思一下,這里面有沒有安全隱患呢?在實際情況中,這種事情我們是必須要去考慮安全問題的,那我們模擬一下錯誤 ~~~ package com.lgl.hellojava; import javax.security.auth.callback.TextInputCallback; //公共的 類 類名 public class HelloJJAVA { public static void main(String[] args) { /** * 需求:簡單的賣票程序,多個線程同時賣票 */ MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); Thread t3 = new Thread(myThread); Thread t4 = new Thread(myThread); t1.start(); t2.start(); t3.start(); t4.start(); } } /** * 賣票程序 * * @author LGL * */ class MyThread implements Runnable { // 票數 private int tick = 100; @Override public void run() { while (true) { if (tick > 0) { try { Thread.sleep(1000); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("賣票:" + tick--); } } } } ~~~ > 我們輸出的結果 ![這里寫圖片描述](http://img.blog.csdn.net/20160604101606637) > 這里出現了0票,如果你繼續跟蹤的話,你會發現,還會出現-1,-2之類的票,這就是安全隱患,那原因是什么呢? * 當多條語句在操作同一個線程共享數據時,一個線程對多條語句只執行了一個部分,還沒有執行完,另外一個線程參與了執行,導致共享數據的錯誤 > 解決辦法:對多條操作共享數據的語句,只能讓一個線程都執行完再執行過程中其他線程不可以參與運行 > > JAVA對多線程的安全問題提供了專業的解決辦法,就是同步代碼塊 ~~~ synchronized(對象){ //需要同步的代碼 } ~~~ > 那我們怎么用呢? ~~~ package com.lgl.hellojava; //公共的 類 類名 public class HelloJJAVA { public static void main(String[] args) { /** * 需求:簡單的賣票程序,多個線程同時賣票 */ MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); Thread t3 = new Thread(myThread); Thread t4 = new Thread(myThread); t1.start(); t2.start(); t3.start(); t4.start(); } } /** * 賣票程序 * * @author LGL * */ class MyThread implements Runnable { // 票數 private int tick = 100; Object oj = new Object(); @Override public void run() { while (true) { synchronized(oj){ if (tick > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("賣票:" + tick--); } } } } } ~~~ > 這樣,就輸出 ![這里寫圖片描述](http://img.blog.csdn.net/20160604102742924) ## 二.多線程同步代碼塊 > 我們為什么可以這樣去同步線程? > > 對象如同鎖,持有鎖的線程可以在同步中執行,沒有執行鎖的線程即使獲取了CPU的執行權,也進不去,因為沒有獲取鎖,我們可以這樣理解 * 四個線程,哪一個進去就開始執行,其他的拿不到執行權,所以即使拿到了執行權,也進不去,這個同步能解決線程的安全問題 > 但是,同步是有前提的 * 1.必須要有兩個或者兩個以上的線程,不然你同步也沒必要呀 * 2.必須是多個線程使用同一鎖 > 必須保證同步中只能有一個線程在運行 > > 但是他也有一個弊端:那就是多個線程都需要判斷鎖,較為消耗資源 ## 三.多線成同步函數 > 我們可以寫一段小程序,來驗證這個線程同步的問題,也就是說我們看看下面這段程序是否有安全問題,有的話,如何解決? ~~~ package com.lgl.hellojava; //公共的 類 類名 public class HelloJJAVA { public static void main(String[] args) { /** * 需求:銀行里有一個金庫 有兩個人要存錢300 */ MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); t1.start(); t2.start(); } } /** * 存錢程序,一次100 * @author LGL * */ class MyThread implements Runnable { private Bank b = new Bank(); @Override public void run() { for (int i = 0; i < 3; i++) { b.add(100); } } } /** * 銀行 * @author LGL * */ class Bank { private int sum; public void add(int n) { sum = sum + n; System.out.println("sum:" + sum); } } ~~~ > 當你執行的時候你會發現 ![這里寫圖片描述](http://img.blog.csdn.net/20160604175804400) > 這里是沒錯的,存了600塊錢,但是,這個程序是有安全隱患的 > > 如何找到問題? * 1.明確哪些代碼是多線成運行代碼 * 2.明確共享數據 * 3.明確多線成運行代碼中哪些語句是操作共享數據的 > 那我們怎么找到安全隱患呢?我們去銀行的類里面做些認為操作 ~~~ /** * 銀行 * @author LGL * */ class Bank { private int sum; public void add(int n) { sum = sum + n; try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("sum:" + sum); } } ~~~ > 讓他sleep一下你就會發現 ![這里寫圖片描述](http://img.blog.csdn.net/20160604180316485) > 這樣的話,我們就可以使用我們的同步代碼了 ~~~ /** * 銀行 * * @author LGL * */ class Bank { private int sum; Object j = new Object(); public void add(int n) { synchronized (j) { sum = sum + n; try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("sum:" + sum); } } } ~~~ > 這樣代碼就可以同步了 ![這里寫圖片描述](http://img.blog.csdn.net/20160604180509216) > 哪些代碼該同步,哪些不該同步,你一定要搞清楚,根據上面的3個條件 > > 大家有沒有注意到,函數式具有封裝代碼的特定,而我們所操作的同步代碼塊也是有封裝代碼的特性,拿這樣的話我們就可以換一種形式去操作,那就是寫成函數的修飾符 ~~~ /** * 銀行 * * @author LGL * */ class Bank { private int sum; public synchronized void add(int n) { sum = sum + n; try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println("sum:" + sum); } } ~~~ > 這樣也是OK的 ## 四.同步函數的鎖是this > 既然我們學習了另一種同步函數的寫法,那我們就可以把剛才的買票小例子進一步封裝一下了 ~~~ package com.lgl.hellojava; //公共的 類 類名 public class HelloJJAVA { public static void main(String[] args) { /** * 需求:簡單的賣票程序,多個線程同時賣票 */ MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); Thread t3 = new Thread(myThread); Thread t4 = new Thread(myThread); t1.start(); t2.start(); t3.start(); t4.start(); } } /** * 賣票程序 * * @author LGL * */ class MyThread implements Runnable { // 票數 private int tick = 100; @Override public synchronized void run() { while (true) { if (tick > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread()+"賣票:" + tick--); } } } } ~~~ > 但是這樣做,你卻會發現一個很嚴重的問題,那就是 ![這里寫圖片描述](http://img.blog.csdn.net/20160604181319734) > 永遠只有0線程在執行賣票 > > 那是因為我們并沒有搞清楚需要同步哪一個代碼段,我們應該執行的只是里面的那兩段代碼,而不是整個死循環,所以我們得封裝個函數進行線程同步 ~~~ package com.lgl.hellojava; //公共的 類 類名 public class HelloJJAVA { public static void main(String[] args) { /** * 需求:簡單的賣票程序,多個線程同時賣票 */ MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); Thread t3 = new Thread(myThread); Thread t4 = new Thread(myThread); t1.start(); t2.start(); t3.start(); t4.start(); } } /** * 賣票程序 * * @author LGL * */ class MyThread implements Runnable { // 票數 private int tick = 100; @Override public void run() { while (true) { show(); } } private synchronized void show() { if (tick > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread() + "賣票:" + tick--); } } } ~~~ > 這樣輸出解決了 ![這里寫圖片描述](http://img.blog.csdn.net/20160604181751286) > 問題是被解決了,但是隨之問題也就來了 * 同步函數用的是哪一個鎖呢? > 函數需要被對象調用,那么函數都有一個所屬對象的引用,就是this,所以同步函數所引用的鎖是this,我們來驗證一下,我們把程序改動一下 > > 使用兩個線程來賣票,一個線程在同步代碼塊中,一個線程在同步函數中,都在執行賣票動作 ~~~ package com.lgl.hellojava; //公共的 類 類名 public class HelloJJAVA { public static void main(String[] args) { /** * 需求:簡單的賣票程序,多個線程同時賣票 */ MyThread myThread = new MyThread(); Thread t1 = new Thread(myThread); Thread t2 = new Thread(myThread); // Thread t3 = new Thread(myThread); // Thread t4 = new Thread(myThread); t1.start(); myThread.flag = false; t2.start(); // t3.start(); // t4.start(); } } /** * 賣票程序 * * @author LGL * */ class MyThread implements Runnable { // 票數 private int tick = 100; Object j = new Object(); boolean flag = true; @Override public void run() { if (flag) { while (true) { synchronized (j) { if (tick > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread() + "code:" + tick--); } } } } else { while (true) { show(); } } } private synchronized void show() { if (tick > 0) { try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } System.out.println(Thread.currentThread() + "show:" + tick--); } } } ~~~ > 當我們運行的時候就發現 ![這里寫圖片描述](http://img.blog.csdn.net/20160604182642189) > 他只在show中進行,那是為什么呢?因為主線程開啟的時候瞬間執行,我們要修改一下,讓線程1開啟的時候,主線程睡個10毫秒試試 ~~~ t1.start(); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } myThread.flag = false; t2.start(); ~~~ > 這樣輸出的結果貌似是交替進行 ![這里寫圖片描述](http://img.blog.csdn.net/20160604183045569) > 但是所知而來的,是0票,這說明這個線程不安全,我們明明加了同步啊,怎么還是不安全呢?因為他用的不是同一個鎖,一個用Object,一個是用this的鎖,我們再改動一下,我們把Object更好為this,這樣輸出 ![這里寫圖片描述](http://img.blog.csdn.net/20160604183312384) > 現在就安全,也正確了 > > 好的,我們本篇幅就先到這里了,我們下篇也繼續講線程 #### 如果有興趣,可以加入群:555974449 版權聲明:本文為博主原創文章,博客地址:http://blog.csdn.net/qq_26787115,未經博主允許不得轉載。
                  <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>

                              哎呀哎呀视频在线观看