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)線程安全的三種方式

        共 4362字,需瀏覽 9分鐘

         ·

        2022-05-28 07:01

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

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

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

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

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

        1, 在不對多線程數(shù)據(jù)進(jìn)行保護(hù)的情況下會引發(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();
        ????????
        ????????
        ????}
        ????

        }

        上述代碼運(yùn)行的結(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ù)進(jìn)行保護(hù)時會出現(xiàn)同一張票會被出售多次!由于線程調(diào)度中的不確定性,讀者在演示上述代碼時,出現(xiàn)的運(yù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)試上述代碼的運(yùn)行結(jié)果

        第三種方式:Lock鎖機(jī)制

        通過創(chuàng)建Lock對象,采用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號窗口");
        ????????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是個不錯的方案。

        補(bǔ)充

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

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

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

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

        notify()方法會喚醒因為調(diào)用對象的wait()而處于等待狀態(tài)的線程,從而使得該線程有機(jī)會獲取對象鎖。調(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)的線程,從而使得該線程有機(jī)會獲取對象鎖。
        ????????????????????????//調(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)的線程,從而使得該線程有機(jī)會獲取對象鎖。
        ????????????????????????//調(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


        如有文章對你有幫助,

        在看”和轉(zhuǎn)發(fā)是對我最大的支持!

        推薦

        點擊領(lǐng)?。?span style="outline: 0px;max-width: 100%;letter-spacing: 0.544px;box-sizing: border-box !important;overflow-wrap: break-word !important;">151個大廠面試講解?。▓D片可上下滑動?。??

        瀏覽 39
        點贊
        評論
        收藏
        分享

        手機(jī)掃一掃分享

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

        手機(jī)掃一掃分享

        分享
        舉報
        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片 | 巨乳美乳视频 | 午夜精品一区二 | 欧美做爱无吗码 | 中文字幕一区二区三区免费2023 | 色开心激情网 | 免费观看又色又爽又黄的软件 | 伸进老师小内裤疯狂揉摸激吻小说 | 秋霞无码 | 好吊妞视频这里只有精品 |