鎖機制


悲觀鎖:

      悲觀鎖悲觀的認為每一次操作都會造成更新丟失問題,在每次查詢時加上排他鎖

       每次去拿數據的時候都認為別人會修改,所以每次在拿數據的時候都會上鎖,這樣別人想拿這個數據就會block直到它拿到鎖。傳統的關系型數據庫里邊就用到了很多這種鎖機制,比如行鎖,表鎖等,讀鎖,寫鎖等,都是在做操作之前先上鎖。

     Select * from xxx for update;

     缺點:因為只能保證一個連接進行操作,所以效率低

樂觀鎖:

        樂觀鎖會樂觀的認為每次查詢都不會造成更新丟失,利用版本字段控制

重入鎖:

       重入鎖也叫做遞歸鎖,指的是同一線程 外層函數獲得鎖之后 ,內層遞歸函數仍然有獲取該鎖的代碼,但外層函數不受內層函數影響,例如當內部釋放鎖(unlock)后,外部不會釋放。在JAVA環境下 ReentrantLock 和synchronized 都是可重入鎖。

synchronized :

public class Test implements Runnable {
       //外層
    public  synchronized void get() {
        System.out.println("name:" + Thread.currentThread().getName() + " get();");
        set();
    }
         //內層
    public synchronized  void set() {
        System.out.println("name:" + Thread.currentThread().getName() + " set();");
    }

    @Override

    public void run() {
        get();
    }

    public static void main(String[] args) {
        Test ss = new Test();
        new Thread(ss).start();
        new Thread(ss).start();
        new Thread(ss).start();
        new Thread(ss).start();
    }
}

ReentrantLock :

public class Test02 extends Thread {
    ReentrantLock lock = new ReentrantLock();
     //外層
    public void get() {
        lock.lock();
        System.out.println(Thread.currentThread().getId());
        set();
        lock.unlock();
    }
      //內層
    public void set() {
        lock.lock();
        System.out.println(Thread.currentThread().getId());
        lock.unlock();
    }
    @Override
    public void run() {
        get();
    }
    public static void main(String[] args) {
        Test ss = new Test();
        new Thread(ss).start();
        new Thread(ss).start();
        new Thread(ss).start();
    }

}

讀寫鎖:

        兩個線程同時讀一個資源沒有任何問題,所以應該允許多個線程能在同時讀取共享資源。但是如果有一個線程想去寫這些共享資源,就不應該再有其它線程對該資源進行讀或寫(讀-讀能共存,讀-寫不能共存,寫-寫不能共存)。這就需要一個讀/寫鎖來解決這個問題。Java5在java.util.concurrent包中已經包含了讀寫鎖。

CAS無鎖機制:

原子類底層實現保證線程安全就是通過CAS實現。

CAS算法的過程是這樣:它包含三個參數CAS(V,E,N): V表示要更新的變量,E表示預期值,N表示新值。僅當V值等於E值時,才會將V的值設為N,如果V值和E值不同,則說明已經有其他線程做了更新,則當前線程什么都不做。最后,CAS返回當前V的真實值。

對應java內存模型,V相當於是主內存,E相當於本地內存,如果(V=E),本地內存與主內存一致,說明變量沒有被修改過那么就將V要更新的變量的值設置成N新值,如果不相等,說明本地內存被修改,需要將主內存的值刷新到本地內存中去,再進行V和E進行比較,然后再設置成新值N。

一個來自碼農翻身的例子:

(1)從內存中讀取value值,假設為10,稱之為A

(2)B=A+1,得到B=11

(3)用A的值和內存的值相比,如果相等(過去的一段時間內,沒人修改過A),就把B寫入內存,如果不相等的,說明A在這段時間內被修改了,就放棄這次修改,返回第一步

     CAS存在一個很明顯的問題,即ABA問題。

  問題:如果變量V初次讀取的時候是A,並且在准備賦值的時候檢查到它仍然是A,那能說明它的值沒有被其他線程修改過了嗎?         

          如果在這段期間曾經被改成B,然后又改回A,那CAS操作就會誤認為它從來沒有被修改過。針對這種情況,java並發包中提供了一個帶有標記的原子引用類AtomicStampedReference,它可以通過控制變量值的版本來保證CAS的正確性。

自旋鎖:

自旋鎖是采用讓當前線程不停地的在循環體內執行實現的,當循環的條件被其他線程改變時 才能進入臨界區。

private AtomicReference<Thread> sign =new AtomicReference<>();
    public void lock() {
        Thread current = Thread.currentThread();
        while (!sign.compareAndSet(null, current)) {
          }
    }
    public void unlock() {
        Thread current = Thread.currentThread();
        sign.compareAndSet(current, null);
    }

public class Test implements Runnable {
    static int sum;
    private SpinLock lock;

    public Test(SpinLock lock) {
        this.lock = lock;
    }

    /**
     * @param args
     * @throws InterruptedException
     */
    public static void main(String[] args) throws InterruptedException {
        SpinLock lock = new SpinLock();
        for (int i = 0; i < 100; i++) {
            Test test = new Test(lock);
            Thread t = new Thread(test);
            t.start();
        }

        Thread.currentThread().sleep(1000);
        System.out.println(sum);
    }

    @Override
    public void run() {
        this.lock.lock();
            this.lock.lock();
        sum++;
        this.lock.unlock();
        this.lock.unlock();
    }

}

當一個線程 調用這個不可重入的自旋鎖去加鎖的時候沒問題,當再次調用lock()的時候,因為自旋鎖的持有引用已經不為空了,該線程對象會誤認為是別人的線程持有了自旋鎖

使用了CAS原子操作,lock函數將owner設置為當前線程,並且預測原來的值為空。unlock函數將owner設置為null,並且預測值為當前線程。

當有第二個線程調用lock操作時由於owner值不為空,導致循環一直被執行,直至第一個線程調用unlock函數將owner設置為null,第二個線程才能進入臨界區。

由於自旋鎖只是將當前線程不停地執行循環體,不進行線程狀態的改變,所以響應速度更快。但當線程數不停增加時,性能下降明顯,因為每個線程都需要執行,占用CPU時間。如果線程競爭不激烈,並且保持鎖的時間段。適合使用自旋鎖。

分布式鎖:

如果想在不同的jvm中保證數據同步,使用分布式鎖技術。

有數據庫實現、緩存實現、Zookeeper分布式鎖


免責聲明!

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



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