之前的文章中简单的为大家介绍了重入锁JAVA并发之多线程基础(2)。这里面也是简单的为大家介绍了重入锁的几种性质,这里我们就去探索下里面是如何实现的。

我们知道在使用的时候,必须锁先有定义,然后我们再拿着当前的锁进行加锁操作,然后处理业务,最后是释放锁的操作(这里就拿里面非公平锁的实现来讲解)。

字节码操作

public class com.montos.lock.ReentrantLockDemo implements java.lang.Runnable {

public static java.util.concurrent.locks.ReentrantLock lock;

public static int k;

public com.montos.lock.ReentrantLockDemo();

Code:

0: aload_0

1: invokespecial #1 // Method java/lang/Object."":()V

4: return

public void run();

Code:

0: iconst_0

1: istore_1

2: iload_1

3: sipush 1000

6: if_icmpge 29 //int类型的值进行栈顶比较

9: getstatic #2 // Field lock:Ljava/util/concurrent/locks/ReentrantLock;

12: invokevirtual #3 // Method java/util/concurrent/locks/ReentrantLock.lock:()V

15: getstatic #4 // Field k:I

18: iconst_1

19: iadd

20: putstatic #4 // Field k:I

23: iinc 1, 1

26: goto 2

29: iconst_0

30: istore_1

31: iload_1

32: sipush 1000

35: if_icmpge 50

38: getstatic #2 // Field lock:Ljava/util/concurrent/locks/ReentrantLock;

41: invokevirtual #5 // Method java/util/concurrent/locks/ReentrantLock.unlock:()V

44: iinc 1, 1

47: goto 31

50: return

public static void main(java.lang.String[]) throws java.lang.InterruptedException;

Code:

0: new #6 // class com/montos/lock/ReentrantLockDemo

3: dup

4: invokespecial #7 // Method "":()V

7: astore_1

8: new #8 // class java/lang/Thread

11: dup

12: aload_1

13: invokespecial #9 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V

16: astore_2

17: new #8 // class java/lang/Thread

20: dup

21: aload_1

22: invokespecial #9 // Method java/lang/Thread."":(Ljava/lang/Runnable;)V

25: astore_3

26: aload_2

27: invokevirtual #10 // Method java/lang/Thread.start:()V

30: aload_3

31: invokevirtual #10 // Method java/lang/Thread.start:()V

34: aload_2

35: invokevirtual #11 // Method java/lang/Thread.join:()V

38: aload_3

39: invokevirtual #11 // Method java/lang/Thread.join:()V

42: getstatic #12 // Field java/lang/System.out:Ljava/io/PrintStream;

45: getstatic #4 // Field k:I

48: invokevirtual #13 // Method java/io/PrintStream.println:(I)V

51: return

static {};

Code:

0: new #14 // class java/util/concurrent/locks/ReentrantLock

3: dup

4: invokespecial #15 // Method java/util/concurrent/locks/ReentrantLock."":()V

7: putstatic #2 // Field lock:Ljava/util/concurrent/locks/ReentrantLock;

10: iconst_0

11: putstatic #4 // Field k:I

14: return

}

复制代码

这里面无非就是入栈,栈元素比较,出栈放入变量中这些操作,没有之前的synchronized里面的监视器相关指令限制,只是简单的一些栈操作。

加锁操作

final void lock(){

if (compareAndSetState(0, 1)) //将同步状态从0变成1 采用cas进行更新

setExclusiveOwnerThread(Thread.currentThread());//设置当前拥有独占访问权的线程。

else

acquire(1);//没有获取到锁,则进行尝试操作

}

复制代码

往下面的选择走:

public final void acquire(int arg){

//先进行再次尝试获取锁的操作,如果获取失败则将当前加入队列中,并设置中断标志。

if (!tryAcquire(arg) &&

acquireQueued(addWaiter(Node.EXCLUSIVE), arg))

selfInterrupt();

}

复制代码

首先走尝试获取锁的操作(这里还是走非公平锁的):

final boolean nonfairTryAcquire(int acquires){

final Thread current = Thread.currentThread();//拿到当前线程

int c = getState();//同步状态

if (c == 0) {//再次做获取锁的操作

if (compareAndSetState(0, acquires)) {

setExclusiveOwnerThread(current);

return true;

}

}

else if (current == getExclusiveOwnerThread()) {//是否是当前线程已经占有

int nextc = c + acquires;//原本的状态数值+当前传入数值

if (nextc < 0) // overflow

throw new Error("Maximum lock count exceeded");

setState(nextc);//设置新的状态

return true;

}

return false;

}

复制代码

接着往下走:

