国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

并發學習筆記(2)

saucxs / 1365人閱讀

摘要:當其他線程調用時,它們被阻塞,直到第一個線程釋放鎖對象。包關于獲取這個鎖如果鎖同時被另一個線程擁有則發生阻塞。所以必須確保沒有其他線程再檢查余額和轉賬活動之間修改金額。方法添加一個線程到等待集中,方法解除等待線程的阻塞狀態。

避免代碼塊受到并發訪問的干擾

java語言提供了兩種機制實現這種功能

Synchonized 關鍵字(調用對象內部的鎖)
synchronized關鍵字自動提供一個鎖以及相關的條件

引入了ReentrantLock類。(顯示鎖)

更好: JUC框架為這些基礎機制提供了獨立的類: 線程池,或者高級一點專門做并發的工具的支持

ReentrantLock類 - 鎖 Lock 與synchronized 區別

Lock 不是Java語言內置(compared to synchronized),Lock是一個類,通過這個類可以實現同步訪問;

Lock 和 synchronized有一點非常大的不同,采用synchronized不需要用戶去手動釋放鎖,當synchronized方法或者synchronized代碼塊執行完之后,系統會自動讓現場釋放對鎖的占用,而Lock必須要用戶手動釋放,如果沒有主動釋放鎖,將會產生死鎖。 + Lock優缺點

Lock優缺點(compared to Synchronized)

Lock 能完成synchronized所實現的所有功能,而且比synchronized更好的性能。而且沒有synchronized簡潔。
但是:
1. 如果希望當獲取鎖時,有一個等待時間,不會無限期等待下去。
2. 希望當獲取不到鎖時,能夠響應中斷
3. 當讀多,寫少的應用時,希望提高性能
4. 獲取不到鎖時,立即返回 false。獲取到鎖時返回 true。

用ReentrantLock 保護代碼塊的基本結構如下:

myLock.lock();
try{
   critical section
}
finally{
    myLock.unlock(); // 把解鎖語句放在finally子句內是至關重要的。如果在臨界區的代碼拋異常,鎖必須被釋放。否則,其他線程將永遠阻塞。
}

用鎖來保護Bank類的transfer方法

public class Bank
{
    private Lock bankLock = new ReentrantLock();
    public void transfer(int from, int to, int amount){
        bankLock.lock();
        try{
            accounts[from] -= amount;
            account[to] += amount; 
        }
        finally{
            bankLock.unlock();
        }
    }
}

這個結構確保任何時刻只有一個線程進入臨界區,一旦一個線程封鎖了鎖對象,其他任何線程都無法通過lock語句。當其他線程調用lock時,它們被阻塞,直到第一個線程釋放鎖對象

JUC包關于Lock
java.util.concurrent.locks.Lock
- void lock()
獲取這個鎖;如果鎖同時被另一個線程擁有則發生阻塞。
- void unlock()
釋放這個鎖
java.util.concurrent.locks.ReentrantLock
- ReentrantLock()
構建一個可以被用來保護臨界區的可重入鎖
- ReentrantLock(boolean fair)
構建一個帶有公平策略的鎖。一個公平鎖偏愛等待時間最長的鎖,但是公平的保證會導致大大降低性能。

條件對象

通常線程進入臨界區,卻發現在某一條件滿足之后它才能執行。要使用一個條件對象來管理那些已經獲得了一個鎖但是不能做擁有工作的線程。
比如銀行的模擬程序。我們避免沒有足夠資金的賬戶作為轉出賬戶. 如下的代碼是不可以的,代碼有可能在transfer方法之前被中斷,在線程在此運行前,賬戶余額可能已經低于提款金額了。

java if(bank.getBalance(from) >= amount)
    // thread might be deactivated at this point
    bank.transfer(from,to,amount);

所以必須確保沒有其他線程再檢查余額和轉賬活動之間修改金額。通過鎖來保護檢查與轉賬動作的原子性,來做到這一點:

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

當賬戶沒有足夠的余額的時候,應該做什么?當前線程陷入wait until 另一個線程向賬戶注入了資金。但是鎖的排他性導致其他線程沒有進行存款操作的機會。這就是為什么需要調節對象的原因。

一個鎖對象可以有一個或者多個相關的條件對象。可以用newCondition方法獲得一個條件對象。

java class Bank{
    private Condition sufficientFunds;
    public Bank(){
        sufficientFunds = bankLock.newCondition();
    }
 }

如果transfer方法發現余額不足,就可以調用sufficientFunds.await() 當前線程被阻塞,并放棄了鎖;一旦一個線程調用了await(),它進入了該條件的等待集(進入等待狀態)。當鎖可用時,該線程不能馬上解除阻塞,相反,它仍然處于阻塞狀態,也就是自己不能激活自己,需要另一個線程調用同一條件的signalAll方法為止。而signalAll方法僅僅是通知正在等待的線程:此時有可能已經滿足條件,值得再次去檢查該條件。
所以正確的代碼是:

javapublic void transfer(int from, int to, int amount){
    backLock.lock();
    try{
        while(accounts[from] < amount)
           sufficientFunds.await();
        // transfer funds;
        ...
       sufficientFunds.signalAll();
    }
    finally{
        bankLock.unlock();
    }
}

Condition newCondition()
返回一個與該鎖相關的條件對象。
java.util.concurrent.locks.Condition

void await()
將該線程放到條件的等待集中

void signalAll()
解除該條件的等待集中的所有線程的阻塞狀態

void signal()
從該條件的等待集中隨機地選擇一個線程,解除其阻塞狀態

總結一下有關鎖(外部鎖)和條件的關鍵之處:
- 鎖用來保護代碼片段,任何時刻只能有一個線程執行被保護的代碼
- 鎖可以管理試圖進入被保護代碼段的線程
- 鎖可以擁有一個或多個相關的條件對象
- 每個條件對象管理那些已經進入被保護代碼段但還不能運行的先。

Java Synchronized keywords

synchronized 是java的關鍵字,也就是說是java語言內置的特性, 是托管給JVM執行的。
java的每一個對象都有一個內部鎖。如果一個方法用synchronized聲明,那么對象的鎖將保護整個方法。namely,要調用該方法線程必須獲得內部的對象鎖。通過使用synchonized 塊可以避免競爭條件;synchonized 修飾的同步代碼塊確保了一次只能一個線程執行同步的代碼塊。所有其它試圖進入同步塊的線程都會阻塞,直到同步塊里面的線程退出這個塊。

用Synchronized保護代碼塊的基本結構如下:

public synchronized void method(){
...
}

synchronized鎖定的是調用這個同步方法的對象。 namely 當一個對象P1在不同的線程中執行這個同步方法時,不同的線程會形成互斥,達到同步的效果。但是這個對象所屬的類的另一個對象P2卻能調用這個被加了synchonized的方法。
上述代碼等同于

public void method(){
synchronized(this){...}
}

this 指的是調用這個方法的對象,可見同步方法實質上是將synchronized作用于object reference -- 拿到了P1對象鎖的線程,才能調用調用P1的同步方法。

javaclass Bank{
    private double[] accounts;
    public synchronized void tranfer(int from, int to, int amount) throws InterruptedException{
        while(accounts[from] < amount)
            wait();// wait on intrinsic object lock"s single condition
        accounts[from] -= amount;
        accounts[to] += amount;
        notifyAll(); // notify all threads waiting on the condition
    }
}

可以看到synchronized關鍵字來編寫代碼要簡潔的多。要理解這一代碼,再一次重申:每一個對象有一個內部鎖,并且該鎖有一個內部條件。 由內部鎖來管理那些試圖進入synchronized方法的線程,由條件來管理那些調用wait的線程
java的 synchronized 關鍵字能夠作為函數的修飾符,也可作為函數內的語句,也就是平時說的同步方法和同步語句塊.

而無論 synchronized 關鍵字是加在了方法上還是對象上,他取得的鎖都是對象,而不是把一段代碼或者函數當做鎖; 每個對象只有一個鎖(lock)與之關聯。 實現同步是要很大的系統開銷作為代價的,甚至可能造成死鎖,所以要盡量不免無謂的同步控制。

相關條件

內部對象鎖只有一個相關條件。wait方法添加一個線程到等待集中,notifyAll/notify方法解除等待線程的阻塞狀態。調用wait or notifyall等價于

intrinsicCondition.await()
intrinsicCondition.signalAll()
    public synchronized void transfer(int from, int to, double amount) throws InterruptedException{
        while(accounts[from] < amount){
            wait();
        }
        System.out.print(Thread.currentThread());
        accounts[from] -= amount;
        accounts[to] += amount;
        notifyAll();
    }

java.lang.Object
- void notifyAll()
解除那些在該對象上調用wait方法的線程的阻塞狀態
java.util.concurrent.locks.Condition
- void await()
將該線程放到條件的等待集中
- void signalAll()
解除該條件的等待集中的所有線程的阻塞狀態
- void signal()
從該條件的等待集中隨機地選擇一個線程,解除其阻塞狀態

synchorinzed(缺陷)
- 不能中斷一個正在試圖獲得鎖的線程; 

