天天看點

Java鎖的設計

1、自旋鎖

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

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

public

class

SpinLock {

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

);

}

}

使用了CAS原子操作,lock函數将owner設定為目前線程,并且預測原來的值為空。unlock函數将owner設定為null,并且預測值為目前線程。

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

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

注:該例子為非公平鎖,獲得鎖的先後順序,不會按照進入lock的先後順序進行。

在自旋鎖中 另有三種常見的鎖形式:TicketLock ,CLHlock 和MCSlock

二、阻塞鎖

阻塞鎖,與自旋鎖不同,改變了線程的運作狀态。

在JAVA環境中,線程Thread有如下幾個狀态:

1,建立狀态

2,就緒狀态

3,運作狀态

4,阻塞狀态

5,死亡狀态

阻塞鎖,可以說是讓線程進入阻塞狀态進行等待,當獲得相應的信号(喚醒,時間) 時,才可以進入線程的準備就緒狀态,準備就緒狀态的所有線程,通過競争,進入運作狀态。

JAVA中,能夠進入\退出、阻塞狀态或包含阻塞鎖的方法有

,synchronized

關鍵字(其中的重量鎖),ReentrantLock,Object.wait()\notify(),LockSupport.park()/unpart()(j.u.c經常使用)

Java鎖的設計
package lock;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
import java.util.concurrent.locks.LockSupport;

public class CLHLock1 {
    public static class CLHNode {
        private volatile Thread isLocked;
    }

    @SuppressWarnings("unused")
    private volatile CLHNode                                            tail;
    private static final ThreadLocal<CLHNode>                           LOCAL   = new ThreadLocal<CLHNode>();
    private static final AtomicReferenceFieldUpdater<CLHLock1, CLHNode> UPDATER = AtomicReferenceFieldUpdater.newUpdater(CLHLock1.class,
                                                                                    CLHNode.class, "tail");

    public void lock() {
        CLHNode node = new CLHNode();
        LOCAL.set(node);
        CLHNode preNode = UPDATER.getAndSet(this, node);
        if (preNode != null) {
            preNode.isLocked = Thread.currentThread();
            LockSupport.park(this);
            preNode = null;
            LOCAL.set(node);
        }
    }

    public void unlock() {
        CLHNode node = LOCAL.get();
        if (!UPDATER.compareAndSet(this, node, null)) {
            System.out.println("unlock\t" + node.isLocked.getName());
            LockSupport.unpark(node.isLocked);
        }
        node = null;
    }
}      
Java鎖的設計

在這裡我們使用了LockSupport.unpark()的阻塞鎖。 該例子是将CLH鎖修改而成。

阻塞鎖的優勢在于,阻塞的線程不會占用cpu時間, 不會導緻 CPu占用率過高,但進入時間以及恢複時間都要比自旋鎖略慢。

在競争激烈的情況下 阻塞鎖的性能要明顯高于 自旋鎖。

理想的情況則是; 線上程競争不激烈的情況下,使用自旋鎖,競争激烈的情況下使用,阻塞鎖。