private Node addWaiter(Node mode){

//独占模式进行封装当前线程

Node node = new Node(Thread.currentThread(), mode);

Node pred = tail;

if (pred != null) {如果尾节点不为null,将当前的节点接入并返回

node.prev = pred;

if (compareAndSetTail(pred, node)) {

pred.next = node;

return node;

}

}

enq(node);

return node;

}

复制代码

继续往下走:

private Node enq(final Node node){

for (;;) {//

Node t = tail;

if (t == null) { // 初始化尾节点

if (compareAndSetHead(new Node()))

tail = head;

} else {

node.prev = t;

if (compareAndSetTail(t, node)) {//尾节点与当前的节点互换

t.next = node;

return t;//返回当前节点

}

}

}

}

复制代码

接着回去往下走:

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; // 手动清除引用 帮助GC

failed = false;

return interrupted;

}

//检测获取锁失败的节点状态 以及暂时挂起并返回当前的中断标志

if (shouldParkAfterFailedAcquire(p, node) &&

parkAndCheckInterrupt())

interrupted = true;

}

} finally {

if (failed)

cancelAcquire(node);//取消正在进行的获取尝试。

}

}

复制代码

说真的,咱们直接看失败的情况,我们接着往下走:

private static boolean shouldParkAfterFailedAcquire(Node pred, Node node){

//检查和更新无法获取的节点的状态。

int ws = pred.waitStatus;

if (ws == Node.SIGNAL)

//该节点已经设置了请求释放信号状态,所以可以进行安全挂起

return true;

if (ws > 0) {

do {//清除不需要执行的节点

node.prev = pred = pred.prev;

} while (pred.waitStatus > 0);

pred.next = node;

} else {

//waitstatus必须为0或传播。表明我们需要信号,但不要挂起。调用者重试以确保在挂起前无法获取。

compareAndSetWaitStatus(pred, ws, Node.SIGNAL);

}

return false;

}

复制代码

然后看向下一个方法:

private final boolean parkAndCheckInterrupt(){

LockSupport.park(this);//挂起当前线程

return Thread.interrupted();//返回中断标识

}

复制代码

上面的取消获取队列里面的节点就不看了..cancelAcquire(node),里面就是取消正在进行的获取尝试。同时将无需的节点移除。当上面的操作走完之后就设置当前线程中断标识。这里面主要流程是说如果加锁不成功之后,对于当前线程是怎么执行操作的,我们可以看到,里面的方法中大部分在获取不到锁之后,下一步操作中会再次尝试获取下,如果获取不到才会继续执行,获取到了我们就可以直接使用,这里也是多线程操作里面的魅力,每一个空隙中就可能会让当前线程进行获得锁的操作。

释放锁操作

释放锁的步骤就简单许多了:

public final boolean release(int arg){

if (tryRelease(arg)) {//尝试释放锁

Node h = head;

if (h != null && h.waitStatus != 0)

unparkSuccessor(h);//唤醒节点的后续节点

return true;

}

return false;

}

复制代码

咱们继续往下看:

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;//只有当前重入次数为0,才能返回true

setExclusiveOwnerThread(null);//当前独占线程设为NULL

}

setState(c);//重新设置同步状态

return free;

}

复制代码

然后往下走:

private void unparkSuccessor(Node node){

//当前状态为负数,则尝试清除当前的线程状态

int ws = node.waitStatus;

if (ws < 0)

compareAndSetWaitStatus(node, ws, 0);

//清除取消或无效的节点,从尾部向后移动以找到实际节点

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);//释放当前线程

}

复制代码从上面的顺序往下面来看,我们主要发现线程在拿锁阶段是有许多的操作的,要根据线程的状态再将线程从等待队列中移除。释放的时候就显得简洁了许多,我们只需要看到当前线程的状态-1,然后看看是否是重入的。

我们通过一个简单的重入锁代码可以看到,作者在用无锁的操作去获得锁,这个整体的步骤里面考虑的东西很多,每一个时刻,线程都有可能千变万化,我们需要了解的是我们每一个步骤都需要可能发生的情况。如果能够考虑到发生的情况,那么有些步骤就可以直接跳过,我们直接就可以获得最后的结果(这块在线程尝试获锁的阶段可以体现)。有小伙伴对于重入锁还有什么看法的可以在下面进行留言,我们可以相互学习,共同进步~

java重入锁,再探JAVA重入锁相关推荐

  1. Java Web基础入门第九讲 Java Web开发入门——再探Tomcat服务器

    web应用程序 web应用程序指供浏览器访问的程序,通常也简称为web应用.例如有a.html.b.html--多个web资源,这多个web资源用于对外提供邮件服务,此时应把这多个web资源放在一个目 ...

  2. java定义一个door的类_再探Java抽象类与接口的设计理念差异

    原文:http://blog.csdn.net/sunboard/article/details/3831823 1.概述 一个软件设计的好坏,我想很大程度上取决于它的整体架构,而这个整体架构其实就是 ...

  3. 再探Java抽象类与接口的设计理念差异

    Java抽象类与接口都可以实现功能与实现的分离,都对多态提供了很好的支持,那么我们什么时候应该使用抽象类或接口呢?在以前的一篇文章初探Java抽象类与接口中谈到了他们语法的区别,在博客通过模板方法模式 ...

  4. 再探java基础——对面向对象的理解(1)

    对象 对象是人们要进行研究的任何事物,从最简单的整数到复杂的飞机等均可看作对象,它不仅能表示具体的事物,还能表示抽象的规则.计划或事件.对象具有属性和行为,在程序设计中对象实现了数据和操作的结合,使数 ...

  5. java归还线程_再谈java线程

    什么是等待唤醒机制? 这是多个线程间的一种协作机制. 就是一个线程进行规定协作后,就进入到了等待状态'wait()',等待其他线程执行完他们的指定代码后,再将其唤醒'notify()'; 在有多个线程 ...

  6. 从 Java 到 Kotlin,再到 Java 的无奈感叹,那么 Kotlin 现状究竟如何?

    5 年前的 2017 Google I/O 大会上,谷歌宣布:官方正式支持将 Kotlin 作为 Android 开发的 First-Class(一等公民)语言.自此,Kotlin 开发商 JetBr ...

  7. 合工大JAVA实验四web_合工大JAVA实验报告.doc

    专业整理 WORD完美格式 <Java技术>实验报告 实验一: 2016 年10 月 11 日 学院 计算机与信息学院 专业班级 姓名 成绩 课程 名称 Java技术 实验项目 名 称 实 ...

  8. java中多线程reentlock_Java多线程系列——深入重入锁ReentrantLock

    简述 ReentrantLock 是一个可重入的互斥(/独占)锁,又称为"独占锁". ReentrantLock通过自定义队列同步器(AQS-AbstractQueuedSychr ...

  9. Java中的锁机制 -- 乐观锁、悲观锁、自旋锁、可重入锁、读写锁、公平锁、非公平锁、共享锁、独占锁、重量级锁、轻量级锁、偏向锁、分段锁、互斥锁、同步锁、死锁、锁粗化、锁消除

    文章目录 1. Java中的锁机制 1.1 乐观锁 1.2 悲观锁 1.3 自旋锁 1.4 可重入锁(递归锁) 1.5 读写锁 1.6 公平锁 1.7 非公平锁 1.8 共享锁 1.9 独占锁 1.1 ...

最新文章

  1. vmware虚拟机怎么让窗口自动调整大小适应主机
  2. CSharpGL(19)用glReadPixels把渲染的内容保存为PNG图片(C#)
  3. 微信小程序日期选择器
  4. BZOJ1016:[JSOI2008]最小生成树计数——题解
  5. C++Vector使用方法
  6. [react] react中的key有什么作用?
  7. 韩国财长:韩国将按计划推进加密货币征税
  8. cobbler批量安装linux
  9. 2022华为软件精英挑战赛
  10. 根升余弦滤波器——MATLAB
  11. 详谈AI芯片架构、分类和关键技术
  12. 百度“哼唱”音乐搜索
  13. html 环形图圆角,canvas绘制圆角环形图
  14. 使用高德地图根据坐标点画出路线
  15. SegmentFault 思否发布开源问答社区软件 Answer
  16. github优秀项目分享 redis客户端
  17. linux删除每行首字符,vim技巧:删除行首、行末的空白字符,删除空白行
  18. i am freshman
  19. linux 动态库 软链接,Linux操作系统下动态库的生成及链接方法
  20. DIV布局——人电影网站(5页) HTML+CSS+JavaScript 学生DW网页设计作业成品

热门文章

  1. Mac OS 被XCode搞到无法正常开机怎么办?
  2. 14.并发容器之ConcurrentHashMap(JDK 1.8版本)
  3. ie8 ajaxSubmit 上传文件提示下载
  4. 同一服务器部署多个tomcat时的端口号修改详情
  5. 清除Linux终端命令的历史记录
  6. Android学习笔记:TabHost 和 FragmentTabHost
  7. SAP里删除trace文件的方法
  8. myeclipse8.5安装反编译工具
  9. Linux下Socket网络编程
  10. 谈CRM产品设计的指导思想