<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:Java 實現(xiàn)線程安全的三種方式

          共 4546字,需瀏覽 10分鐘

           ·

          2022-05-10 02:54


          大家好,我是程序汪,關(guān)于線程安全是面試中比較高頻的問題,強烈建議大家掌握下。

          一個程序在運行起來的時候會轉(zhuǎn)換成進程,通常含有多個線程。

          通常情況下,一個進程中的比較耗時的操作(如長循環(huán)、文件上傳下載、網(wǎng)絡(luò)資源獲取等),往往會采用多線程來解決。

          比如顯示生活中,銀行取錢問題、火車票多個售票窗口的問題,通常會涉及到并發(fā)的問題,從而需要多線程的技術(shù)。

          當(dāng)進程中有多個并發(fā)線程進入一個重要數(shù)據(jù)的代碼塊時,在修改數(shù)據(jù)的過程中,很有可能引發(fā)線程安全問題,從而造成數(shù)據(jù)異常。例如,正常邏輯下,同一個編號的火車票只能售出一次,卻由于線程安全問題而被多次售出,從而引起實際業(yè)務(wù)異常。

          現(xiàn)在我們就以售票問題來演示線程安全的問題

          1, 在不對多線程數(shù)據(jù)進行保護的情況下會引發(fā)的狀況

          public?class?ThreadUnSecurity?{
          ????
          ????static?int?tickets?=?10;
          ????
          ????class?SellTickets?implements?Runnable{

          ????????@Override
          ????????public?void?run()?{
          ????????????//?未加同步時產(chǎn)生臟數(shù)據(jù)
          ????????????while(tickets?>?0)?{
          ????????????????
          ????????????????System.out.println(Thread.currentThread().getName()+"--->售出第:??"+tickets+"?票");
          ????????????????tickets--;
          ????????????????
          ????????????????try?{
          ????????????????????Thread.sleep(1000);
          ????????????????}?catch?(InterruptedException?e)?{
          ????????????????????e.printStackTrace();
          ????????????????}
          ????????????????
          ????????????}
          ????????????
          ????????????if?(tickets?<=?0)?{
          ????????????????
          ????????????????System.out.println(Thread.currentThread().getName()+"--->售票結(jié)束!");
          ????????????}
          ????????}
          ????}
          ????
          ????
          ????public?static?void?main(String[]?args)?{
          ????????
          ????????
          ????????SellTickets?sell?=?new?ThreadUnSecurity().new?SellTickets();
          ????????
          ????????Thread?thread1?=?new?Thread(sell,?"1號窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????????
          ????}
          ????

          }

          上述代碼運行的結(jié)果:

          1號窗口--->售出第:? 10?票
          3號窗口--->售出第:? 10?票
          2號窗口--->售出第:? 10?票
          4號窗口--->售出第:? 10?票
          2號窗口--->售出第:? 6 票
          1號窗口--->售出第:? 5 票
          3號窗口--->售出第:? 4 票
          4號窗口--->售出第:? 3 票
          2號窗口--->售出第:? 2 票
          4號窗口--->售出第:? 1 票
          1號窗口--->售出第:? 1 票
          3號窗口--->售票結(jié)束!
          2號窗口--->售票結(jié)束!
          1號窗口--->售票結(jié)束!
          4號窗口--->售票結(jié)束!

          我們可以看出同一張票在不對票數(shù)進行保護時會出現(xiàn)同一張票會被出售多次!由于線程調(diào)度中的不確定性,讀者在演示上述代碼時,出現(xiàn)的運行結(jié)果會有不同。

          第一種方式:同步代碼塊

          package?com.bpan.spring.beans.thread;

          import?com.sun.org.apache.regexp.internal.recompile;

          public?class?ThreadSynchronizedSecurity?{
          ????
          ????static?int?tickets?=?10;
          ????
          ????class?SellTickets?implements?Runnable{

          ????????@Override
          ????????public?void?run()?{
          ????????????//?同步代碼塊
          ????????????while(tickets?>?0)?{
          ????????????????
          ????????????????synchronized?(this)?{
          ????????????????????
          //????????????????????System.out.println(this.getClass().getName().toString());
          ????????????????????
          ????????????????????if?(tickets?<=?0)?{
          ????????????????????????
          ????????????????????????return;
          ????????????????????}
          ????????????????????
          ????????????????????System.out.println(Thread.currentThread().getName()+"--->售出第:??"+tickets+"?票");
          ????????????????????tickets--;
          ????????????????????
          ????????????????????try?{
          ????????????????????????Thread.sleep(100);
          ????????????????????}?catch?(InterruptedException?e)?{
          ????????????????????????e.printStackTrace();
          ????????????????????}
          ????????????????}
          ????????????????
          ????????????????if?(tickets?<=?0)?{
          ????????????????????
          ????????????????????System.out.println(Thread.currentThread().getName()+"--->售票結(jié)束!");
          ????????????????}
          ????????????}
          ????????}
          ????}
          ????
          ????
          ????public?static?void?main(String[]?args)?{
          ????????
          ????????
          ????????SellTickets?sell?=?new?ThreadSynchronizedSecurity().new?SellTickets();
          ????????
          ????????Thread?thread1?=?new?Thread(sell,?"1號窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????????
          ????}
          ????

          }

          輸出結(jié)果讀者可自行調(diào)試,不會出現(xiàn)同一張票被出售多次的情況。

          第二種方式:同步方法

          package?com.bpan.spring.beans.thread;

          public?class?ThreadSynchroniazedMethodSecurity?{
          ????
          ????
          ????static?int?tickets?=?10;
          ????
          ????class?SellTickets?implements?Runnable{

          ????????@Override
          ????????public?void?run()?{
          ????????????//同步方法
          ????????????while?(tickets?>?0)?{
          ????????????????
          ????????????????synMethod();
          ????????????????
          ????????????????try?{
          ????????????????????Thread.sleep(100);
          ????????????????}?catch?(InterruptedException?e)?{
          ????????????????????//?TODO?Auto-generated?catch?block
          ????????????????????e.printStackTrace();
          ????????????????}
          ????????????????
          ????????????????if?(tickets<=0)?{
          ????????????????????
          ????????????????????System.out.println(Thread.currentThread().getName()+"--->售票結(jié)束");
          ????????????????}
          ????????????????
          ????????????}
          ????????????
          ????????????
          ????????}
          ????????
          ????????synchronized?void?synMethod()?{
          ????????????
          ????????????synchronized?(this)?{
          ????????????????if?(tickets?<=0)?{
          ????????????????????
          ????????????????????return;
          ????????????????}
          ????????????????
          ????????????????System.out.println(Thread.currentThread().getName()+"---->售出第?"+tickets+"?票?");
          ????????????????tickets--?;
          ????????????}
          ????????????
          ????????}
          ????????
          ????}
          ????public?static?void?main(String[]?args)?{
          ????????
          ????????
          ????????SellTickets?sell?=?new?ThreadSynchroniazedMethodSecurity().new?SellTickets();
          ????????
          ????????Thread?thread1?=?new?Thread(sell,?"1號窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????}

          }

          讀者可自行調(diào)試上述代碼的運行結(jié)果

          第三種方式:Lock鎖機制

          通過創(chuàng)建Lock對象,采用lock()加鎖,unlock()解鎖,來保護指定的代碼塊

          package?com.bpan.spring.beans.thread;

          import?java.util.concurrent.locks.Lock;
          import?java.util.concurrent.locks.ReentrantLock;

          public?class?ThreadLockSecurity?{
          ????
          ????static?int?tickets?=?10;
          ????
          ????class?SellTickets?implements?Runnable{
          ????????
          ????????Lock?lock?=?new?ReentrantLock();

          ????????@Override
          ????????public?void?run()?{
          ????????????//?Lock鎖機制
          ????????????while(tickets?>?0)?{
          ????????????????
          ????????????????try?{
          ????????????????????lock.lock();
          ????????????????????
          ????????????????????if?(tickets?<=?0)?{
          ????????????????????????
          ????????????????????????return;
          ????????????????????}
          ????????????????????????
          ????????????????????System.out.println(Thread.currentThread().getName()+"--->售出第:??"+tickets+"?票");
          ????????????????????tickets--;
          ????????????????}?catch?(Exception?e1)?{
          ????????????????????//?TODO?Auto-generated?catch?block
          ????????????????????e1.printStackTrace();
          ????????????????}finally?{
          ????????????????????
          ????????????????????lock.unlock();
          ????????????????????try?{
          ????????????????????????Thread.sleep(100);
          ????????????????????}?catch?(InterruptedException?e)?{
          ????????????????????????e.printStackTrace();
          ????????????????????}
          ????????????????}
          ????????????}
          ????????????????
          ????????????if?(tickets?<=?0)?{
          ????????????????
          ????????????????System.out.println(Thread.currentThread().getName()+"--->售票結(jié)束!");
          ????????????}
          ????????????
          ????????}
          ????}
          ????
          ????
          ????public?static?void?main(String[]?args)?{
          ????????
          ????????
          ????????SellTickets?sell?=?new?ThreadLockSecurity().new?SellTickets();
          ????????
          ????????Thread?thread1?=?new?Thread(sell,?"1號窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????????
          ????}
          ????

          }

          最后總結(jié)

          由于synchronized是在JVM層面實現(xiàn)的,因此系統(tǒng)可以監(jiān)控鎖的釋放與否;而ReentrantLock是使用代碼實現(xiàn)的,系統(tǒng)無法自動釋放鎖,需要在代碼中的finally子句中顯式釋放鎖lock.unlock()

          另外,在并發(fā)量比較小的情況下,使用synchronized是個不錯的選擇;但是在并發(fā)量比較高的情況下,其性能下降會很嚴(yán)重,此時ReentrantLock是個不錯的方案。

          補充

          在使用synchronized 代碼塊時,可以與wait()notify()nitifyAll()一起使用,從而進一步實現(xiàn)線程的通信。

          其中,wait()方法會釋放占有的對象鎖,當(dāng)前線程進入等待池,釋放cpu,而其他正在等待的線程即可搶占此鎖,獲得鎖的線程即可運行程序;

          線程的sleep()方法則表示,當(dāng)前線程會休眠一段時間,休眠期間,會暫時釋放cpu,但并不釋放對象鎖,也就是說,在休眠期間,其他線程依然無法進入被同步保護的代碼內(nèi)部,當(dāng)前線程休眠結(jié)束時,會重新獲得cpu執(zhí)行權(quán),從而執(zhí)行被同步保護的代碼。

          wait()sleep()最大的不同在于wait()會釋放對象鎖,而sleep()不會釋放對象鎖。

          notify()方法會喚醒因為調(diào)用對象的wait()而處于等待狀態(tài)的線程,從而使得該線程有機會獲取對象鎖。調(diào)用notify()后,當(dāng)前線程并不會立即釋放鎖,而是繼續(xù)執(zhí)行當(dāng)前代碼,直到synchronized中的代碼全部執(zhí)行完畢,才會釋放對象鎖。JVM會在等待的線程中調(diào)度一個線程去獲得對象鎖,執(zhí)行代碼。

          需要注意的是,wait()和notify()必須在synchronized代碼塊中調(diào)用。

          notifyAll()是喚醒所有等待的線程。

          下面是示例代碼,

          package?com.bpan.spring.beans.thread;

          public?class?ThreadDemo?{
          ????
          ????static?final?Object?obj?=?new?Object();
          ????
          ????//第一個子線程
          ????static?class?ThreadA?implements?Runnable{

          ????????@Override
          ????????public?void?run()?{
          ????????????
          ????????????
          ????????????int?count?=?10;
          ????????????while(count?>?0)?{
          ????????????????
          ????????????????synchronized?(ThreadDemo.obj)?{
          ????????????????????
          ????????????????????System.out.println("A-----"+count);
          ????????????????????count--;
          ????????????????????
          ????????????????????synchronized?(ThreadDemo.obj)?{
          ????????????????????????
          ????????????????????????//notify()方法會喚醒因為調(diào)用對象的wait()而處于等待狀態(tài)的線程,從而使得該線程有機會獲取對象鎖。
          ????????????????????????//調(diào)用notify()后,當(dāng)前線程并不會立即釋放鎖,而是繼續(xù)執(zhí)行當(dāng)前代碼,直到synchronized中的代碼全部執(zhí)行完畢,
          ????????????????????????ThreadDemo.obj.notify();
          ????????????????????????
          ????????????????????????try?{
          ????????????????????????????ThreadDemo.obj.wait();
          ????????????????????????}?catch?(InterruptedException?e)?{
          ????????????????????????????//?TODO?Auto-generated?catch?block
          ????????????????????????????e.printStackTrace();
          ????????????????????????}
          ????????????????????}
          ????????????????}
          ????????????}
          ????????????
          ????????}
          ????????
          ????}
          ????
          ????static?class?ThreadB?implements?Runnable{
          ????????
          ????????
          ????????@Override
          ????????public?void?run()?{
          ????????????
          ????????????int?count?=?10;
          ????????????
          ????????????while(count?>?0)?{
          ????????????????
          ????????????????synchronized?(ThreadDemo.obj)?{
          ????????????????????System.out.println("B-----"+count);
          ????????????????????count--;
          ????????????????????
          ????????????????????synchronized?(ThreadDemo.obj)?{
          ????????????????????
          ????????????????????????//notify()方法會喚醒因為調(diào)用對象的wait()而處于等待狀態(tài)的線程,從而使得該線程有機會獲取對象鎖。
          ????????????????????????//調(diào)用notify()后,當(dāng)前線程并不會立即釋放鎖,而是繼續(xù)執(zhí)行當(dāng)前代碼,直到synchronized中的代碼全部執(zhí)行完畢,
          ????????????????????????ThreadDemo.obj.notify();
          ????????????????????????
          ????????????????????????try?{
          ????????????????????????????ThreadDemo.obj.wait();
          ????????????????????????}?catch?(InterruptedException?e)?{
          ????????????????????????????//?TODO?Auto-generated?catch?block
          ????????????????????????????e.printStackTrace();
          ????????????????????????}
          ????????????????????}
          ????????????????????
          ????????????????}
          ????????????????
          ????????????}
          ????????????
          ????????}
          ????????
          ????}
          ????
          ????public?static?void?main(String[]?args)?{
          ????????
          ????????
          ????????new?Thread(new?ThreadA()).start();
          ????????new?Thread(new?ThreadB()).start();
          ????????
          ????}

          }

          來源:cnblogs.com/revel171226/p/9411131.html

          程序汪資料鏈接

          程序汪接的7個私活都在這里,經(jīng)驗整理

          Java項目分享 ?最新整理全集,找項目不累啦 07版

          堪稱神級的Spring Boot手冊,從基礎(chǔ)入門到實戰(zhàn)進階

          臥槽!字節(jié)跳動《算法中文手冊》火了,完整版 PDF 開放下載!

          臥槽!阿里大佬總結(jié)的《圖解Java》火了,完整版PDF開放下載!

          字節(jié)跳動總結(jié)的設(shè)計模式 PDF 火了,完整版開放下載!


          歡迎添加程序汪個人微信 itwang009? 進粉絲群或圍觀朋友

          瀏覽 36
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  丁香五月五月丁香 | 国产毛片一区二区三区在线视频 | 99热高清在线观看 | 神马午夜精品95 | 西西WWW444大胆视频 |