成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

java并發(fā)編程學(xué)習(xí)8--同步--ReentrantLock

bergwhite / 2031人閱讀

摘要:而問題往往就是有多個(gè)線程同時(shí)在執(zhí)行步驟。另一個(gè)線程有機(jī)會(huì)執(zhí)行轉(zhuǎn)賬操作,為當(dāng)前賬戶打錢。相反的,它處于阻塞狀態(tài),直到另一個(gè)線程調(diào)用同一條件的。喚醒所有處于該條件中的等待線程,這些線程將重新競(jìng)爭(zhēng)鎖。

【條件競(jìng)爭(zhēng)

在多線程的開發(fā)中,兩個(gè)及其以上的線程需要共享統(tǒng)一數(shù)據(jù)的存取。如果兩個(gè)線程存取相同的對(duì)象,并且每一個(gè)線程都調(diào)用一個(gè)修改該對(duì)象狀態(tài)的方法,根據(jù)線程訪問數(shù)據(jù)的順序,可能會(huì)出現(xiàn)錯(cuò)誤的數(shù)據(jù)結(jié)果,這種現(xiàn)象成為條件競(jìng)爭(zhēng)。因?yàn)樾薷膶?duì)象狀態(tài)的方法并不是一個(gè)原子操作,通常步驟是:

1. 讀取當(dāng)前狀態(tài)值到線程工作內(nèi)存。
2. 在線程工作內(nèi)存中修改狀態(tài)值。
3. 將修改后的狀態(tài)值重新寫入主內(nèi)存。

而問題往往就是有多個(gè)線程同時(shí)在執(zhí)行步驟2。

【有兩種機(jī)制代碼受并發(fā)訪問的干擾

synchronized關(guān)鍵字。

Reentrantlock類。

【Reentrantlock類

可重入的互斥鎖,又被稱為“獨(dú)占鎖”。Lock和synchronized機(jī)制的主要區(qū)別:

    1. synchronized機(jī)制提供了對(duì)與每個(gè)對(duì)象相關(guān)的隱式監(jiān)視器鎖的訪問, 并強(qiáng)制所有鎖獲取和釋放均要出現(xiàn)在一個(gè)塊結(jié)構(gòu)中, 當(dāng)獲取了多個(gè)鎖時(shí),它們必須以相反的順序釋放。
    2. synchronized機(jī)制對(duì)鎖的釋放是隱式的, 只要線程運(yùn)行的代碼超出了synchronized語句塊范圍, 鎖就會(huì)被釋放;而Lock機(jī)制必須顯式的調(diào)用Lock對(duì)象的unlock()方法才能釋放鎖, 這為獲取鎖和釋放鎖不出現(xiàn)在同一個(gè)塊結(jié)構(gòu)中, 以及以更自由的順序釋放鎖提供了可能。
    

public class LockObject {

    private ReentrantLock lock = new ReentrantLock();

    public void lockSet(int value){
        lock.lock();
        try{
            System.out.println("lock writing : " + value);
//            while (1==1){}
        }finally {
            lock.unlock();
        }
    }
    public void unLock(){
        System.out.println("unlock");
    }
}

如果我們把while(1==1){}放開的話,可以看見只有一個(gè)線程能進(jìn)入該方法中,說明鎖有效。

【讀寫鎖

不過有一個(gè)問題出現(xiàn)了,如果兩個(gè)線程有寫的操作,那么上鎖是沒有問題的。 但是如果都是讀的操作那么還用不用上鎖呢?應(yīng)該不用了,因?yàn)殒i是很消耗資源的,能不用就不用。基于這樣的考慮,jdk提供了讀寫鎖:

private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private Lock writeLock = readWriteLock.writeLock();
private Lock readLock = readWriteLock.readLock();

總結(jié)一些讀寫鎖最重要的特點(diǎn):

多個(gè)線程包含至少一個(gè)寫操作:鎖生效。

