IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> ReentrantLock源码解析 -> 正文阅读

[游戏开发]ReentrantLock源码解析

ReentrantLock源码解析

1.首先我们来分析 ReentrantLock ,首先看构造函数

 // 是否创建公平锁
 public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();  
 }
 
// 我们来看看 FairSync 和 NoFairSync() 都死继承 class sync
// lock方法其实是调用  sync.lock()  NonfairSync 是ReentranLock的内部实现,我们先来看看NoFairSync()

static final class NonfairSync extends Sync {
        private static final long serialVersionUID = 7316153563782823691L;

        /**
         * Performs lock.  Try immediate barge, backing up to normal
         * acquire on failure.
         */
        final void lock() { 
         
            if (compareAndSetState(0, 1)) //抢锁机制 只有状态 :0 cas修改状态拿到锁。。
               //设置 exclusiveOwnerThread 判断当前线程是否被抢占,和哪个线程抢占
                setExclusiveOwnerThread(Thread.currentThread()); //设置该线程为拿到锁的线程
            else
                acquire(1);  //公平🔒不经过上面判断直接走这 
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
}

从上面可以知道ReentrantLock.lock()最核心走的就是走(AbstractQueuedSynchronizer)sync.acquire(1),接下来分析他

public final void acquire(int arg) {
        if (!tryAcquire(arg) && acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
            selfInterrupt();  // LockSupport.lock() 可 以被intteruted()和LockSupport.unlock()解开
  					// 如果是被 如果是被intterute打断会被intteruted()清楚打断,这里重新设置
}

// 首先走tryAcquire(arg),noFair 和 Fair都要实现  tryAcquire(int acquires) 先看非公平的实现
protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
}

// 非公平🔒直接走 sync 的下面这个方法 走重入功能
final boolean nonfairTryAcquire(int acquires) {
    final Thread current = Thread.currentThread();  // 拿到当前线程
    int c = getState();
    if (c == 0) {  // 无锁状态 CAS 拿到锁
      if (compareAndSetState(0, acquires)) {
        setExclusiveOwnerThread(current);
        return true;
      }
    }
    else if (current == getExclusiveOwnerThread()) { // 如果有锁判断锁是不是当前锁如果是就状态+1,重入
      int nextc = c + acquires;
      if (nextc < 0) // overflow 超过Ineter.MAX_VAL  就是 -1
        throw new Error("Maximum lock count exceeded");
      setState(nextc); // 设置状态 
      return true;
    }
    return false;
}

之后这里我们懂了 如果返回了false没有抢到锁,也没有执行重入那么我们就先走addWaiter()方法。分析下他

// 想也知道如果没有锁,将当前的线程弄进去一个队列里的队尾,让他一直等待 tail 尾节点
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)) { // cas将tail置为node
                pred.next = node; //双向链表将原本的尾节点的后指针node,完成加节点
                return node;
            }
        }
  		 	// 这里是只有第一个node才会走的地方
        enq(node);
        return node;
}
// 之后我们看AQS的enq方法
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)) {
            return t; 
        }
      }
    }
}

从上述可以看出这里返回了队列的最后一个的node,之后走acquire的最后一个方法acquireQueued

// node:构建node arg:1
final boolean acquireQueued(final Node node, int arg) {
        boolean failed = true;
        try {
            boolean interrupted = false;
            for (;;) {
                final Node p = node.predecessor(); // 获得node的前置节点
                if (p == head && tryAcquire(arg)) { // 如果这个是第一个节点,去试试抢锁,如果抢锁失败走后面
                    setHead(node);  // 这里就是解锁了的流程, parkAndCheckInterrupt())一直阻塞之后,到该线程可以执行了
                    p.next = null; // help GC
                    failed = false;
                    return interrupted;
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    interrupted = true;
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }


// 这里开看下 shouldParkAfterFailedAcquire(p, node)  ,获取锁之后判断是否可以执行等待操作
private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * This node has already set status asking a release
             * to signal it, so it can safely park.
             */
            return true;
        if (ws > 0) {  // ws > 0 的状态 只有CANCEL状态,一处这个节点
            /*
             * Predecessor was cancelled. Skip over predecessors and
             * indicate retry.
             */
            do {
                node.prev = pred = pred.prev; // 清理这个节点
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * waitStatus must be 0 or PROPAGATE.  Indicate that we
             * need a signal, but don't park yet.  Caller will need to
             * retry to make sure it cannot acquire before parking.
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL); // 将现在过来的设点设置为singal
        }
        return false;
    }

private final boolean parkAndCheckInterrupt() {
        LockSupport.park(this); // 一直阻塞,可以被interrupte()打断
        return Thread.interrupted();
    }

从这里就讲完了lock这个逻辑,之后我们看看解锁流程 sync.unlock()

public void unlock() {
    sync.release(1);
}
// 执行释放锁的逻辑 我们来看AQS的release()方法
public final boolean release(int arg) {
    if (tryRelease(arg)) {
      Node h = head; // 如果解锁成功,之后我们这里拿到,最先阻塞的头节点,unparkSuccessor(h) 看后面的解析
      if (h != null && h.waitStatus != 0)
        unparkSuccessor(h);
      return true;
    }
    return false;
}
//我们先来看看tryRelease()方法
 protected final boolean tryRelease(int releases) {
     int c = getState() - releases; // 重入锁
     if (Thread.currentThread() != getExclusiveOwnerThread()) // 不是当前线程肯定不能释放锁啊
       throw new IllegalMonitorStateException();
     boolean free = false;
     if (c == 0) {
       free = true;
       setExclusiveOwnerThread(null);
     }
     setState(c);  // 如果当前状态  ==0 无锁了,设置当前独占锁为null ,之后返回解锁成功
     return free;
}

 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) // 如果ws不是CANCEL状态将ws状态设置为0,头节点默认就是0,前面new的空
            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; // 从后往前找到一个正常的节点,<0或者=0的,其他节点设置为空
        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);
  }

从这里就讲完了ReentrantLock的加锁解锁过程

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2022-04-07 23:02:30  更:2022-04-07 23:04:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/16 21:02:17-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码