最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

學(xué)習(xí)Java多線(xiàn)程之同步

 更新時(shí)間:2016年02月29日 10:17:30   作者:劉望舒  
這篇文章主要為大家詳細(xì)介紹了Java多線(xiàn)程之同步,感興趣的小伙伴們可以參考一下

如果你的java基礎(chǔ)較弱,或者不大了解java多線(xiàn)程請(qǐng)先看這篇文章《學(xué)習(xí)Java多線(xiàn)程之線(xiàn)程定義、狀態(tài)和屬性》

同步一直是java多線(xiàn)程的難點(diǎn),在我們做android開(kāi)發(fā)時(shí)也很少應(yīng)用,但這并不是我們不熟悉同步的理由。希望這篇文章能使更多的人能夠了解并且應(yīng)用java的同步。
在多線(xiàn)程的應(yīng)用中,兩個(gè)或者兩個(gè)以上的線(xiàn)程需要共享對(duì)同一個(gè)數(shù)據(jù)的存取。如果兩個(gè)線(xiàn)程存取相同的對(duì)象,并且每一個(gè)線(xiàn)程都調(diào)用了修改該對(duì)象的方法,這種情況通常成為競(jìng)爭(zhēng)條件。
競(jìng)爭(zhēng)條件最容易理解的例子就是:比如火車(chē)賣(mài)票,火車(chē)票是一定的,但賣(mài)火車(chē)票的窗口到處都有,每個(gè)窗口就相當(dāng)于一個(gè)線(xiàn)程,這么多的線(xiàn)程共用所有的火車(chē)票這個(gè)資源。并且無(wú)法保證其原子性,如果在一個(gè)時(shí)間點(diǎn)上,兩個(gè)線(xiàn)程同時(shí)使用這個(gè)資源,那他們?nèi)〕龅幕疖?chē)票是一樣的(座位號(hào)一樣),這樣就會(huì)給乘客造成麻煩。解決方法為,當(dāng)一個(gè)線(xiàn)程要使用火車(chē)票這個(gè)資源時(shí),我們就交給它一把鎖,等它把事情做完后在把鎖給另一個(gè)要用這個(gè)資源的線(xiàn)程。這樣就不會(huì)出現(xiàn)上述情況。

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

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

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

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

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

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;
    }
  }
  }

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

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

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

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();
    //得到條件對(duì)象
    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){
        //阻塞當(dāng)前線(xiàn)程,并放棄鎖
        condition.await();
      }
    }finally {
      bankLock.unlock();
    }
  }
}

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

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

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

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

public synchronized void method(){

}

等價(jià)于

public void method(){
this.lock.lock();
try{

}finally{
this.lock.unlock();
}

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

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

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

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

4. 同步阻塞
上面我們說(shuō)過(guò),每一個(gè)Java對(duì)象都有一個(gè)鎖,線(xiàn)程可以調(diào)用同步方法來(lái)獲得鎖,還有另一種機(jī)制可以獲得鎖,通過(guò)進(jìn)入一個(gè)同步阻塞,當(dāng)線(xiàn)程進(jìn)入如下形式的阻塞:

synchronized(obj){

}

于是他獲得了obj的鎖。再來(lái)看看Bank類(lèi)

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對(duì)象創(chuàng)建僅僅是用來(lái)使用每個(gè)Java對(duì)象持有的鎖。有時(shí)開(kāi)發(fā)人員使用一個(gè)對(duì)象的鎖來(lái)實(shí)現(xiàn)額外的原子操作,稱(chēng)為客戶(hù)端鎖定。例如Vector類(lèi),它的方法是同步的?,F(xiàn)在假設(shè)在Vector中存儲(chǔ)銀行余額

 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類(lèi)的get和set方法是同步的,但是這并未對(duì)我們有所幫助。在第一次對(duì)get調(diào)用完成以后,一個(gè)線(xiàn)程完全可能在transfer方法中被被剝奪運(yùn)行權(quán),于是另一個(gè)線(xiàn)程可能在相同的存儲(chǔ)位置存入了不同的值,但是,我們可以截獲這個(gè)鎖

 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;
 }
}

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

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助。