多個(gè)線程全部是讀操作:鎖不生效。

這樣一來,我們就把讀寫分離開來,在系統(tǒng)查詢是大部分操作,如果將讀寫分離出來,性能就會(huì)大大受益。 有人會(huì)問:讀操作本來就不需要加鎖啊,只在寫操作上用鎖就可以了。如果是這樣的話,試想:共享對(duì)象正在執(zhí)行寫操作,這時(shí)一個(gè)讀操作執(zhí)行了, 但是讀操作讀出來的數(shù)據(jù)與執(zhí)行完寫操作的數(shù)據(jù)不一致,這就會(huì)影響使用讀操作返回值的邏輯,這種現(xiàn)象就是:不可重復(fù)讀! 是的,數(shù)據(jù)庫中就使用了讀寫鎖的思想,并且劃分了事務(wù)的隔離級(jí)別。

public class LockObject2 {

    private ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private Lock writeLock = readWriteLock.writeLock();
    private Lock readLock = readWriteLock.readLock();
    //共享數(shù)據(jù)
    private int shareValue = 100;

    public void setLock(int value){
        writeLock.lock();
        try{
            System.out.println("writing : " + value);
            shareValue = value;
            while (1==1){

            }
        }finally {
            writeLock.unlock();
        }
    }

    public void getLock(){
        readLock.lock();
        try{
            System.out.println("getting : " + shareValue);
            while (1==1){

            }
        }finally {
            readLock.unlock();
        }
    }
}
【常用方法

void lock():獲得鎖,如果鎖同時(shí)被另一個(gè)線程持有則發(fā)生阻塞。

void unlock():釋放鎖,必須在finally{}中。

【構(gòu)造方法

ReentrantLock():構(gòu)建一個(gè)可以用來保護(hù)臨界區(qū)的可重入鎖對(duì)象。

ReentrantLock(boolean fair):構(gòu)建一個(gè)帶有公平策略的鎖。公平鎖偏愛等待時(shí)間最長的線程,但是會(huì)大大降低性能,所以默認(rèn)情況下,鎖不公平。 貌似公平鎖更加的合理,但是公平鎖會(huì)比常規(guī)鎖慢很多,只有當(dāng)你確定自己要做什么并且對(duì)于你要解決的問題有一個(gè)特定的理由時(shí),才使用公平鎖。并且就算是公平鎖也無法絕對(duì)的公平因?yàn)檫@和線程的調(diào)度器有關(guān)。

【條件對(duì)象

些時(shí)候,線程進(jìn)入臨界區(qū)之后,發(fā)現(xiàn)需要滿足一定的條件才可以執(zhí)行。要使用一個(gè)條件對(duì)象來管理那些已經(jīng)獲得鎖但是不能工作的線程。我們使用銀行轉(zhuǎn)賬的例子:

 private ReentrantLock bankLock = new ReentrantLock();        
             public void lockTrans(int to ,int amount){            
                   bankLock.lock();           
                   try{               
                         while (this.amount < amount){                    
                              //wait               
                         }            
                    }finally {               
                         bankLock.unlock();           
                    }
             }   

現(xiàn)在,賬戶沒有足夠金額的時(shí)候,只有等待另一個(gè)線程向賬戶中轉(zhuǎn)賬。但是這個(gè)線程已經(jīng)獲得了鎖,其他線程無法執(zhí)行轉(zhuǎn)賬操作啊。這就是為什么我們要使用條件對(duì)象的原因。 一個(gè)對(duì)象鎖,可以有多個(gè)相關(guān)的條件對(duì)象。

private ReentrantLock bankLock = new ReentrantLock();        
             private Condition sufficientFunds = bankLock.newCondition();        
             public void lockTrans(int to ,int amount) throws InterruptedException {           
                  bankLock.lock();           
                  try{               
                        while (this.amount < amount){                   
                             //wait                    
                             sufficientFunds.await();                
                         }            
                   }finally {               
                         bankLock.unlock();           
                   }       
              } 

