Java并发编程-ReentrantLock原理

Java并发编程-ReentrantLock原理

api使用

 static   ReentrantLock lock = new ReentrantLock(true);//true 公平锁,false非公平锁
    public static void main(String[] args) {
        new Thread(new Runnable() {
            public void run() {
                lock.lock(); //手动加锁
                todo();//执行任务
                lock.unlock();//手动解锁
            }
        }).start();
    }

    public static void todo(){
        System.out.println("--------------todo------");
    }

AQS(同步器)原理:
通过独占锁来分析同步器原理。
AQS:队列同步器用例构建锁。使用的是一个int成员变量,来表示同步状态,查看AbstractQueuedSynchronizer类的源码如下:
在这里插入图片描述
getState() 获取当前同步状态
setState() 设置当前同步状态
compareAndSetState() 使用CAS设置当前状态,改方法能够保证状态设置的原子性。

同步队列的基本结构:

没有成功获取同步状态的线程会成为节点加入该队列的队尾。同步队列的基本结构:
在这里插入图片描述
如图所示:同步队列基本结构包含了两个节点类型引用,一个指向头节点,另一个指向尾节点。
假设当一个线程成功获取了同步状态,其他线程无法获取到同步状态,会被构建成节点并加入同步队列中,而这个加入队列过程中必须保证线程安全,因此同步器提供了基于CAS的设置节点方法:compareAndSetTail(Node expect, Node update)。
在这里插入图片描述

static final class Node { //节点类
 volatile int waitStatus;//
  volatile Node prev;
  volatile Node next;
  volatile Thread thread;
  Node nextWaiter;
  }

如下代码:重要是通过tryAcquire方法获取同步状态,如果获取失败则构造同步节点,并通过addWaiter方法加入同步队列队尾,最后通过acquireQueued方法使得该节点已“死循环”的方式获取同步状态。

   此代码重要是是否能获取同步锁,如果获取失败则加入同步队列尾部,(同步锁获取,节点构造,加入同步队列,在同步队列中自旋)
   public final void acquire(int arg) {
        if (!tryAcquire(arg) && // tryAcquire 获取同步状态
            acquireQueued(addWaiter(Node.EXCLUSIVE), arg))//Node.EXCLUSIVE代表独占锁
            selfInterrupt();
    }

 private Node addWaiter(Node mode) { //加入同步节点到尾部
        Node node = new Node(Thread.currentThread(), mode); //构造一个节点
        // Try the fast path of enq; backup to full enq on failure
        Node pred = tail;
        if (pred != null) {
            node.prev = pred;
            if (compareAndSetTail(pred, node)) {//使用compareAndSetTail保证加入同步队列
                pred.next = node;
                return node;
            }
        }
        enq(node);//通过“死循环”来保证节点正确添加
        return node;
    }

//通过acquireQueued方法使该节点已“死循环”的方式获取同步状态。如果获取不到则阻塞节点的线程,而被阻塞线程的唤醒重要依靠前驱节点出队或阻塞节点被中断实现的。(自旋)

  final boolean acquireQueued(final Node node, int arg) {
            boolean failed = true;
            try {
                boolean interrupted = false;
                for (;;) {
                    final Node p = node.predecessor();
                    if (p == head && tryAcquire(arg)) {
                        setHead(node);//设置首节点
                        p.next = null; // help GC
                        failed = false;
                        return interrupted;
                    }
                    if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt()) //加锁
                        interrupted = true;
                }
            } finally {
                if (failed)
                    cancelAcquire(node);
            }
        }
 private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this);//阻塞当前线程,
        return Thread.interrupted();
    }
    //
 private Node enq(final Node node) {
            for (;;) {//通过“死循环”来保证节点正确添加,
                Node t = tail;
                if (t == null) { // Must initialize
                    if (compareAndSetHead(new Node()))
                        tail = head;
                } else {
                    node.prev = t;
                    if (compareAndSetTail(t, node)) {
                        t.next = node;
                        return t;
                    }
                }
            }
        }

同步器将节点加到同步队列的过程如图:节点加入到同步队列
在这里插入图片描述
同步队列遵循FIFO(先进先出),首节点是获取同步成功的节点,首节点在释放同步状态时,将会唤醒后继节点,而后继节点在获取同步状态成功时,并设置为首节点。该过程如下:首节点的设置
在这里插入图片描述
如图所示:设置首节点是通过获取同步状态成功的线程来完成的,需要将首节点设置为原节点的后继节点并断开首节点的next的引用即可。

如图所示:节点自旋获取同步状态:acquireQueued
在这里插入图片描述
在这里插入图片描述

加锁过程
在这里插入图片描述
总结:
在这里插入图片描述
解锁过程

public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)//队尾0 其他为-1
            unparkSuccessor(h); //叫醒其他的节点
        return true;
    }
    return false;
}

顶顶顶顶顶顶顶顶顶

 private void unparkSuccessor(Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);//唤醒处于阻塞状态的线程thread
    }


public static void unpark(Thread thread) {
    if (thread != null)
        UNSAFE.unpark(thread);
}

public native void unpark(Object var1); //使用os底层的指令。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值