如果一個代碼塊被synchronized修飾了,當一個線程獲取了對應的鎖,并執行該代碼塊時,其他線程便只能一直等待,等待獲取鎖的線程釋放鎖,而這里獲取鎖的線程釋放鎖只會有兩種情況:

  1)獲取鎖的線程執行完了該代碼塊,然后線程釋放對鎖的占有;
  2)線程執行發生異常,此時 JVM 會讓線程自動釋放鎖。

  那么如果這個獲取鎖的線程由于要等待 IO 或者其他原因(比如調用sleep方法)被阻塞了,但是又沒有釋放鎖,其他線程便只能干巴巴地等待,試想一下,這多么影響程序執行效率。

- 試圖獲得鎖時不能設定超時;

- 每個鎖僅有單一的條件,可能不夠的;
總結

在代碼中應該使用哪一種呢? Lock 和 Condition對象還是同步方法?
下面是Core java的一些建議:

最好既不是用Lock/Condition 也不是用synchonized關鍵字。 在許多情況下可以使用JUC包中的一種機制,它會為你處理所有加鎖.

如果synchronized關鍵字適合你的程序,那么盡量使用它,這樣可以減少編寫的代碼數量,減少出錯幾率。

如果特別需要Lock/Condition結果提供的獨有特性,才使用Lock/Condition

5.18

阻塞隊列

對于許多線程問題,可以通過使用一個或多個隊列以優雅且安全的方式將其形式化。比如生產者線程向隊列插入元素,消費者線程則取出它們。使用隊列,可以安全地從一個線程向另一個線程傳遞數據。

阻塞隊列是一種比lock, synchonized更高級的管理同步的方法。

具體實現:
先定義一個BlockingQueue,隊列放共享的資源,然后多個線程取或者存然后直接調用他的函數放入和取出元素就行了.

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/65300.html

相關文章

  • Java 并發學習筆記(二)

    摘要:請參看前一篇文章并發學習筆記一原子性可見性有序性問題六等待通知機制什么是等待通知機制當線程不滿足某個條件,則進入等待狀態如果線程滿足要求的某個條件后,則通知等待的線程重新執行。經極客時間并發編程實戰專欄內容學習整理 請參看前一篇文章:Java 并發學習筆記(一)——原子性、可見性、有序性問題 六、等待—通知機制 什么是等待通知—機制?當線程不滿足某個條件,則進入等待狀態;如果線程滿足要...

    zgbgx 評論0 收藏0
  • Java 并發學習筆記(一)——原子性、可見性、有序性問題

    摘要:最后,總結一下,導致并發問題的三個源頭分別是原子性一個線程在執行的過程當中不被中斷。可見性一個線程修改了共享變量,另一個線程能夠馬上看到,就叫做可見性。 計算機的 CPU、內存、I/O 設備的速度一直存在較大的差異,依次是 CPU > 內存 > I/O 設備,為了權衡這三者的速度差異,主要提出了三種解決辦法: CPU 增加了緩存,均衡和內存的速度差異 發明了進程、線程,分時復用 CP...

    Chao 評論0 收藏0
  • 并發學習筆記 (6)

    摘要:每個通過網絡到達服務器的連接都被包裝成一個任務并且傳遞給線程池。線程池的線程會并發的處理連接上的請求。用線程池控制線程數量,其他線程排隊等候。實現包,線程池頂級接口是但是嚴格意義講并不是一個線程。此線程池支持定時以及周期性執行任務的需求。 tutorial site1tutorial site2 一個問題: 每啟動一個新線程都會有相應的性能開銷(涉及到OS的交互:創建線程,銷毀線程...

    superw 評論0 收藏0
  • 并發學習筆記 (4)

    摘要:不剝奪條件進程已獲得的資源,在末使用完之前,不能強行剝奪。如果能確保所有的線程都是按照相同的順序獲得鎖,那么死鎖就不會發生。按照順序加鎖是一種有效的死鎖預防機制。這種機制存在一個問題,在中不能對同步塊設置超時時間。 [tutorial site][1] 死鎖 deadlock 死鎖是指兩個或兩個以上的進程在執行過程中,因競爭資源而造成的一種互相等待的現在,若無外力作用,它們都無法推...

    shiguibiao 評論0 收藏0
  • Go語言核心36講(Go語言實戰與應用十二)--學習筆記

    摘要:除此之外,把并發安全字典封裝在一個結構體類型中,往往是一個很好的選擇。請看下面的代碼如上所示,我編寫了一個名為的結構體類型,它代表了鍵類型為值類型為的并發安全字典。在這個結構體類型中,只有一個類型的字段。34 | 并發安全字典sync.Map (上)我們今天再來講一個并發安全的高級數據結構:sync.Map。眾所周知,Go 語言自帶的字典類型map并不是并發安全的。前導知識:并發安全字典誕生...

    不知名網友 評論0 收藏0

發表評論

0條評論

saucxs

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<