現(xiàn)在當(dāng)前線程被阻塞了,并放棄了鎖。另一個(gè)線程有機(jī)會(huì)執(zhí)行轉(zhuǎn)賬操作,為當(dāng)前賬戶打錢。等待獲得鎖的線程與調(diào)用了await()的線程有本質(zhì)區(qū)別,一旦一個(gè)線程調(diào)用了await(),它就進(jìn)入該條件的等待集,當(dāng)鎖可以使用時(shí),它不能馬上解鎖。相反的,它處于阻塞狀態(tài),直到另一個(gè)線程調(diào)用同一條件的signalAll()。

signalAll():?jiǎn)拘阉刑幱谠摋l件中的等待線程,這些線程將重新競(jìng)爭(zhēng)鎖。

 private ReentrantLock bankLock = new ReentrantLock();            
                private Condition sufficientFunds = bankLock.newCondition();            
                public void lockTrans(int to ,int amount) throws InterruptedException {                
                       bankLock.lock();                
                       try{                   
                              while (this.amount < amount){                       
                              //wait                       
                              sufficientFunds.await();                        
                              //處理轉(zhuǎn)賬邏輯                        
                              sufficientFunds.signalAll();                    
                              }                
                       }finally {                   
                              bankLock.unlock();                
                       }           
                 }
