1. 程式人生 > >java併發程式設計應用易忘知識點(1)java中的鎖

java併發程式設計應用易忘知識點(1)java中的鎖

Lock介面

Java se5之後,出現了Lock介面,提供了與Synchronized類似同步功能。與synchronized相比,他雖然少了隱式獲取釋放鎖的便捷性,卻擁有了鎖獲取與釋放的可操作性、可中斷的獲取鎖以及超時獲取鎖等多種synchronized不具備的同步特性。 Lock的使用方式非常簡單,首先例項化Lock物件 Lock lock = new ReentrantLock(); 然後在需要的地方加鎖: lock.lock(): try{ //do something }finally{ lock.unlock();//避免發生異常不能釋放鎖 }

佇列同步器

AbstractQueueSynchronizer佇列同步器(以下簡稱同步器),是用來構建鎖或者其他同步元件的基本框架。同步器是實現鎖的關鍵,鎖和同步器的關係如下:鎖是面向使用者的,隱藏了實現細節。而同步器面向的是鎖的實現者,它簡化了鎖的實現方式,遮蔽了同步狀態管理、執行緒的排隊、等待與喚醒等底層操作。

佇列同步器的介面與示例

同步器的設計是基於模板方法模式的,因此,使用者需要繼承同步器並重寫指定的方法,隨後將同步器組合在自定義的同步元件的實現中,並呼叫同步器提供的模板方法。而這些模板方法將會呼叫使用者重寫的方法。 也就是說同步器的方法主要分為兩類:模板方法和可重寫方法。 如下所示 同時,你要重寫可重寫方法,需要使用下面三個方法來訪問或者修改同步狀態 getState():獲取當前同步狀態 setState(int newState):設定當前同步狀態 compareAndSetState(int expect,int update):使用CAS操作設定當前狀態,該方法能保證狀態設定的原子性。 下面我們來看一個同步器的簡單實現;
import java.util.concurrent.locks.AbstractQueuedSynchronizer;
import java.util.concurrent.locks.Condition;

public class Mutex {
	private class Sync extends AbstractQueuedSynchronizer{
		//是否處於佔用狀態
		protected boolean isHeldExclusively(){
			return getState()==1;
		}
		//當狀態為0的時候獲取鎖
		public boolean tryAcquire(int acquire){
			if(compareAndSetState(0, 1)){
				setExclusiveOwnerThread(Thread.currentThread());
				return true;
			}
			return false;
		}
		//釋放鎖,將狀態設定為0
		protected boolean tryRease(int releases){
			if(getState()==0){
				throw new IllegalMonitorStateException();
			}
			setExclusiveOwnerThread(null);
			setState(0);
			return true;
		}
		public Condition newCondition() {
			// TODO Auto-generated method stub
			return new ConditionObject();
		}
	}
	public final Sync s = new Sync();
	public void lock(){s.acquire(1);}
	public boolean tryLock(){return s.tryAcquire(1);}
	public void unlock(){s.release(1);}
	public Condition newCondition(){return s.newCondition();}
	public boolean isLocked(){return s.isHeldExclusively();}
	public boolean hasQueueThreads(){return s.hasQueuedThreads();}
	//other methods as same as in Lock

}

佇列同步器的實現分析

佇列同步器的實現主要包括以下幾個方面:同步佇列、度展示同步狀態獲取與釋放、共享式同步狀態獲取與釋放、以及超時獲取同步狀態等同步器的核心資料結構和模板方法。

同步佇列

同步器依賴同步佇列---一個雙向的FIFO佇列----來完成同步狀態的管理。當前執行緒獲取同步狀態失敗時,同步器會將當前執行緒以及等待狀態等資訊封裝的成一個節點放入同步佇列,同時會阻塞當前執行緒。當同步狀態釋放時,會把首節點的執行緒喚醒,使其再次嘗試獲得同步狀態。 同步器擁有首節點和為節點,每當一個執行緒獲取同步狀態失敗,都會加入這個同步佇列的尾部,這個操作是要保證執行緒安全的,也就是說,必須使用CAS操作。同步器提供了一個CAS方法:compareAndSetTail(node expect,node update)

同步器持有的同步佇列的節點喚醒基本過程就是:首先,首節點是獲取同步狀態成功的節點,當同步狀態被當前首節點釋放時,後繼的節點會在獲取同步狀態成功時將自己設定為首節點。同時這個過程是由獲得同步狀態成功的執行緒來執行的,因此不需要CAS操作。

獨佔式同步狀態的獲取和釋放

通過呼叫同步器的acquire方法可以獲得同步狀態。該方法對中斷不敏感,也就是和synchronized一樣,對於獲得鎖之後的中斷不會執行中斷操作。同步器的acquire方法呼叫了我們在自定義同步器類Sync中的tryAcquire()方法。其原始碼如下所示:
public final void acquire(int arg) {
        if (!tryAcquire(arg) &&
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();
    }
這個方法的if語句作了三個操作:獲取同步狀態,如果上一步不成功則進行節點構造並將節點加入同步佇列(addWaiter()方法),並在同步佇列中自旋(acquireQueued()方法)。我麼前面已經實現了tryAcquire,下面我們來看看addWaiter的原始碼
 private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {
                pred.next = node;
                return node;
            }
        }
        enq(node);
        return node;
    }
