【JUC并发】StampedLock

文章目录

StampedLock

是什么?

​ 源码中的解释是:

​ 一种基于功能的锁,具有三种模式来控制读写访问。 StampedLock的状态由版本和模式组成。锁获取方法返回一个表示并控制相对于锁状态的访问的stamp;这些方法的“尝试”版本可能会返回特殊值零,以表示无法获取访问权限。锁释放和转换方法需要使用图章作为参数,如果它们与锁的状态不匹配,则会失败。

​ 三种模式分别是:写锁、读锁、乐观读(可以升级的锁)。

​ 注意:是一种不可重入的锁。

​ 在读多写少的场景下可以提高吞吐量

结构

类图

【JUC并发】StampedLock

组成

一个等待队列

/** 等待节点队列 */
static final class WNode {
    volatile WNode prev;
    volatile WNode next;
    // 链接的读者列表
    volatile WNode cowait;    // list of linked readers
    // 可能停放时为非空
    volatile Thread thread;   // non-null while possibly parked
    volatile int status;      // 0, WAITING, or CANCELLED
    final int mode;           // RMODE or WMODE
    WNode(int m, WNode p) { mode = m; prev = p; }
}

/** Head of CLH queue */
private transient volatile WNode whead;
/** Tail (last) of CLH queue */
private transient volatile WNode wtail;

一个读锁

// 读锁
final class ReadLockView implements Lock {
    public void lock() { readLock(); }
    public void lockInterruptibly() throws InterruptedException {
        readLockInterruptibly();
    }
    public boolean tryLock() { return tryReadLock() != 0L; }
    public boolean tryLock(long time, TimeUnit unit)
        throws InterruptedException {
        return tryReadLock(time, unit) != 0L;
    }
    public void unlock() { unstampedUnlockRead(); }
    public Condition newCondition() {
        throw new UnsupportedOperationException();
    }
}

一个写锁

// 写锁
final class WriteLockView implements Lock {
    public void lock() { writeLock(); }
    public void lockInterruptibly() throws InterruptedException {
        writeLockInterruptibly();
    }
    public boolean tryLock() { return tryWriteLock() != 0L; }
    public boolean tryLock(long time, TimeUnit unit)
        throws InterruptedException {
        return tryWriteLock(time, unit) != 0L;
    }
    public void unlock() { unstampedUnlockWrite(); }
    public Condition newCondition() {
        throw new UnsupportedOperationException();
    }
}

一个读写锁

// 读写锁
final class ReadWriteLockView implements ReadWriteLock {
    public Lock readLock() { return asReadLock(); }
    public Lock writeLock() { return asWriteLock(); }
}

Unsafe类

// Unsafe mechanics
private static final sun.misc.Unsafe U;
private static final long STATE;
private static final long WHEAD;
private static final long WTAIL;
private static final long WNEXT;
private static final long WSTATUS;
private static final long WCOWAIT;
private static final long PARKBLOCKER;

static {
    try {
        // 获取Unsafe类的字段值
        U = sun.misc.Unsafe.getUnsafe();
        Class<?> k = StampedLock.class;
        Class<?> wk = WNode.class;
        STATE = U.objectFieldOffset
            (k.getDeclaredField("state"));
        WHEAD = U.objectFieldOffset
            (k.getDeclaredField("whead"));
        WTAIL = U.objectFieldOffset
            (k.getDeclaredField("wtail"));
        WSTATUS = U.objectFieldOffset
            (wk.getDeclaredField("status"));
        WNEXT = U.objectFieldOffset
            (wk.getDeclaredField("next"));
        WCOWAIT = U.objectFieldOffset
            (wk.getDeclaredField("cowait"));
        Class<?> tk = Thread.class;
        PARKBLOCKER = U.objectFieldOffset
            (tk.getDeclaredField("parkBlocker"));

    } catch (Exception e) {
        throw new Error(e);
    }
}

和其他的一些变量。

/** 获取当前计算机的CPU核数 */
private static final int NCPU = Runtime.getRuntime().availableProcessors();

/** Maximum number of retries before enqueuing on acquisition */
// 64:0
private static final int SPINS = (NCPU > 1) ? 1 << 6 : 0;

/** Maximum number of retries before blocking at head on acquisition */
// 1024:0
private static final int HEAD_SPINS = (NCPU > 1) ? 1 << 10 : 0;

/** Maximum number of retries before re-blocking */
// 65536:0
private static final int MAX_HEAD_SPINS = (NCPU > 1) ? 1 << 16 : 0;

