synchronized 與 Lock 的那點(diǎn)事

      網(wǎng)友投稿 751 2025-04-04

      原文:http://www.cnblogs.com/benshan/p/3551987.html


      最近在做一個監(jiān)控系統(tǒng),該系統(tǒng)主要包括對數(shù)據(jù)實(shí)時分析和存儲兩個部分,由于并發(fā)量比較高,所以不可避免的使用到了一些并發(fā)的知識。為了實(shí)現(xiàn)這些要求,后臺使用一個隊(duì)列作為緩存,對于請求只管往緩存里寫數(shù)據(jù)。同時啟動一個線程監(jiān)聽該隊(duì)列,檢測到數(shù)據(jù),立即請求調(diào)度線程,對數(shù)據(jù)進(jìn)行處理。 具體的使用方案就是使用同步保證數(shù)據(jù)的正常,使用線程池提高效率。

      同步的實(shí)現(xiàn)當(dāng)然是采用鎖了,java中使用鎖的兩個基本工具是 Synchronized 和 Lock。

      一直很喜歡Synchronized,因?yàn)槭褂盟芊奖恪1热纾枰獙σ粋€方法進(jìn)行同步,那么只需在方法的簽名添加一個synchronized關(guān)鍵字。

      // 未同步的方法

      public void test() {}

      // 同步的方法

      pubilc synchronized void test() {}

      synchronized 也可以用在一個代碼塊上,看

      public void test() {

      synchronized(obj) {

      System.out.println("===");

      }

      }

      synchronized 用在方法和代碼塊上有什么區(qū)別呢?

      synchronized 用在方法簽名上(以test為例),當(dāng)某個線程調(diào)用此方法時,會獲取該實(shí)例的對象鎖,方法未結(jié)束之前,其他線程只能去等待。當(dāng)這個方法執(zhí)行完時,才會釋放對象鎖。其他線程才有機(jī)會去搶占這把鎖,去執(zhí)行方法test,但是發(fā)生這一切的基礎(chǔ)應(yīng)當(dāng)是所有線程使用的同一個對象實(shí)例,才能實(shí)現(xiàn)互斥的現(xiàn)象。否則synchronized關(guān)鍵字將失去意義。

      ( 但是如果該方法為類方法,即其修飾符為static,那么synchronized 意味著某個調(diào)用此方法的線程當(dāng)前會擁有該類的鎖,只要該線程持續(xù)在當(dāng)前方法內(nèi)運(yùn)行,其他線程依然無法獲得方法的使用權(quán)!)

      synchronized 用在代碼塊的使用方式:synchronized(obj){//todo code here}

      當(dāng)線程運(yùn)行到該代碼塊內(nèi),就會擁有obj對象的對象鎖,如果多個線程共享同一個Object對象,那么此時就會形成互斥!特別的,當(dāng)obj == this時,表示當(dāng)前調(diào)用該方法的實(shí)例對象。即

      public void test() {

      ...

      synchronized(this) {

      // todo your code

      synchronized 與 Lock 的那點(diǎn)事

      }

      ...

      }

      此時,其效果等同于

      public synchronized void test() {

      // todo?your code

      }

      使用synchronized代碼塊,可以只對需要同步的代碼進(jìn)行同步,這樣可以大大的提高效率。

      小結(jié):

      使用synchronized 代碼塊相比方法有兩點(diǎn)優(yōu)勢:

      1、可以只對需要同步的使用

      2、與wait()/notify()/nitifyAll()一起使用時,比較方便

      ----------------------------------------------------------------------------------------------------------------------------------------------------------

      wait() 與notify()/notifyAll()

      這三個方法都是Object的方法,并不是線程的方法!

      wait():釋放占有的對象鎖,線程進(jìn)入等待池,釋放cpu,而其他正在等待的線程即可搶占此鎖,獲得鎖的線程即可運(yùn)行程序。而sleep()不同的是,線程調(diào)用此方法后,會休眠一段時間,休眠期間,會暫時釋放cpu,但并不釋放對象鎖。也就是說,在休眠期間,其他線程依然無法進(jìn)入此代碼內(nèi)部。休眠結(jié)束,線程重新獲得cpu,執(zhí)行代碼。 wait()和sleep()最大的不同在于wait()會釋放對象鎖,而sleep()不會!

      notify(): 該方法會喚醒因?yàn)檎{(diào)用對象的wait()而等待的線程,其實(shí)就是 對對象鎖的喚醒,從而使得wait()的線程可以有機(jī)會獲取對象鎖。調(diào)用notify()后,并不會立即釋放鎖,而是繼續(xù)執(zhí)行當(dāng)前代碼,直到synchronized中的代碼全部執(zhí)行完畢,才會釋放對象鎖。JVM則會在等待的線程中調(diào)度一個線程去獲得對象鎖,執(zhí)行代碼。需要注意的是, wait()和notify()必須在synchronized代碼塊中調(diào)用。

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

      為了說明這一點(diǎn),舉例如下:

      兩個線程依次打印"A""B",總共打印10次。

      public? class? Consumer? implements? Runnable {

      @Override

      public? synchronized? void? run() {

      //? TODO? Auto-generated method stub

      int? count = 10;

      while (count > 0) {

      synchronized? (Test.? obj ) {

      System.? out .print(? "B" );

      count --;

      Test.? obj .notify();? // 主動釋放對象鎖

      try? {

      Test.? obj .wait();

      }? catch? (InterruptedException e) {

      //? TODO? Auto-generated catch block

      e.printStackTrace();

      }

      }

      }

      }

      }

      public? class? Produce? implements? Runnable {

      @Override

      public? void? run() {

      //? TODO? Auto-generated method stub

      int? count = 10;

      while (count > 0) {

      synchronized? (Test.? obj ) {

      //System.out.print("count = " + count);

      System.? out .print(? "A" );

      count --;

      Test.? obj .notify();

      try? {

      Test.? obj .wait();

      }? catch? (InterruptedException e) {

      //? TODO? Auto-generated catch block

      e.printStackTrace();

      }

      }

      }

      }

      }

      測試類如下:

      public? class? Test {

      public? static? final? Object? obj? =? new? Object();

      public? static? void? main(String[] args) {

      new? Thread(? new? Produce()).start();

      new? Thread(? new? Consumer()).start();

      }

      }

      這里使用static obj作為鎖的對象,當(dāng)線程Produce啟動時(假如Produce首先獲得鎖,則Consumer會等待),打印“A”后,會先主動釋放鎖,然后阻塞自己。Consumer獲得對象鎖,打印“B”,然后釋放鎖,阻塞自己,那么Produce又會獲得鎖,然后...一直循環(huán)下去,直到count = 0.這樣,使用Synchronized和wait()以及notify()就可以達(dá)到線程同步的目的。

      ----------------------------------------------------------------------------------------------------------------------------------------------------------

      除了wait()和notify()協(xié)作完成線程同步之外,使用Lock也可以完成同樣的目的。

      ReentrantLock 與synchronized有相同的并發(fā)性和內(nèi)存語義,還包含了中斷鎖等候和定時鎖等候,意味著線程A如果先獲得了對象obj的鎖,那么線程B可以在等待指定時間內(nèi)依然無法獲取鎖,那么就會自動放棄該鎖。

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

      同樣的例子,使用lock 如何實(shí)現(xiàn)呢?

      public? class? Consumer? implements? Runnable {

      private? Lock? lock ;

      public? Consumer(Lock lock) {

      this .? lock? = lock;

      }

      @Override

      public? void? run() {

      //? TODO? Auto-generated method stub

      int? count = 10;

      while ( count > 0 ) {

      try? {

      lock .lock();

      count --;

      System.? out .print(? "B" );

      }? finally? {

      lock .unlock(); //主動釋放鎖

      try? {

      Thread.?sleep(91L);

      }? catch? (InterruptedException e) {

      //? TODO? Auto-generated catch block

      e.printStackTrace();

      }

      }

      }

      }

      }

      public? class? Producer? implements? Runnable{

      private? Lock? lock ;

      public? Producer(Lock lock) {

      this .? lock? = lock;

      }

      @Override

      public? void? run() {

      //? TODO? Auto-generated method stub

      int? count = 10;

      while? (count > 0) {

      try? {

      lock .lock();

      count --;

      System.? out .print(? "A" );

      }? finally? {

      lock .unlock();

      try? {

      Thread.?sleep(90L);

      }? catch? (InterruptedException e) {

      //? TODO? Auto-generated catch block

      e.printStackTrace();

      }

      }

      }

      }

      }

      調(diào)用代碼:

      public? class? Test {

      public? static? void? main(String[] args) {

      Lock lock =? new? ReentrantLock();

      Consumer consumer =? new? Consumer(lock);

      Producer?producer?=? new? Producer(lock);

      new? Thread(consumer).start();

      new? Thread(?producer).start();

      }

      }

      使用建議:

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

      任務(wù)調(diào)度

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      版權(quán)聲明:本文內(nèi)容由網(wǎng)絡(luò)用戶投稿,版權(quán)歸原作者所有,本站不擁有其著作權(quán),亦不承擔(dān)相應(yīng)法律責(zé)任。如果您發(fā)現(xiàn)本站中有涉嫌抄襲或描述失實(shí)的內(nèi)容,請聯(lián)系我們jiasou666@gmail.com 處理,核實(shí)后本網(wǎng)站將在24小時內(nèi)刪除侵權(quán)內(nèi)容。

      上一篇:圖表甘特圖怎么用(甘特圖的用法)
      下一篇:apaas的服務(wù)范圍(apaas和saas)
      相關(guān)文章
      亚洲综合无码一区二区三区| 狠狠色婷婷狠狠狠亚洲综合| 亚洲人成无码网站在线观看| 亚洲精品无码久久毛片波多野吉衣| 国产亚洲一区二区三区在线观看| 国产91精品一区二区麻豆亚洲| www.亚洲精品| 亚洲国产精品无码久久久秋霞1 | 国产综合精品久久亚洲| 亚洲A∨午夜成人片精品网站| 蜜芽亚洲av无码一区二区三区| 亚洲欧美日韩国产成人| 亚洲AV性色在线观看| 国产精品自拍亚洲| 一本久久综合亚洲鲁鲁五月天| 国产精品亚洲五月天高清| 国产AV无码专区亚洲AV麻豆丫| 成人婷婷网色偷偷亚洲男人的天堂| 精品久久久久久久久亚洲偷窥女厕| 亚洲AV无码成人精品区日韩| 亚洲av无码一区二区三区四区| 苍井空亚洲精品AA片在线播放 | 亚洲国产精品第一区二区| 亚洲国产精品久久66| 亚洲一级二级三级不卡| 亚洲精品国产福利在线观看| 亚洲白色白色在线播放| 亚洲性猛交xx乱| 亚洲国产成人精品激情| 亚洲日韩国产二区无码| 精品亚洲成a人在线观看| 亚洲国产成人久久综合野外| 亚洲男人av香蕉爽爽爽爽| 亚洲精品无码av人在线观看| 亚洲日本中文字幕区| 亚洲成人福利网站| 亚洲一卡2卡三卡4卡无卡下载 | 久久久久亚洲av无码尤物| 亚洲精品电影在线| 亚洲最大福利视频| 99亚洲男女激情在线观看|