上面的程式碼首先建立了一個儲存當前節點和其狀態的Node例項node,然後將當前尾節點儲存(Node pred = tail)。接下來在if語句中,如果當前尾節點非空,表示佇列非空,那麼就讓node的前一個節點指標pre指向pred,同時compareAndSetTail方法確保節點能夠被獨立的設定為尾節點。然後讓pred的下一個節點指向node,也就是現在的尾節點。enq(node)通過死迴圈的方式來保證節點正確新增。
節點進入佇列後,就保持自旋的狀態來觀察,當條件滿足,獲取到了同步狀態,那麼就可以從自旋中退出。如下面的程式碼所示:
 final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor();
                if (p == head && tryAcquire(arg)) {
                    setHead(node);
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

共享式同步狀態獲取與釋放

共享式允許同一時刻多個執行緒同時獲取同步狀態。 共享式同步狀態的acquireShared(int arg)可以共享的獲得同步狀態,當tryAcquaire方法返回值大於0,表示能夠獲得同步狀態,否則呼叫doAcquireShare進行自旋該方法程式碼如下:
釋放的程式碼如下:

重入鎖

前面我們寫的Mutex就是一個不可重入的鎖,重入的鎖就是當一個執行緒A獲得一個鎖之後,再次呼叫lock()方法,不會把自己阻塞,而是在當前鎖繼續加一。釋放鎖的時候,也會每釋放一次就讓state加一,直到加到0表示釋放完成。 公平鎖和非公平鎖:在研究衝入鎖的時候,ReentrantLokc實現了兩個方法。TryAcquire是公平的鎖,nonfairTryAcquire()方法是非公平鎖。二者的區別是,公平鎖能夠是能夠按照進入的佇列順序來進行鎖的獲取的,而非公平鎖不管這個。可以通過呼叫ReentrantLock(Boolean flag)來設定開啟。設定flag為true是公平鎖,否則為非公平鎖。預設是非公平鎖

讀寫鎖ReentrantReadWriteLock

讀寫鎖不是實現的lock介面,因此不能用多型的方式例項化ReentrantReadWriteLock。讀寫鎖能夠獲得讀鎖和寫鎖。讀鎖是共享的,寫鎖是排除的。

LockSupport工具

LockSupport工具定義了一組的公共靜態方法,提供了基本的執行緒阻塞和喚醒功能。

Condition介面

condition介面也實現了類似的像監控器一樣的方法。與lock配合可以實現等待通知模式。

相關推薦

java併發程式設計應用知識點1java

Lock介面 Java se5之後,出現了Lock介面,提供了與Synchronized類似同步功能。與synchronized相比,他雖然少了隱式獲取釋放鎖的便捷性,卻擁有了鎖獲取與釋放的可操作性、可中斷的獲取鎖以及超時獲取鎖等多種synchronized不具備的同步特性

16-Java併發程式設計:Timer和TimerTask轉載

Java併發程式設計:Timer和TimerTask(轉載)   下面內容轉載自:   其實就Timer來講就是一個排程器,而TimerTask呢只是一個實現了run方法的一個類,而具體的TimerTask需要由你自己來實現,例如這樣: Timer timer =

Java併發程式設計的藝術——讀書筆記 併發程式設計的挑戰

第一章 併發程式設計的挑戰 因為最近找工作,準備筆試/面試,開始嘗試閱讀這本書,我不常寫部落格,距上一次寫已經過去大概一年時間了,連CSDN密碼都忘了/衰,所以這次新開一個賬號重新開始,希望我能堅持下去。 第一章沒什麼內容,我認為其目的主要是給出足夠多的閱讀這本書的理

Java併發程式設計:Timer和TimerTask轉載

public Timer(boolean isDaemon) { this("Timer-" + serialNumber(), isDaemon); }    另外兩個構造方法負責傳入名稱和將timer啟動: public Timer(String name, boo

JAVA併發程式設計——執行緒協作通訊

執行緒間的協作 在前面我們瞭解了很多關於同步(互斥鎖)的問題,下面來看一下執行緒之間的協作。這裡主要說一下Java執行緒中的join()、sleep()、yield()、wait()、notify()和notifyAll()方法。其中wait()、notify(

Java併發程式設計實戰--計數訊號量Semaphore

計數訊號量(Counting Semaphore)用來控制同時訪問某個特定資源的運算元量,或者同時執行某個指定操作的數量。計數訊號量還可以用來實現某種資源池,或者對容器施加邊界。 Semaphore中管理著一組虛擬的許可(permit),許可的初始數量可通過建

Java併發程式設計:volatile關鍵字解析轉載

Java併發程式設計:volatile關鍵字解析    volatile這個關鍵字可能很多朋友都聽說過,或許也都用過。在Java 5之前,它是一個備受爭議的關鍵字,因為在程式中使用它往往會導致出人意料的結果。在Java 5之後,volatile關鍵字才得以重獲生機。   volatile關鍵字雖然

Java併發框架——AQS阻塞佇列管理——自旋

我們知道一個執行緒在嘗試獲取鎖失敗後將被阻塞並加入等待佇列中,它是一個怎樣的佇列?又是如何管理此佇列?這節聊聊CHL Node FIFO佇列。 在談到CHL Node FIFO佇列之前,我們先分析這種佇列的幾個要素。首先要了解的是自旋鎖,所謂自旋鎖即是某一執行緒去嘗試獲取某個

《C++併發程式設計實戰》讀書筆記1

這兩天開始看《C++併發程式設計實戰》一書,感覺受益匪淺啊! 按照書中的同步併發操作(第四章)的內容,嘗試編寫執行緒安全的佇列,模擬固定採集時間和不確定處理時間下的佇列行為,供大家參考。 用到的C++多執行緒相關的主要內容為:mutex類(鎖物件),lock_guard模板

Java併發框架——AQS阻塞佇列管理——自旋優化

看Craig, Landin, and Hagersten發明的CLH鎖如何優化同步帶來的花銷,其核心思想是:通過一定手段將所有執行緒對某一共享變數輪詢競爭轉化為一個執行緒佇列且佇列中的執行緒各自輪詢自己的本地變數。這個轉化過程由兩個要點,一是構建怎樣的佇列&如何構建

Java併發程式設計:Synchronized底層優化偏向、輕量級 Java併發程式設計:Synchronized底層優化偏向、輕量級

轉自:https://www.cnblogs.com/paddix/p/5405678.html Java併發程式設計:Synchronized底層優化(偏向鎖、輕量級鎖)   Java併發程式設計系列: J

Java併發程式設計原理與實戰一執行緒狀態及建立執行緒的多種方式

一、為什麼要學習併發程式設計 1.發揮多處理的強大能力 2.建模的簡單性 3.非同步事件的簡化處理 4.響應更加靈敏的使用者介面 二、併發的缺點 1.安全性問題 多執行緒環境下 多個執行緒共享一個資源 對資源進行非原子性操作 2.活躍

Java併發程式設計:Synchronized底層優化偏向、輕量級

Java併發程式設計系列: 一、重量級鎖   上篇文章中向大家介紹了Synchronized的用法及其實現的原理。現在我們應該知道,Synchronized是通過物件內部的一個叫做監視器鎖(monitor)來實現的。但是監視器鎖本質又是依賴於底層的作業系統的Mutex Lock來實現的。而

Java併發程式設計:volatile關鍵字解析二.併發程式設計的三個概念

在併發程式設計中,我們通常會遇到以下三個問題:原子性問題,可見性問題,有序性問題。我們先看具體看一下這三個概念: 1.原子性   原子性:即一個操作或者多個操作 要麼全部執行並且執行的過程不會被任何因素打斷,要麼就都不執行。   一個很經典的例子就是銀行賬戶轉賬問題

[Java併發程式設計實戰] 柵欄 CyclicBarrier 實現含程式碼

溫故而知新,可以為師矣。—《論語》 它的意思是:“溫習舊知識從而得知新的理解與體會,憑藉這一點就可以成為老師了 PS: 如果覺得本文有用的話,請幫忙點贊,留言評論支援一下哦,您的支援是我最大的動力!謝謝啦~ 柵欄(Barrier)類似於閉鎖,

Java併發程式設計之執行緒管理基本執行緒同步3

 (上一節的繼續) 2.2 使用Lock機制         Java提供了另外一種機制來同步程式碼塊。它是比synchrozied關鍵字更為強大且彈性的機制。它是基於鎖介面和實現了這個介面的類

Java併發程式設計之執行緒管理高階執行緒同步7

3執行緒同步實用程式 在這一節中,我們將討論如何使用高級別機制去獲得多執行緒的同步(synchronized)。這些高級別機制有下面幾種: Ø  訊號(Semaphores):一個訊號就是一個計數

Java併發程式設計】之八:多執行緒環境安全使用集合API含程式碼

在集合API中,最初設計的Vector和Hashtable是多執行緒安全的。例如:對於Vector來說,用來新增和刪除元素的方法是同步的。如果只有一個執行緒與Vector的例項互動,那麼,要求獲取

Java併發程式設計:volatile關鍵字解析一.記憶體模型的相關概念

大家都知道,計算機在執行程式時,每條指令都是在CPU中執行的,而執行指令過程中,勢必涉及到資料的讀取和寫入。由於程式執行過程中的臨時資料是存放在主存(實體記憶體)當中的,這時就存在一個問題,由於CPU執行速度很快,而從記憶體讀取資料和向記憶體寫入資料的過程跟CPU執行指令

Java併發程式設計之執行緒管理基本執行緒同步6

3.5 在一個鎖中使用多種狀態 一個鎖(Lock)可能和一個或者多個狀態相關聯,這些狀態在Condition介面中已經被宣告好了。這些狀態的作用就是去執行執行緒控制一個鎖或者檢查一個狀態是否為true或者false。如果是false,這個執行緒將被掛起直到其它的執行緒將它們