java併發編程基礎(四)

Java中的鎖

1.Lock接口

鎖是用來控制多個線程訪問共享資源的方式,一般來說,一個鎖能夠防止多個線程訪問共享資源。在lock接口出現之前,java程序依靠synchroized關鍵字實現鎖的功能,Loc接口擁有了獲取鎖與釋放鎖的可操作性,可中斷的獲取鎖。

package cn.smallmartial.concurrency;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @Author smallmartial
 * @Date 2019/8/25
 * @Email [email protected]
 */
public class LockUseCase {
    public static void main(String[] args) {
        ReentrantLock lock = new ReentrantLock();
        lock.lock();
        try{
            
        }finally {
            lock.unlock();
        }
    }
}

在finally塊中釋放鎖,目的保證在獲取鎖之後,最終能夠被釋放

2.隊列同步器

隊列同步器是用來構建鎖和其他同步組件的基礎,它使用一個int成員變量表示同步狀態,通過內置的FIFO隊列來完成資源獲取線程的排隊工作。

同步器是實現鎖的關鍵,在鎖的實現中聚合同步器,利用同步器實現鎖的語義。鎖是面向使用者的,它定義了使用者與鎖的交互接口,隱藏了實現細節;同步器面向的是鎖的實現者,它簡化了鎖的實現方式,屏蔽了同步狀態管理,線程的排隊,等待與喚醒等底層操作。鎖與同步器很好的隔離了使用者和實現者所關注的領域。

同步器主要使用方式是繼承,子類通過繼承同步器並實現它的抽象方法來管理同步器的狀態:

  • getState():獲取當前的同步狀態
  • setState(int newState):設置當前同步器的狀態
  • compareAndSetState(int expect,int update):使用CAS設置當前狀態,該方法能夠保證狀態設置的原子性。

同步器提供的模板方法分爲3類:

  • 獨佔式獲取與釋放同步狀態
  • 共享式獲取與釋放同步狀態
  • 查詢同步隊列中等待線程情況
package cn.smallmartial.lock;

import sun.misc.Lock;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.AbstractQueuedLongSynchronizer;
import java.util.concurrent.locks.Condition;

/**
 * @Author smallmartial
 * @Date 2019/8/25
 * @Email [email protected]
 */
public class Mutex extends Lock {
    //靜態內部類 自定義同步器
    private static class Sync extends AbstractQueuedLongSynchronizer{
        //是否處於佔用狀態
        @Override
        protected boolean isHeldExclusively(){
            return getState() == 1;
        }
        //當狀態爲0的時候獲取鎖 同步器重寫方法 獨佔式獲取同步狀態
        public boolean tryAcquire(int acquires){
            /**
             *  CAS的機制就相當於這種(非阻塞算法),CAS是由CPU硬件實現,所以執行相當快.
             *  CAS有三個操作參數:內存地址,期望值,要修改的新值,當期望值和內存當中的值進行比較不相等的時候,
             *  表示內存中的值已經被別線程改動過,這時候失敗返回,當相等的時候,將內存中的值改爲新的值,並返回成功。
             */
            if(compareAndSetState(0,1)){
                setExclusiveOwnerThread(Thread.currentThread());
                return true;
            }
            return false;
        }
        //釋放鎖,將狀態設置爲0 同步器重寫方法 獨佔式釋放同步狀態
        protected boolean tryRelease(int relaeases){
            if (getState() == 0){
                throw new IllegalMonitorStateException();
            }
            setExclusiveOwnerThread(null);
            setState(0);
            return true;
        }
        //返回一個Condition,每個Condition都包含一個condition對列
        Condition newCondition(){
            return new ConditionObject();
        }

    }
    private final Sync sync = new Sync();

    public void lock1(){
        sync.acquire(1);
    }

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

    public void unlock1(){
        sync.release(1);
    }

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

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

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

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

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

    public static void main(String[] args) {

    }
}

3.隊列同步器的實現分析

3.1同步器

3.2獨佔式同步狀態獲取流程,acquire(int arg)方法調用流程。

3.3總結

在獲取同步狀態時,同步器維護一個同步隊列,獲取狀態失敗的線程都會被加入隊列並在隊列中進行自旋;移除隊列(停止自旋的條件)是前驅節點爲頭節點且成功獲取了同步狀態。在釋放鎖同步狀態時,同步器調用tryRelease(int arg)方法釋放同步狀態,然後喚醒頭節點的後繼節點。

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