/** 自旋锁溢出yield的时间 */
private static final int OVERFLOW_YIELD_RATE = 7; // must be power 2 - 1

/** The number of bits to use for reader count before overflowing */
private static final int LG_READERS = 7;

// Values for lock state and stamp operations
// 锁定状态和标记操作的值
private static final long RUNIT = 1L;
// 128
private static final long WBIT  = 1L << LG_READERS;
// 127
private static final long RBITS = WBIT - 1L;
// 126
private static final long RFULL = RBITS - 1L;
// 255
private static final long ABITS = RBITS | WBIT;
// ~取反运算符
private static final long SBITS = ~RBITS; // note overlap with ABITS

// 锁定状态的初始值;避免故障值为零
// 256
private static final long ORIGIN = WBIT << 1;

// Special value from cancelled acquire methods so caller can throw IE
// 取消获取方法中的特殊值,因此调用方可以抛出IE
private static final long INTERRUPTED = 1L;

// Values for node status; order matters
private static final int WAITING   = -1;
private static final int CANCELLED =  1;

// Modes for nodes (int not boolean to allow arithmetic)
// 节点的模式(不为布尔值以允许算术)
private static final int RMODE = 0;
private static final int WMODE = 1;

原理

【JUC并发】StampedLock

​ 其中相较于AQS多的一个cowait节点是读模式使用该节点的链表。thread是挂起的线程。

进来一个线程:如果队尾是读节点,不会连接到队尾,而是读节点的cowait()节点;如果队尾是写节点,直接连接到队尾。

​ **唤醒线程:**唤醒队首节点,如果是读节点,cowait也会被唤醒。

案例

public class StampedLockTest {
    public static void main(String[] args) {

        StampedLockTest lockTest = new StampedLockTest();

        //new Thread(()->{
        //    for (int i = 0; i < 20; i++) {
        //
        //        System.out.println("read1:"+lockTest.distanceFromOrigin());
        //
        //    }
        //},"read1").start();

        new Thread(()->{
            for (double i = 1; i <= 10; i++) {
                lockTest.moveIfAtOrigin(i,i);
            }
        },"升级锁").start();

        //new Thread(()->{
        //    for (int i = 0; i < 20; i++) {
        //        try {
        //            System.out.println("read2:"+lockTest.distanceFromOrigin());
        //        } catch (InterruptedException e) {
        //            e.printStackTrace();
        //        }
        //    }
        //},"read2").start();



       new Thread(()->{
            for (int i = 0; i < 2000000000; i++) {
                lockTest.move(-1,-1);
            }
        },"write1").start();

        new Thread(()->{
            for (int i = 0; i < 2000000000; i++) {
                lockTest.move(-1,-1);
            }
        },"write2").start();
    }

    private double x, y;
    private final StampedLock sl = new StampedLock();

    void move(double deltaX, double deltaY) {
         long stamp = sl.writeLock();
        System.out.println("写锁");
         try {
            x += deltaX;
            y += deltaY;
         }finally {
            sl.unlockWrite(stamp);
         }
    }

    /**
     * 共享读锁
     * 可以被写锁中断
     */
    double distanceFromOrigin() { // A read-only method
         long stamp = sl.tryOptimisticRead();
         double currentX = x, currentY = y;
         //
         if (!sl.validate(stamp)) {
             stamp = sl.readLock();
              try {
                 currentX = x;
                 currentY = y;

             } finally {
                 sl.unlockRead(stamp);
             }
         }
         return Math.sqrt(currentX * currentX + currentY * currentY);
    }

    /**
     * 满足条件就会升级锁
     */
    void moveIfAtOrigin(double newX, double newY) {
        
        long stamp = sl.readLock();
        try {
            System.out.println(x+"----"+y);
            // 只有当变量满足这个条件时,才会升级为写锁
            while (x == 0.0 && y == 0.0) {
                // 尝试升级为写锁
                long ws = sl.tryConvertToWriteLock(stamp);
                if (ws != 0L) {
                    stamp = ws;
                    x = newX;
                    y = newY;
                    break;
                }
                else {
                    sl.unlockRead(stamp);
                    stamp = sl.writeLock();
                }
            }
        } finally {
            sl.unlock(stamp);
        }
   }
}

图片转自:https://www.jianshu.com/p/bfd5d2321cc0

上一篇:【Java集合系列】---ArrayList


下一篇:Python时间戳和时间元祖的转换