java.util.concurrent——Locks

1.Locks包

下面這個圖更方便理解:

在這裏插入圖片描述

juc-locks鎖框架中一共就三個接口:Lock、Condition、ReadWriteLock,接下來對這些接口作介紹。

1.1 Lock

Lock接口可以視爲synchronized的增強版,提供了更靈活的功能。該接口提供了限時鎖等待、鎖中斷、鎖嘗試等功能。

Lock接口主要的實現是ReentrantLock重入鎖,另外還有ConcurrentHashMap中的Segment繼承了ReentrantLock,在ReentrantReadWriteLock中的WriteLock和ReadLock也實現了Lock接口。

談到併發,不得不談ReentrantLock;而談到ReentrantLock,不得不談AbstractQueuedSynchronizer(AQS)(同步器)!

1.1.1 AQS

AbstractQueuedSynchronizer就是大名鼎鼎的AQS類,怎麼說呢,這個類就是JUC鎖的根基,是JUC中不同鎖的共同抽象類,鎖的許多公共方法都是在這個類中實現的,我給你看看它類繼承結構你就知道了

有沒有發現,無論是帶有Lock後綴的類如ReentrantLock還是不帶Lock後綴的類如Semaphore它們裏面的內部類Sync都繼承了AQS。其實JUC中的各種鎖只是一個表面裝飾,它們裏面真正實現功能的還是Sync

AQS核心原理是,如果被請求的共享資源空閒,則將當前請求資源的線程設置爲有效的工作線程,並且將共享資源設置爲鎖定狀態。如果被請求的共享資源被佔用,那麼就需要一套線程阻塞等待以及被喚醒時鎖分配的機制,這個機制AQS是用CLH隊列鎖實現的,即將暫時獲取不到鎖的線程加入到隊列中。

 

  同步器的核心方法是acquire和release操作,其背後的思想也比較簡潔明確。

acquire操作是這樣的:

  while (當前同步器的狀態不允許獲取操作) {

          如果當前線程不在隊列中,則將其插入隊列

          阻塞當前線程

  }

  如果線程位於隊列中,則將其移出隊列  

release操作是這樣的:

  更新同步器的狀態

  if (新的狀態允許某個被阻塞的線程獲取成功){

         解除隊列中一個或多個線程阻塞

       }

AQS定義兩種資源共享方式

  • Exclusive(獨佔):只有一個線程能執行,如ReentrantLock。又可分爲公平鎖和非公平鎖:

    • 公平鎖:按照線程在隊列中的排隊順序,先到者先拿到鎖
    • 非公平鎖:當線程要獲取鎖時,無視隊列順序直接去搶鎖,誰搶到就是誰的
  • Share(共享):多個線程可同時執行,如Semaphore/CountDownLatch。Semaphore、CountDownLatCh、 CyclicBarrier、ReadWriteLock 我們都會在後面講到。

一般來說,自定義同步器要麼是獨佔方法,要麼是共享方式,他們也只需實現tryAcquire-tryReleasetryAcquireShared-tryReleaseShared中的一種即可。但AQS也支持自定義同步器同時實現獨佔和共享兩種方式,如ReentrantReadWriteLock

1.1.2 ReentrantLock

可重入原理,以非公平鎖爲例:

final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    //1\. 如果該鎖未被任何線程佔有,該鎖能被當前線程獲取
    if (c == 0) {
        if (compareAndSetState(0, acquires)) {
            setExclusiveOwnerThread(current);
            return true;
        }
    }
    //2.若被佔有,檢查佔有線程是否是當前線程
    else if (current == getExclusiveOwnerThread()) {
        // 3\. 再次獲取,計數加一
        int nextc = c + acquires;
        if (nextc < 0) // overflow
            throw new Error("Maximum lock count exceeded");
        setState(nextc);
        return true;
    }
    return false;
}

ReenTrantLock可重入鎖(和synchronized的區別)總結

2.Condition

Condition是一個多線程間協調通信的工具類,使得某個,或者某些線程一起等待某個條件(Condition),只有當該條件具備( signal 或者 signalAll方法被帶調用)時 ,這些等待線程纔會被喚醒,從而重新爭奪鎖。

Condition函數列表

// 造成當前線程在接到信號或被中斷之前一直處於等待狀態。
void await()
// 造成當前線程在接到信號、被中斷或到達指定等待時間之前一直處於等待狀態。
boolean await(long time, TimeUnit unit)
// 造成當前線程在接到信號、被中斷或到達指定等待時間之前一直處於等待狀態。
long awaitNanos(long nanosTimeout)
// 造成當前線程在接到信號之前一直處於等待狀態。
void awaitUninterruptibly()
// 造成當前線程在接到信號、被中斷或到達指定最後期限之前一直處於等待狀態。
boolean awaitUntil(Date deadline)
// 喚醒一個等待線程。
void signal()
// 喚醒所有等待線程。
void signalAll()

   看一個生產者消費者的例子  (使用了signal喚醒生產線程)

/**
 * 生產者、消費者示例
 */
public class ConditionTest {
    private int storage;
    private int putCounter;
    private int getCounter;
    private Lock lock = new ReentrantLock();
    private Condition putCondition = lock.newCondition();
    private Condition getCondition = lock.newCondition();

    public void put() throws InterruptedException {
        try {
            lock.lock();
            if (storage > 0) {
                putCondition.await();
            }
            storage++;
            System.out.println("put => " + ++putCounter );
            getCondition.signal();
        } finally {
            lock.unlock();
        }
    }

    public void get() throws InterruptedException {
        try {
            lock.lock();
            lock.lock();
            if (storage <= 0) {
                getCondition.await();
            }
            storage--;
            System.out.println("get  => " + ++getCounter);
            putCondition.signal();
        } finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public class PutThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    put();
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public class GetThread extends Thread {
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    get();
                } catch (InterruptedException e) {
                }
            }
        }
    }

    public static void main(String[] args) {
        final ConditionTest test = new ConditionTest();
        Thread put = test.new PutThread();
        Thread get = test.new GetThread();
        put.start();
        get.start();
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章