【完整的轉(zhuǎn)賬代碼如下
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Bank {

    public static void main(String[] args) {
        Bank b = new Bank(5,100);
        Random random = new Random();
        List cfs = new ArrayList<>();
        for (int i = 0; i < b.size(); i++) {
            int from = i;
            int to = (int) (b.size() * Math.random());
            int max = 100;
            int min = 10;
            int amount = random.nextInt(max) % (max - min + 1) + min;
            cfs.add(CompletableFuture.runAsync(() -> {
                    try {
                        b.transfer(from, to, amount);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                })
            );
        }
        CompletableFuture.allOf(cfs.toArray(new CompletableFuture[cfs.size()])).join();
    }

    private final int[] accounts;
    private Lock bankLock;
    private Condition sufficientFunds;

    public Bank(int n,int initialBalance){
        accounts = new int[n];
        Arrays.fill(accounts,initialBalance);
        bankLock = new ReentrantLock();
        sufficientFunds = bankLock.newCondition();
    }

    //轉(zhuǎn)賬邏輯
    public void transfer(int from,int to,int amount) throws InterruptedException {
        bankLock.lock();
        try {
            while (accounts[from] < amount){
                sufficientFunds.await();
            }
            System.out.println(Thread.currentThread());
            accounts[from] -= amount;
            System.out.println("---------------------------------------");
            System.out.println(String.format("%d from %d to %d",amount,from,to));
            accounts[to] += amount;
            System.out.println(String.format("Total balance:%d",getTotalBalance()));
            System.out.println("Detail:");
            for (int i = 0; i < accounts.length; i++) {
                System.out.println(String.format("account[%d]"s amount is : %d",i,accounts[i]));
            }
            System.out.println("---------------------------------------");
            sufficientFunds.signalAll();
        }finally {
            bankLock.unlock();
        }
    }
    public int getTotalBalance(){
        bankLock.lock();
        try{
            int sum = 0;
            for(int a : accounts){
                sum += a;
            }
            return sum;
        }finally {
            bankLock.unlock();
        }
    }

    public int size(){
        return accounts.length;
    }
}

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/68092.html

相關(guān)文章

  • java并發(fā)編程學(xué)習(xí)13--Atomic數(shù)據(jù)結(jié)構(gòu)簡(jiǎn)介

    摘要:介紹中無鎖的線程安全整數(shù),一個(gè)提供原子操作的的類。在語言中,和操作并不是線程安全的,在使用的時(shí)候,不可避免的會(huì)用到關(guān)鍵字。而則通過一種線程安全的加減操作接口。就是的意思,比較并操作。有個(gè)操作數(shù),內(nèi)存值,舊的預(yù)期值,要修改的新值。 【介紹 JAVA 中無鎖的線程安全整數(shù) AtomicInteger,一個(gè)提供原子操作的Integer的類。在Java語言中,++i和i++操作并不是線程安全的...

    李增田 評(píng)論0 收藏0
  • Java并發(fā)編程,深入理解ReentrantLock

    摘要:公平鎖為了保證時(shí)間上的絕對(duì)順序,需要頻繁的上下文切換,而非公平鎖會(huì)降低一定的上下文切換,降低性能開銷。因此,默認(rèn)選擇的是非公平鎖,則是為了減少一部分上下文切換,保證了系統(tǒng)更大的吞吐量。ReentrantLock簡(jiǎn)介ReentrantLock重入鎖,是實(shí)現(xiàn)Lock接口的一個(gè)類,也是在實(shí)際編程中使用頻率很高的一個(gè)鎖, 支持重入性,表示能夠?qū)蚕碣Y源能夠重復(fù)加鎖,即當(dāng)前線程獲取該鎖再次獲取不會(huì)被阻...

    番茄西紅柿 評(píng)論0 收藏0
  • Java并發(fā)編程,深入理解ReentrantLock

    摘要:公平鎖為了保證時(shí)間上的絕對(duì)順序,需要頻繁的上下文切換,而非公平鎖會(huì)降低一定的上下文切換,降低性能開銷。因此,默認(rèn)選擇的是非公平鎖,則是為了減少一部分上下文切換,保證了系統(tǒng)更大的吞吐量。ReentrantLock簡(jiǎn)介ReentrantLock重入鎖,是實(shí)現(xiàn)Lock接口的一個(gè)類,也是在實(shí)際編程中使用頻率很高的一個(gè)鎖, 支持重入性,表示能夠?qū)蚕碣Y源能夠重復(fù)加鎖,即當(dāng)前線程獲取該鎖再次獲取不會(huì)被阻...

    番茄西紅柿 評(píng)論0 收藏0
  • Java并發(fā)編程,深入理解ReentrantLock

    摘要:公平鎖為了保證時(shí)間上的絕對(duì)順序,需要頻繁的上下文切換,而非公平鎖會(huì)降低一定的上下文切換,降低性能開銷。因此,默認(rèn)選擇的是非公平鎖,則是為了減少一部分上下文切換,保證了系統(tǒng)更大的吞吐量。ReentrantLock簡(jiǎn)介ReentrantLock重入鎖,是實(shí)現(xiàn)Lock接口的一個(gè)類,也是在實(shí)際編程中使用頻率很高的一個(gè)鎖, 支持重入性,表示能夠?qū)蚕碣Y源能夠重復(fù)加鎖,即當(dāng)前線程獲取該鎖再次獲取不會(huì)被阻...

    fredshare 評(píng)論0 收藏0
  • JAVA并發(fā)編程之-ReentrantLock鎖原理解讀

    摘要:作者畢來生微信鎖狀態(tài)轉(zhuǎn)換分類以后幫助我們提供了線程同步機(jī)制,通過顯示定義同步鎖來實(shí)現(xiàn)對(duì)象之間的同步。等待重新嘗試因?yàn)樵谥惺怯藐P(guān)鍵字聲明的,故可以在線程間可見再次判斷一下能否持有鎖可能線程同步代碼執(zhí)行得比較快,已經(jīng)釋放了鎖,不可以就返回。 作者 : 畢來生微信: 878799579 鎖狀態(tài)轉(zhuǎn)換 showImg(https://segmentfault.com/img/remote/...

    荊兆峰 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

bergwhite

|高級(jí)講師

TA的文章

閱讀更多
最新活動(dòng)
閱讀需要支付1元查看
<