<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      安卓筆記俠

      專注安卓開發(fā)

      導航

      Java并發(fā)編程(二)同步

        在多線程的應用中,兩個或者兩個以上的線程需要共享對同一個數(shù)據(jù)的存取。如果兩個線程存取相同的對象,并且每一個線程都調(diào)用了修改該對象的方法,這種情況通常成為競爭條件。 
        競爭條件最容易理解的例子就是:比如火車賣票,火車票是一定的,但賣火車票的窗口到處都有,每個窗口就相當于一個線程,這么多的線程共用所有的火車票這個資源。并且無法保證其原子性,如果在一個時間點上,兩個線程同時使用這個資源,那他們?nèi)〕龅幕疖嚻笔且粯拥模ㄗ惶栆粯樱@樣就會給乘客造成麻煩。解決方法為,當一個線程要使用火車票這個資源時,我們就交給它一把鎖,等它把事情做完后在把鎖給另一個要用這個資源的線程。這樣就不會出現(xiàn)上述情況。

      1. 鎖對象 
      synchronized關鍵字自動提供了鎖以及相關的條件,大多數(shù)需要顯式鎖的情況使用synchronized非常的方便,但是等我們了解ReentrantLock類和條件對象時,我們能更好的理解synchronized關鍵字。ReentrantLock是JAVA SE 5.0引入的, 用ReentrantLock保護代碼塊的結(jié)構(gòu)如下:

      mLock.lock();
      try{
             ...
      }
      finally{
          mLock.unlock();
      }    

      這一結(jié)構(gòu)確保任何時刻只有一個線程進入臨界區(qū),一旦一個線程封鎖了鎖對象,其他任何線程都無法通過lock語句。當其他線程調(diào)用lock時,它們則被阻塞直到第一個線程釋放鎖對象。把解鎖的操作放在finally中是十分必要的,如果在臨界區(qū)發(fā)生了異常,鎖是必須要釋放的,否則其他線程將會永遠阻塞。

      2. 條件對象 
      進入臨界區(qū)時,卻發(fā)現(xiàn)在某一個條件滿足之后,它才能執(zhí)行。要使用一個條件對象來管理那些已經(jīng)獲得了一個鎖但是卻不能做有用工作的線程,條件對象又稱作條件變量。 
      我們來看看下面的例子來看看為何需要條件對象

      假設一個場景我們需要用銀行轉(zhuǎn)賬,我們首先寫了銀行的類,它的構(gòu)造函數(shù)需要傳入賬戶數(shù)量和賬戶金額

      public class Bank {
          private double[] accounts;
          private Lock bankLock;
          public Bank(int n,double initialBalance){
              accounts=new double[n];
              bankLock=new ReentrantLock();
              for (int i=0;i<accounts.length;i++){
                  accounts[i]=initialBalance;
              }
          }
      }

      接下來我們要提款,寫一個提款的方法,from是轉(zhuǎn)賬方,to是接收方,amount轉(zhuǎn)賬金額,結(jié)果我們發(fā)現(xiàn)轉(zhuǎn)賬方余額不足,如果有其他線程給這個轉(zhuǎn)賬方再存足夠的錢就可以轉(zhuǎn)賬成功了,但是這個線程已經(jīng)獲取了鎖,它具有排他性,別的線程也無法獲取鎖來進行存款操作,這就是我們需要引入條件對象的原因。

      public void transfer(int from,int to,int amount){
              bankLock.lock();
              try{
                  while (accounts[from]<amount){
                      //wait
                  }
              }finally {
                  bankLock.unlock();
              }
      }

      一個鎖對象擁有多個相關的條件對象,可以用newCondition方法獲得一個條件對象,我們得到條件對象后調(diào)用await方法,當前線程就被阻塞了并放棄了鎖

      public class Bank {
          private double[] accounts;
          private Lock bankLock;
          private Condition condition;
          public Bank(int n,double initialBalance){
              accounts=new double[n];
              bankLock=new ReentrantLock();
              //得到條件對象
              condition=bankLock.newCondition();
              for (int i=0;i<accounts.length;i++){
                  accounts[i]=initialBalance;
              }
          }
          public void transfer(int from,int to,int amount) throws InterruptedException {
              bankLock.lock();
              try{
                  while (accounts[from]<amount){
                      //阻塞當前線程,并放棄鎖
                      condition.await();
                  }
              }finally {
                  bankLock.unlock();
              }
          }
      }

      等待獲得鎖的線程和調(diào)用await方法的線程本質(zhì)上是不同的,一旦一個線程調(diào)用的await方法,他就會進入該條件的等待集。當鎖可用時,該線程不能馬上解鎖,相反他處于阻塞狀態(tài),直到另一個線程調(diào)用了同一個條件上的signalAll方法時為止。當另一個線程準備轉(zhuǎn)賬給我們此前的轉(zhuǎn)賬方時,只要調(diào)用condition.signalAll();該調(diào)用會重新激活因為這一條件而等待的所有線程。 
      當一個線程調(diào)用了await方法他沒法重新激活自身,并寄希望于其他線程來調(diào)用signalAll方法來激活自身,如果沒有其他線程來激活等待的線程,那么就會產(chǎn)生死鎖現(xiàn)象,如果所有的其他線程都被阻塞,最后一個活動線程在解除其他線程阻塞狀態(tài)前調(diào)用await,那么它也被阻塞,就沒有任何線程可以解除其他線程的阻塞,程序就被掛起了。 
      那何時調(diào)用signalAll呢?正常來說應該是有利于等待線程的方向改變時來調(diào)用signalAll。在這個例子里就是,當一個賬戶余額發(fā)生變化時,等待的線程應該有機會檢查余額。

      public void transfer(int from,int to,int amount) throws InterruptedException {
              bankLock.lock();
              try{
                  while (accounts[from]<amount){
                      //阻塞當前線程,并放棄鎖
                      condition.await();
                  }
                  //轉(zhuǎn)賬的操作
                  ...
                  condition.signalAll();
              }finally {
                  bankLock.unlock();
              }
      }

      當調(diào)用signalAll方法時并不是立即激活一個等待線程,它僅僅解除了等待線程的阻塞,以便這些線程能夠在當前線程退出同步方法后,通過競爭實現(xiàn)對對象的訪問。還有一個方法是signal,它則是隨機解除某個線程的阻塞,如果該線程仍然不能運行,那么則再次被阻塞,如果沒有其他線程再次調(diào)用signal,那么系統(tǒng)就死鎖了。

      3. Synchronized關鍵字 
      Lock和Condition接口為程序設計人員提供了高度的鎖定控制,然而大多數(shù)情況下,并不需要那樣的控制,并且可以使用一種嵌入到java語言內(nèi)部的機制。從Java1.0版開始,Java中的每一個對象都有一個內(nèi)部鎖。如果一個方法用synchronized關鍵字聲明,那么對象的鎖將保護整個方法。也就是說,要調(diào)用該方法,線程必須獲得內(nèi)部的對象鎖。 
      換句話說,

      public synchronized void method(){
      
      }

      等價于

      public void method(){
          this.lock.lock();
          try{
      
          }finally{
          this.lock.unlock();
      }

      上面銀行的例子,我們可以將Bank類的transfer方法聲明為synchronized,而不是使用一個顯示的鎖。 
      內(nèi)部對象鎖只有一個相關條件,wait方法添加到一個線程到等待集中,notifyAll或者notify方法解除等待線程的阻塞狀態(tài)。也就是說wait相當于調(diào)用condition.await(),notifyAll等價于condition.signalAll();

      我們上面的例子transfer方法也可以這樣寫:

       public synchronized void transfer(int from,int to,int amount)throws InterruptedException{
              while (accounts[from]<amount) {
                  wait();
              }
              //轉(zhuǎn)賬的操作
              ...
              notifyAll();   
      }

      可以看到使用synchronized關鍵字來編寫代碼要簡潔很多,當然要理解這一代碼,你必須要了解每一個對象有一個內(nèi)部鎖,并且該鎖有一個內(nèi)部條件。由鎖來管理那些試圖進入synchronized方法的線程,由條件來管理那些調(diào)用wait的線程。

      4. 同步阻塞 
      上面我們說過,每一個Java對象都有一個鎖,線程可以調(diào)用同步方法來獲得鎖,還有另一種機制可以獲得鎖,通過進入一個同步阻塞,當線程進入如下形式的阻塞:

      synchronized(obj){
      
      }

      于是他獲得了obj的鎖。再來看看Bank類

      public class Bank {
      private double[] accounts;
      private Object lock=new Object();
         public Bank(int n,double initialBalance){
              accounts=new double[n];
              for (int i=0;i<accounts.length;i++){
                  accounts[i]=initialBalance;
              }
          }
          public void transfer(int from,int to,int amount){
              synchronized(lock){
                //轉(zhuǎn)賬的操作
                  ...
              }
          }
      }

      在此,lock對象創(chuàng)建僅僅是用來使用每個Java對象持有的鎖。有時開發(fā)人員使用一個對象的鎖來實現(xiàn)額外的原子操作,稱為客戶端鎖定。例如Vector類,它的方法是同步的。現(xiàn)在假設在Vector中存儲銀行余額

      public void transfer(Vector<Double>accounts,int from,int to,int amount){
        accounts.set(from,accounts.get(from)-amount);
        accounts.set(to,accounts.get(to)+amount;
      }

      Vecror類的get和set方法是同步的,但是這并未對我們有所幫助。在第一次對get調(diào)用完成以后,一個線程完全可能在transfer方法中被被剝奪運行權,于是另一個線程可能在相同的存儲位置存入了不同的值,但是,我們可以截獲這個鎖

      public void transfer(Vector<Double>accounts,int from,int to,int amount){
        synchronized(accounts){
            accounts.set(from,accounts.get(from)-amount);
            accounts.set(to,accounts.get(to)+amount;
        }
      }

      客戶端鎖定(同步代碼塊)是非常脆弱的,通常不推薦使用,一般實現(xiàn)同步最好用java.util.concurrent包下提供的類,比如阻塞隊列。如果同步方法適合你的程序,那么請盡量的使用同步方法,他可以減少編寫代碼的數(shù)量,減少出錯的幾率,如果特別需要使用Lock/Condition結(jié)構(gòu)提供的獨有特性時,才使用Lock/Condition。

      posted on 2016-12-04 16:39  安卓筆記俠  閱讀(490)  評論(0)    收藏  舉報

      主站蜘蛛池模板: 国产亚洲精品VA片在线播放| 亚洲高清日韩专区精品| 色综合久久中文综合久久激情 | 亚洲va久久久噜噜噜久久狠狠| 精品国产精品三级精品av网址| 91亚洲国产三上悠亚在线播放| 日韩有码中文字幕av| 亚洲av日韩av一区久久| 99国产精品永久免费视频| 长泰县| 日本真人添下面视频免费| 99热久久这里只有精品| 99精品国产在热久久婷婷| 日韩国产精品无码一区二区三区| 人妻精品动漫H无码中字| 一本一道av无码中文字幕麻豆| 国产成人久久综合第一区| 国产精品成人国产乱| 中文字幕在线精品人妻| 久久精品国产亚洲av亚| 国产三级视频网站| 起碰免费公开97在线视频| 青青草国产精品日韩欧美| 欧洲中文字幕一区二区| 海丰县| 国产成人高清亚洲综合| 无码精品人妻一区二区三区中| 性做久久久久久久久| 国产欧美日韩另类精彩视频| 中文字幕乱码中文乱码毛片| 国产福利片一区二区三区| 国产乱啊有帅gv小太正| 久久99精品久久久久麻豆| 国产精品日韩中文字幕熟女| 日韩有码中文在线观看| 国产在线无遮挡免费观看| 欧美啪啪网| 亚洲精品日韩精品久久| 国产精品视频全国免费观看| 中文字幕乱码一区二区免费| 国产亚洲精品成人av久|