<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 實(shí)現(xiàn)線程安全的三種方式

          共 4504字,需瀏覽 10分鐘

           ·

          2021-01-06 15:05

          點(diǎn)擊上方藍(lán)色字體,選擇“標(biāo)星公眾號(hào)”

          優(yōu)質(zhì)文章,第一時(shí)間送達(dá)

          ? 作者?|? 潘彬

          來源 |? urlify.cn/jARjuq

          66套java從入門到精通實(shí)戰(zhàn)課程分享

          一個(gè)程序在運(yùn)行起來的時(shí)候會(huì)轉(zhuǎn)換成進(jìn)程,通常含有多個(gè)線程。

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

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

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

          ?

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

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

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

          ????????@Override
          ????????public?void?run()?{
          ????????????//?未加同步時(shí)產(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號(hào)窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號(hào)窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號(hào)窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號(hào)窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????????
          ????}
          ????

          }

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

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

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

          ?

          第一種實(shí)現(xiàn)線程安全的方式

            同步代碼塊

          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號(hào)窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號(hào)窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號(hào)窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號(hào)窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????????
          ????}
          ????

          }

          輸出結(jié)果讀者可自行調(diào)試,不會(huì)出現(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號(hào)窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號(hào)窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號(hào)窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號(hào)窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????}

          }

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

          ?

          第三種 方式

            Lock鎖機(jī)制, 通過創(chuàng)建Lock對(duì)象,采用lock()加鎖,unlock()解鎖,來保護(hù)指定的代碼塊

          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鎖機(jī)制
          ????????????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號(hào)窗口");
          ????????Thread?thread2?=?new?Thread(sell,?"2號(hào)窗口");
          ????????Thread?thread3?=?new?Thread(sell,?"3號(hào)窗口");
          ????????Thread?thread4?=?new?Thread(sell,?"4號(hào)窗口");
          ????????
          ????????thread1.start();
          ????????thread2.start();
          ????????thread3.start();
          ????????thread4.start();
          ????????
          ????????
          ????}
          ????

          }

           最后總結(jié):

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

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

          ?

           補(bǔ)充:  

            在使用synchronized 代碼塊時(shí),可以與wait()、notify()、nitifyAll()一起使用,從而進(jìn)一步實(shí)現(xiàn)線程的通信。
          其中,wait()方法會(huì)釋放占有的對(duì)象鎖,當(dāng)前線程進(jìn)入等待池,釋放cpu,而其他正在等待的線程即可搶占此鎖,獲得鎖的線程即可運(yùn)行程序;線程的sleep()方法則表示,當(dāng)前線程會(huì)休眠一段時(shí)間,休眠期間,會(huì)暫時(shí)釋放cpu,但并不釋放對(duì)象鎖,也就是說,在休眠期間,其他線程依然無法進(jìn)入被同步保護(hù)的代碼內(nèi)部,當(dāng)前線程休眠結(jié)束時(shí),會(huì)重新獲得cpu執(zhí)行權(quán),從而執(zhí)行被同步保護(hù)的代碼。
          wait()和sleep()最大的不同在于wait()會(huì)釋放對(duì)象鎖,而sleep()不會(huì)釋放對(duì)象鎖。

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

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

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

          ?

          下面是示例代碼,

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

          public?class?ThreadDemo?{
          ????
          ????static?final?Object?obj?=?new?Object();
          ????
          ????//第一個(gè)子線程
          ????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()方法會(huì)喚醒因?yàn)檎{(diào)用對(duì)象的wait()而處于等待狀態(tài)的線程,從而使得該線程有機(jī)會(huì)獲取對(duì)象鎖。
          ????????????????????????//調(diào)用notify()后,當(dāng)前線程并不會(huì)立即釋放鎖,而是繼續(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()方法會(huì)喚醒因?yàn)檎{(diào)用對(duì)象的wait()而處于等待狀態(tài)的線程,從而使得該線程有機(jī)會(huì)獲取對(duì)象鎖。
          ????????????????????????//調(diào)用notify()后,當(dāng)前線程并不會(huì)立即釋放鎖,而是繼續(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();
          ????????
          ????}

          }






          粉絲福利:Java從入門到入土學(xué)習(xí)路線圖

          ???

          ?長按上方微信二維碼?2 秒


          感謝點(diǎn)贊支持下哈?

          瀏覽 121
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <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青草国产精品视频无码一区 | www.奇米影视 | 亚洲综合精品在线 | 不要播放器的av网站 |