Java鎖的實現


volatile屬性:可見性、保證有序性、不保證原子性。

  Java的內存中所有的對象都存在主內存中,每個線程都有自己的棧和程序計數器,多個線程對同一個對象的變量讀取時,會將對象的變量從主內存中拷貝到自己的棧幀里(操作數棧),線程之間也無法直接訪問對方的操作數棧,只能通過主內存傳遞變量的值;

  可見性:如果對聲明了volatile變量進行寫操作時,JVM會向處理器發送一條Lock前綴的指令,將這個變量所在緩存行的數據寫會到系統內存。 這一步確保了如果有其他線程對聲明了volatile變量進行修改,則立即更新主內存中數據。

  有序性:但這時候其他處理器的緩存還是舊的,所以在多處理器環境下,為了保證各個處理器緩存一致,每個處理會通過嗅探在總線上傳播的數據來檢查 自己的緩存是否過期,當處理器發現自己緩存行對應的內存地址被修改了,就會將當前處理器的緩存行設置成無效狀態,當處理器要對這個數據進行修改操作時,會強制重新從系統內存把數據讀到處理器緩存里。 這一步確保了其他線程獲得的聲明了volatile變量都是從主內存中獲取最新的。

使用場景:狀態標記,后續會再寫另一個使用場景,單例模式下的volatile確保單例對象的返回的正確性。

原子操作:可以是一個步驟,也可以是多個步驟,但是其順序不可以被打斷,也不可以被切面只執行其中的一部分(不可中斷性)。

並發示例:

public class Counter {
    volatile int i=0;
    public void add(){
        //非原子操作 可以加鎖synchronized或者ReentrantLock等保證原子性
        i++;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        final Counter counter=new Counter();
        for(int i=0;i<5;i++){
            new Thread(new Runnable(){
                @Override
                public void run() {
                  for(int j=0;j<10000;j++){
                      counter.add();
                  }
                  System.out.println("Done......");
                }
            }).start();
        }
        //主線程進行等待
        Thread.sleep(5000);
        System.out.println(counter.i);
    }
}

  其中上述add()方法對應的匯編指令,其中i++被編譯成下面四條指令,因此無法實現原子性:

public void add();
    flags: ACC_PUBLIC
    Code:
      stack=3, locals=1, args_size=1
         0: aload_0
         1: dup
         2: getfield      #2      // Field i:I   【從主內存中加載到操作數棧】
         5: iconst_1                 【將1賦值給變量】
         6: iadd
         7: putfield      #2    // Field i:I   【將計算結果放回到主內存】
        10: return
      LineNumberTable:
        line 11: 0
        line 12: 10

 

CAS(Compare and swap)

Compare and swap 比較和交換,屬於硬件同步原語,處理器提供了基本內存操作的原子性保證。

CAS操作需要輸入兩個數值,一個舊值A和一個新值B,在操作期間對舊值進行比較,若沒有發生變化,才交換成新值,發生了變化則不交換。

Java中的sun.misc.Unsafe類,提供了compareAndSwapInt()和compareAndSwapLong()等幾個方法實現CAS。

JDK中很多工具類底層都是用到了CAS機制

原子操作類:AtomicInteger、AtomicIntegerArray、AutomicIntegerFieldUpdater

並發操作類:ReentrantLock、ReentrantReadWriteLock、AQS、Semphore、CountDownLatch

Map:ConcurrentHashMap、ConcurrentSkipListMap

List:CopyOnWriteArrayList

Set:CopyOnWriteArraySet、ConcurrentSkipListSet

線程池:ThreadPoolExecutor

 

使用CAS實現鎖:

public class CounterUnsafe
{
    volatile int i=0;
    private static Unsafe unsafe=null;
    private static long valueOffset;
    static{
        try {
            //反射出unsafe對象
            Field field=Unsafe.class.getDeclaredField("theUnsafe");
            field.setAccessible(true);
            unsafe=(Unsafe)field.get(null);
            //獲取Counter類中的字段i的偏移量
            Field ifield=Counter.class.getDeclaredField("i");
            valueOffset=unsafe.objectFieldOffset(ifield);
        }catch (NoSuchFieldException |IllegalAccessException e){
            e.printStackTrace();
        }
    }

    public void add(){
        for(;;){
            //拿到舊的值
            int current=unsafe.getIntVolatile(this,valueOffset);
            //進行+1操作
            int newValue=current+1;
            //如果寫會到堆內存成功
            if(unsafe.compareAndSwapInt(this,valueOffset,current,newValue)){
                break;
            }
        }
    }
}

 Counter類進行加鎖實現:

//對變量進行加鎖實現
public class Counter {
    volatile int i=0;
    Lock lock=new MyReentrantLock(); //自定義鎖
    public void add(){
        lock.lock();
        i++;
        lock.unlock();
    }
}


//自定義鎖
public class MyReentrantLock implements Lock{
    //標記鎖的持有線程
    AtomicReference<Thread> owner=new AtomicReference<>();
    //等待的線程隊列  BlockQueue [ 如果當前沒有可用的空間  add 拋異常,offer 返回false,put阻塞][remove,poll,take]
    private LinkedBlockingQueue<Thread> waiters=new LinkedBlockingQueue<>();

    @Override
    public void lock(){
        //如果未獲取到鎖
        if(!tryLock()){
            waiters.offer(Thread.currentThread());
            //1、線程掛起的幾種方式 [condition] [Suspend.resume()  容易造成死鎖,已經被棄用] [wait nofity 必須與synchronized 關鍵字一起使用]
           for(;;){
               //判斷是否是線程的頭部,如果不是繼續循環
               Thread peek = waiters.peek(); //peek拿到元素,元素本身不出隊列
               if(peek!=Thread.currentThread()){
                 continue;
               }
               //如果是線程的頭部則嘗試着去拿鎖
               if(tryLock()){
                   waiters.poll();// poll拿到元素,元素出隊列
                   return;
               }
               //沒有拿到鎖,則將線程掛起
               LockSupport.park();
           }
        }
    }

    @Override
    public boolean tryLock() {
        return owner.compareAndSet(null,Thread.currentThread());
    }

    @Override
    public void unlock() {
        if(tryUnlock()){
            Thread thread=waiters.peek();
            if(thread!=null){
                LockSupport.unpark(thread); //喚醒線程
            }
        }
    }


    public boolean tryUnlock(){
        if(owner.get()!=Thread.currentThread()){
            throw new IllegalMonitorStateException();
        }else{
            return owner.compareAndSet(Thread.currentThread(),null);
        }
    }
    
    @Override
    public void lockInterruptibly() throws InterruptedException{

    }

    @Override
    public boolean tryLock(long time, TimeUnit unit) throws InterruptedException {
        return false;
    }

    @Override
    public Condition newCondition() {
        return null;
    }
}

 其中Unsafe參見:Java魔法類:Unsafe應用解析


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM