1. <strong id="7actg"></strong>
    2. <table id="7actg"></table>

    3. <address id="7actg"></address>
      <address id="7actg"></address>
      1. <object id="7actg"><tt id="7actg"></tt></object>

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

        共 4504字,需瀏覽 10分鐘

         ·

        2021-01-06 15:05

        點擊上方藍色字體,選擇“標星公眾號”

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

        ? 作者?|? 潘彬

        來源 |? urlify.cn/jARjuq

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

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

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

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

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

        ?

        現(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é)果會有不同。

        ?

        第一種實現(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號窗口");
        ????????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ā)量比較高的情況下,其性能下降會很嚴重,此時ReentrantLock是個不錯的方案。

        ?

         補充:  

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

          notify()方法會喚醒因為調(diào)用對象的wait()而處于等待狀態(tài)的線程,從而使得該線程有機會獲取對象鎖。調(diào)用notify()后,當前線程并不會立即釋放鎖,而是繼續(xù)執(zhí)行當前代碼,直到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()后,當前線程并不會立即釋放鎖,而是繼續(xù)執(zhí)行當前代碼,直到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()后,當前線程并不會立即釋放鎖,而是繼續(xù)執(zhí)行當前代碼,直到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從入門到入土學習路線圖

        ???

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


        感謝點贊支持下哈?

        瀏覽 121
        點贊
        評論
        收藏
        分享

        手機掃一掃分享

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

        手機掃一掃分享

        分享
        舉報
        1. <strong id="7actg"></strong>
        2. <table id="7actg"></table>

        3. <address id="7actg"></address>
          <address id="7actg"></address>
          1. <object id="7actg"><tt id="7actg"></tt></object>
            A片操逼 亚洲97视频 | 国产精品国产三级国产传播 | 美日韩操逼视频 | 久久精品日本啪啪涩涩 | 色哟哟 入口国产精品 | 国产高清码 在线观看 | 亚洲第一电影网站 | 亚洲国产高清无码在线观看 | 日韩精品一区二区三区免费观看 | 亚洲三级影视 |