天天看點

Java 并發程式設計 鎖與同步器

1、lock

Lock 接口是Java并發包中最基礎的一個接口,相應的它定義了一些鎖的基本功能。相比synchronized關鍵字,具有以下特征:

可以嘗試非阻塞地擷取鎖

可中斷的擷取鎖

定時擷取鎖

Lock這個基礎接口的相對比較簡單,有如下方法:

void lock();
   void lockInterruptibly() throws InterruptedException;
   boolean tryLock();
   boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
   void unlock();
           

2 、同步器

隊列同步器 AbstractQueuedSynchronizer ,使用來建構鎖或者其他同步元件的基礎架構,它使用一個int成員變量表示同步狀态。通過内置FIFO隊列來完成資源擷取線程的排隊工作。

同步器提供了三個方法來進行對狀态值的操作。

getState  
 setState(int  newState)  
 compareAndSetState(int expect ,int update)
           

同步器可以支援獨占式地擷取同步狀态,也可以共享式地擷取同步狀态。

同步其是實作鎖(任意同步元件)的關鍵,在鎖的實作中聚合同步器,利用同步其實作鎖的語義。

鎖是面試使用者的,它定義了使用者與鎖互動的接口,隐藏了實作細節。

同步器面向的是鎖的實作者,它簡化了鎖的實作方式,屏蔽了同步狀态管理,線程的排隊,等待與喚醒等底層操作。

3、 同步器的内部實作:

3.1同步器的設計原理:

同步器的設計是基于模闆方法模式的,也就是說,使用者需要繼承同步器并重寫指定的方法,随後将同步器組合在定義同步元件的實作中,并調用同步器提供的模闆方法,而這些模闆方法将會調用使用者重寫的方法。

3.2 同步器可重寫的方法:

//獨占式擷取鎖和釋放鎖的方法:
   protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
    }
           

實作該方法,需要查詢目前狀态并判斷同步狀态是否符合預期,然後再 進行CAS設定同步狀态。

//獨占式釋放同步狀态,等待擷取同步狀态線程将有機會擷取同步狀态。
  protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }
           

共享式擷取鎖和釋放鎖的方法:

//共享式擷取同步狀态,傳回大于等于0的值,表示擷取成功,反之擷取失敗。
 protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }
           
//共享式釋放同步狀态。
protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }
           
//目前同步器是否在獨占模式下被線程占用,一般該方法表示是否被目前線程所獨占。
protected boolean isHeldExclusively() {
        throw new UnsupportedOperationException();
    }
           

3.3同步器提供的模闆方法

void acquire(int  arg)
           

獨占式擷取同步狀态,如果目前線程擷取同步狀态成功,則由該方法傳回,否則,将會進入同步隊列等待,該方法将會調用重寫的tryAcquire(int arg)方法。

void acquireInterruptibly(int arg)
           

與 acquire 方法相同,但是該方法相應中斷,目前線程未擷取到同步狀态而進入同步隊列中,如果目前線程被中斷,則該方法抛出InterruptedException并傳回。

boolean tryAcquireNanos(int arg, long nanos)
           

在 基礎上增加了逾時限制,如果目前線程在逾時時間内沒有擷取到同步狀态,那麼将會傳回false,如果擷取到了傳回true。

boolean  acquireShared(int arg)
           

共享式的擷取同步狀态,如果目前線程為擷取到同步狀态,将會進入同步對列等待,與獨占式擷取的主要差別是在同一時刻可以有多個線程擷取到同步狀态。

void  acquireSharedInterruptibly(int arg)
           

該方法響應中斷。

void  tryAcquireSharedNanos(int arg,long nanos)
           

在基礎上了增加了逾時限制。

boolean release(int arg)
           

獨占式的釋放同步狀态,該方法會在釋放同步狀态之後,将同步隊列中第一節點包含的線程喚醒。

boolean releaseShared(int arg)
           

共享式的釋放同步狀态,

boolean releaseShared(int arg)
           

擷取等待在同步對列上的線程集合。

4、 獨占鎖的示例

獨占鎖就是在同一時刻隻能又一個線程擷取到鎖,而其他擷取鎖的線程隻能處于同步隊列中等待,隻有擷取鎖的線程釋放了鎖,後繼的線程才能夠擷取鎖。

上面說了,同步器采用了模闆的設計模式,需要重寫相應的方法,獨占鎖自然隻要重寫獨占式的方法就可以了:

public class Mutex implements Lock{

    private static class Sync extends  AbstractQueuedSynchronizer{
        protected boolean isHeldExclusively(){
            return getState()==;
        }


        public boolean tryAcquire(int acuqires){
            if(compareAndSetState(,)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }

        protected boolean  tryRelease(int releases){
            if(getState()==)  throw new IllegalMonitorStateException();
            setExclusiveOwnerThread(null);
            setState();
            return true;
        }

        Condition newCondition(){
            return new ConditionObject();
        }

    }
    private final Sync sync = new Sync();


    public void lock() {
          sync.acquire();      
    }

    public void lockInterruptibly() throws InterruptedException {
        sync.acquireInterruptibly();
    }

    public boolean tryLock() {
        return sync.tryAcquire();
    }

    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return sync.tryAcquireNanos(,unit.toNanos(time));
    }

    public void unlock() {
        sync.release();
    }

    public Condition newCondition() {
        return sync.newCondition();
    }

    public boolean isLocked(){
        return sync.isHeldExclusively();
    }

    public boolean hasQueuedThreads(){
        return sync.hasQueuedThreads();
    }


}
           

繼續閱讀