相關(guān)文章

  • springboot如何獲取yml文件的自定義參數(shù)

    springboot如何獲取yml文件的自定義參數(shù)

    這篇文章主要介紹了springboot如何獲取yml文件的自定義參數(shù),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • java Date裝成英文String后,無(wú)法再轉(zhuǎn)回Date的解決方案

    java Date裝成英文String后,無(wú)法再轉(zhuǎn)回Date的解決方案

    本文介紹了java Date裝成英文String后,無(wú)法再轉(zhuǎn)回Date的解決方案。具有一定的參考價(jià)值,下面跟著小編一起來(lái)看下吧
    2017-01-01
  • Java中@Async異步失效的9種場(chǎng)景

    Java中@Async異步失效的9種場(chǎng)景

    最近在項(xiàng)目某個(gè)方法使用@Async注解,但是該方法還是同步執(zhí)行了,異步不起作用,到底是什么原因呢?接下來(lái)本文給大家介紹了Java?@Async異步失效的9種場(chǎng)景,需要的朋友可以參考下
    2024-05-05
  • Java基于正則表達(dá)式實(shí)現(xiàn)xml文件的解析功能詳解

    Java基于正則表達(dá)式實(shí)現(xiàn)xml文件的解析功能詳解

    這篇文章主要介紹了Java基于正則表達(dá)式實(shí)現(xiàn)xml文件的解析功能,結(jié)合實(shí)例形式分析了java使用正則表達(dá)式針對(duì)xml文件節(jié)點(diǎn)的相關(guān)操作技巧,需要的朋友可以參考下
    2017-08-08
  • 基于Java 談回調(diào)函數(shù)

    基于Java 談回調(diào)函數(shù)

    回調(diào)函數(shù)就是一個(gè)通過(guò)函數(shù)指針調(diào)用的函數(shù)。如果你把函數(shù)的指針(地址)作為參數(shù)傳遞給另一個(gè)函數(shù),當(dāng)這個(gè)指針被用來(lái)調(diào)用其所 指向的函數(shù)時(shí),我們就說(shuō)這是回調(diào)函數(shù)
    2017-05-05
  • Spring中@PathVariable注解的簡(jiǎn)單使用

    Spring中@PathVariable注解的簡(jiǎn)單使用

    這篇文章主要介紹了Spring中@PathVariable注解的簡(jiǎn)單使用,@PathVariable 是 Spring Framework 中的注解之一,用于處理 RESTful Web 服務(wù)中的 URL 路徑參數(shù),它的作用是將 URL 中的路徑變量綁定到方法的參數(shù)上,需要的朋友可以參考下
    2024-01-01
  • Springcloud Config支持本地配置文件的方法示例

    Springcloud Config支持本地配置文件的方法示例

    這篇文章主要介紹了Springcloud Config支持本地配置文件的方法示例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 淺談Spring事務(wù)傳播行為實(shí)戰(zhàn)

    淺談Spring事務(wù)傳播行為實(shí)戰(zhàn)

    這篇文章主要介紹了淺談Spring事務(wù)傳播行為實(shí)戰(zhàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Hibernate批量處理海量數(shù)據(jù)的方法

    Hibernate批量處理海量數(shù)據(jù)的方法

    這篇文章主要介紹了Hibernate批量處理海量數(shù)據(jù)的方法,較為詳細(xì)的分析了Hibernate批量處理海量數(shù)據(jù)的原理與相關(guān)實(shí)現(xiàn)技巧,需要的朋友可以參考下
    2016-03-03
  • Java變量命名規(guī)則詳解及常見(jiàn)命名錯(cuò)誤(建議收藏)

    Java變量命名規(guī)則詳解及常見(jiàn)命名錯(cuò)誤(建議收藏)

    這篇文章主要介紹了Java中變量命名的規(guī)則及最佳實(shí)踐,包括有效字符、大小寫(xiě)敏感性、不能使用保留字、駝峰命名法、描述性命名、特定類(lèi)型的命名習(xí)慣、避免潛在問(wèn)題、常見(jiàn)命名錯(cuò)誤及如何避免等內(nèi)容,文中通過(guò)代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-02-02

最新評(píng)論

英吉沙县| 波密县| 雷波县| 福安市| 高雄市| 仙桃市| 藁城市| 象山县| 宝鸡市| 武清区| 山西省| 广安市| 武清区| 辽阳市| 九台市| 高青县| 盐源县| 平塘县| 贵港市| 凤翔县| 武山县| 任丘市| 磐石市| 裕民县| 惠东县| 高要市| 钟祥市| 郴州市| 岳阳市| 延吉市| 武威市| 桂阳县| 齐齐哈尔市| 乐业县| 信阳市| 漳州市| 平顺县| 湖南省| 古浪县| 昌江| 望城县|