# jdk-study **Repository Path**: cllyl/jdk-study ## Basic Information - **Project Name**: jdk-study - **Description**: No description available - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-05-16 - **Last Updated**: 2021-05-16 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 10-2模块作业习题 ## 1. 什么是CAS CAS是Compare And Set,比较然后修改的原子性操作。 CAS是Compare And Swap,比较然后交换的原子性操作。 JMM(Java Memory Model)中在并发编程下需要保证三件事情。并发编程最大的难点就在于对临界区资源的操作。 三个基本原则 **原子性**:操作要么成功,要么失败,不会出现中间状态。一旦开始,不会受其它线程的影响。 **有序性**:happen-before原则 - 指令重排 - 取指IF - 译码和取寄存器操作数ID - 执行或者有效地址计算EX - 存储器访问MEM - 写回 WB - Happen-Before原则 - 线程内串行语义的一致性。但是不能保证多线程下语义的一致性 - volatile规则,写优先于读,保证可见性 - 锁规则,unlock必须发生在随后的lock前 - 传递性。A先于B,B先于C。那么A先于C - 线程的start方法先于它的每一个动作 - 线程的所有操作先于线程的终结(Thread.join()) - 线程的中断(interupt())先于被中断线程的代码 - 对象构造方法的执行、结束必须先于finalize()方法 - 对final变量的写,先于final变量的读 **可见性**:其它线程修改了临界区资源,其它线程可以感知到。 - 编译器优化 - 缓存优化 - 硬件优化 - 指令重排 上述都有可能造成可见性问题。 因为临界区资源是共享,所以对临界区的访问需要进行加锁。 悲观锁:先获取锁,然后再对资源进行操作 乐观锁:直接对资源进行操作,遇到冲突的情况下进行重试 CAS就是乐观锁的实现。 #### 说明 JDK中一般跟并发相关的操作都在rt包下面的juc(java.util.concurrent)包中。 原子性操作的基本都在juca(java.util.concurrent.atomic)包中. 已AtomicInteger为例 ```java // setup to use Unsafe.compareAndSwapInt for updates // java中的指针用来进行CAS操作 private static final Unsafe unsafe = Unsafe.getUnsafe(); // 保存当前对象在操作系统的偏移量 private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } } // volatile保证可见性 private volatile int value; ``` ```java /** * Atomically sets the value to the given updated value * if the current value {@code ==} the expected value. * * @param expect the expected value * @param update the new value * @return {@code true} if successful. False return indicates that * the actual value was not equal to the expected value. */ // 当前值等于期望值,就修改。调用unsafe中的方法进行修改 public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); } ``` ````java package sun.misc // 不能被继承,不能修改。相当于指针。直接对内存进行操作。有C和c++编写 public final class Unsafe{ public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5); } ```` ```java /* 根据java中的类加载器原理。应用程序的类是由Application ClassLoader进行加载的。系统核心类rt.jar等中的类是由Bootstrap类加载器加载的。Bootstrap类加载器是没有java对象的对象,所以试图获取Bootstrap类加载器就是null。可以正常使用unsafe类。 */ @CallerSensitive public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } } ``` #### 使用场景 假设对临界区的访问是没有冲突的,对临界资源的无锁修改操作,一般会结合while循环或者for(;;)循环使用,直到修改成功结束 #### 优点 - 非阻塞 - 不加锁,天生不会出现死锁情况 - 多线程之间对临界资源的访问,几乎没有影响(线程之间不会互相影响) - 没有竞争锁的时候的性能开销 - 没有线程之间切换调度的开销 #### 缺点 - 只能对共享变量操作,不能操作语句块 - 如果修改不成功的话,可能出现一直无法退出的情况,导致CPU过高 - 可能出现ABA问题 #### 解决方法 - 设定重试次数,超过重试次数之后,默认修改不成功,然后退出 - 添加版本号,不仅比对值,而且比对版本号 ## 2. 什么是AQS AQS:AbstactQueuedSyncronizer.抽象排队控制器。是所有竞争锁资源的同步的抽象父类 支持公平锁和非公平锁。 在悲观锁情况下,线程想要操作临界区资源,需要先获取锁,才能对资源进行操作。 在公平锁下,需要先进入排队队列,阻塞线程,然后排队结束之后,唤醒线程。涉及到线程间切换的资源开销和系统态空间和用户态空间的切换的资源开销,效率较低 在非公平锁条件下,先抢占资源,抢占不到资源之后再进行线程入队,继续上述的阻塞,唤醒流程。 AQS一般情况下均为state变量,cas操作和排队队列。 ## 3. CyclicBarrier和CountDownLatch #### 概念描述 ###### CountDownLatch CountDownLatch:倒计数器。所有前置条件都完成之后,才会开始的事情。可以理解为发令枪,赛跑的时候,需要等到所有的参赛人员都到赛道之后,才会打响发令枪,所有的参赛人员一起出发。 ###### 示例代码1 ```java package com.cll.learn.control.demo01; import java.util.Random; import java.util.concurrent.CountDownLatch; /** * 描述信息: * * @author CLL * @version 1.0 * @date 2021/5/15 16:55 */ public class WaitThread implements Runnable { private final CountDownLatch countDownLatch; public WaitThread(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { // 随机的准备时长 long prepareTime = new Random().nextInt(1000); System.out.println("线程:" + Thread.currentThread().getName() + "开始准备,准备时长" + prepareTime); try { Thread.sleep(prepareTime); } catch (InterruptedException e){ e.printStackTrace(); } System.out.println("线程:" + Thread.currentThread().getName() + "准备完成"); countDownLatch.countDown(); } } ``` ```java package com.cll.learn.control.demo01; import java.util.concurrent.CountDownLatch; /** * 描述信息: * 发令枪 * 主线程等待所有任务结束 * 所有任务都结束,主线程才会结束 * @author CLL * @version 1.0 * @date 2021/5/15 16:55 */ public class CountDownLatchDemo { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < 5; i++) { new Thread(new WaitThread(countDownLatch)).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("===>>> 所有线程准备完成,主线程可以结束了"); } } ``` ###### 示例代码2 ```java package com.cll.learn.control.demo02; import java.util.concurrent.CountDownLatch; /** * 描述信息: * * @author CLL * @version 1.0 * @date 2021/5/15 17:05 */ public class ConcurrentThread implements Runnable { private final CountDownLatch countDownLatch; public ConcurrentThread(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { try { long entryTime = System.currentTimeMillis(); System.out.println("线程" + Thread.currentThread().getName() + "进入时间" + entryTime); countDownLatch.await(); long startTime = System.currentTimeMillis(); System.out.println("线程" + Thread.currentThread().getName() + "开始时间" + startTime + "等待时间 = " + (startTime - entryTime)); } catch (InterruptedException e) { e.printStackTrace(); } } } ``` ```java package com.cll.learn.control.demo02; import java.util.Random; import java.util.concurrent.CountDownLatch; /** * 描述信息: * 主线程通知下面的任务,同时开始 * @author CLL * @version 1.0 * @date 2021/5/15 17:03 */ public class CountDownLatchDemo02 { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(5); Thread thread1 = new Thread(new ConcurrentThread(countDownLatch)); thread1.start(); try { Thread.sleep(new Random().nextInt(100)); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } Thread thread2 = new Thread(new ConcurrentThread(countDownLatch)); thread2.start(); try { Thread.sleep(new Random().nextInt(100)); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } Thread thread3 = new Thread(new ConcurrentThread(countDownLatch)); thread3.start(); try { Thread.sleep(new Random().nextInt(100)); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } Thread thread4 = new Thread(new ConcurrentThread(countDownLatch)); thread4.start(); try { Thread.sleep(new Random().nextInt(100)); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } Thread thread5 = new Thread(new ConcurrentThread(countDownLatch)); thread5.start(); try { Thread.sleep(new Random().nextInt(100)); countDownLatch.countDown(); } catch (InterruptedException e) { e.printStackTrace(); } try { thread1.join(); thread2.join(); thread3.join(); thread4.join(); thread5.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("主线程结束"); } } ``` ###### CyclicBarrier CyclicBarrier:循环栅栏。顾名思义,支持循环使用。可以分阶段控制参与的所有的线程,并且可以在一个阶段完成之后去做一次事情。可以以公务员考试为例。考试分为笔试和面试两个阶段。第一个阶段,参与笔试的时候,有的人去得早有的人去的晚,但是都需要等到指定的时间一块开始考试。第一个阶段完成之后,会计算一次大家的成绩。第二个阶段面试的时候,同样有人到的早,有人到得晚。需要等到所有人都到了之后才会开始面试。第二个阶段完成之后,也会计算一次大家的成绩。 ###### 示例代码 ```java package com.cll.learn.control.demo03; /** * 描述信息: * CyclicBarrier阶段完成处理事件 * @author CLL * @version 1.0 * @date 2021/5/15 17:37 */ public class CyclicBarrierActionThread implements Runnable { private int stage; public CyclicBarrierActionThread() { this.stage = 0; } @Override public void run() { stage++; if(1== stage) { System.out.println("===>>> 人员报到完成,开始考试"); } else if (2== stage) { System.out.println("===>>> 笔试结束,计算笔试成绩"); } else if (3== stage) { System.out.println("===>>> 面试结束,计算面试成绩"); } else { System.out.println("===>>> 未知阶段,无法处理"); } } } ``` ```java package com.cll.learn.control.demo03; import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; /** * 描述信息: * 学生线程类 * 等待考试 * 笔试 * 面试 * @author CLL * @version 1.0 * @date 2021/5/15 17:36 */ public class StudentThread implements Runnable { private final String name; private final CyclicBarrier cyclicBarrier; public StudentThread(String name, CyclicBarrier cyclicBarrier) { this.name = name; this.cyclicBarrier = cyclicBarrier; } @Override public void run() { try { System.out.println("===>>> " + name + "到达,等待开始考试"); cyclicBarrier.await(); doBiShi(); cyclicBarrier.await(); doMianShi(); cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } private void doBiShi(){ System.out.println("===>>> " + name + "开始笔试"); try { Thread.sleep(Math.abs(new Random().nextInt(1000))); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("===>>> " + name + "笔试完成"); } private void doMianShi(){ System.out.println("===>>> " + name + "开始面试"); try { Thread.sleep(Math.abs(new Random().nextInt(1000))); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("===>>> " + name + "面试完成"); } } ``` ```java package com.cll.learn.control.demo03; import java.util.concurrent.CyclicBarrier; /** * 描述信息: * * @author CLL * @version 1.0 * @date 2021/5/15 17:42 */ public class CyclicBarrierTest { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(10, new CyclicBarrierActionThread()); System.out.println("开始考试"); for (int i = 0; i < 10; i++) { new Thread(new StudentThread("学生" + i, cyclicBarrier)).start(); } } } ``` ###### Phaser Phaser:相位。基于分治法的可以继承的线程并发控制器,基于相位的控制器。可以实现CountDownLatch和CyclicBarrier的功能。并且可以进行继承,分治和动态的调整线程个数。 当Phaser参与者不为0的时候,向父节点注册自己。当参与者为0的时候,解除注册。 Pahser虽然没有基于AQS实现,但是同样包含AQS的核心思想。 **state变量** 64位long类型变量 最高位表示phaser下的所有参与者是否完成同步。0表示未完成;1表示同步完成 2-32中共31位标识纪元(轮数) 33-48共16位表示总线程数 49-64共16位表示未到达线程数(未完成线程数)arrive到达 cas操作 **排队阻塞队列** phaser中阻塞队列为了并发的考虑,分成了两个消息队列,一个是偶数队列,一个是基数队列。 阻塞队列不是AQS中常用的队列或者链表,而是栈。出栈,入栈都是同一个口,所以只需要head指针就可以了。 ###### 示例代码1 ```java package com.cll.learn.control.demo04; import java.util.Random; import java.util.concurrent.Phaser; /** * 描述信息: * * @author CLL * @version 1.0 * @date 2021/5/15 18:02 */ public class AwaitThread implements Runnable { private final Phaser phaser; public AwaitThread(Phaser phaser) { this.phaser = phaser; } @Override public void run() { System.out.println(phaser.getArrivedParties() + "线程" + Thread.currentThread().getName() + "开始执行"); try { Thread.sleep(Math.abs(new Random().nextInt(1000))); } catch (InterruptedException e) { e.printStackTrace(); } // 表明到达了 phaser.arrive(); System.out.println(phaser.getArrivedParties() + "线程" + Thread.currentThread().getName() + "执行完成"); } } ``` ```java package com.cll.learn.control.demo04; import java.util.concurrent.Phaser; /** * 描述信息: * * @author CLL * @version 1.0 * @date 2021/5/15 18:02 */ public class PhaserTest { public static void main(String[] args) { Phaser phaser = new Phaser(10); System.out.println(phaser.getPhase()); for (int i = 0; i < 10; i++) { new Thread(new AwaitThread(phaser)).start(); } phaser.awaitAdvance(0); System.out.println("===>>> 执行完成"); } } ``` ###### 示例代码2 ```java package com.cll.learn.control.demo05; import java.util.Random; import java.util.concurrent.BrokenBarrierException; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.Phaser; /** * 描述信息: * 学生线程类 * 等待考试 * 笔试 * 面试 * @author CLL * @version 1.0 * @date 2021/5/15 17:36 */ public class StudentThread implements Runnable { private final String name; private final Phaser phaser; public StudentThread(String name, Phaser phaser) { this.name = name; this.phaser = phaser; } @Override public void run() { System.out.println("===>>> " + name + "到达,等待开始考试"); // 到达了,并开启了下一轮 phaser.arriveAndAwaitAdvance(); doBiShi(); phaser.arriveAndAwaitAdvance(); doMianShi(); // phaser.arriveAndAwaitAdvance(); } private void doBiShi(){ System.out.println("===>>> " + name + "开始笔试"); try { Thread.sleep(Math.abs(new Random().nextInt(1000))); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("===>>> " + name + "笔试完成"); } private void doMianShi(){ System.out.println("===>>> " + name + "开始面试"); try { Thread.sleep(Math.abs(new Random().nextInt(1000))); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("===>>> " + name + "面试完成"); } } ``` ````java package com.cll.learn.control.demo05; import java.util.concurrent.Phaser; /** * 描述信息: * * @author CLL * @version 1.0 * @date 2021/5/15 18:19 */ public class PhaserTest2 { public static void main(String[] args) { Phaser phaser = new Phaser(5); for (int i = 0; i < 5; i++) { new Thread(new StudentThread("学生" + i, phaser)).start(); } phaser.awaitAdvance(0); } } ```` ## 4. volatile作用 JMM:Java Memory Model。java内存模型。java的内存模型就是围绕原子性、有序性、可见性展开的。 #### 原子性 volatile虽然无法确保原子性,比对i++这样的32为整形的自增的复合操作。 但是可以实现在32位操作系统中64位数据(long和double)的并发写问题。 #### 有序性 在JVM进行指令重排的时候,遵循Happen-Before原则。在Happen-Before原则中有一条规则就是不会对volatilex吸怪的变量进行指令重排。 #### 可见性 volatile修改的变量的写操作优先于读操作。保证了可见性。 ## 5. wait()的正确使用,是使用if判断还是while循环? wait()方法用来释放当前锁,解除CPU的占用,进入阻塞状态,供其它线程进行执行。直到notify()或者notifyAll()进行唤醒。 如果线程只需要阻塞一次,使用if进行判断。如果唤醒之后还需要进行判断,就是用while循环。 建议统一使用 ```java while(判断条件) { lock.wait(); } ``` ## 6. 为什么wait(),notify()和notifyAll()不能放在Thread类中? - Java是面向对象的语言,所有的东西都是对象,锁是加在对象上面的 - wait()给对象加锁的时候,可以通过notify()或者notifyAll()唤醒等待锁的线程,那么wait()给对象加锁的时候,肯定有一个地方存储请求锁的线程。就是wait set。wait(),notify()和notifyAll()就是操作wait set的 ## 7. syncronized和ReentrantLock的区别 #### 基本概念 ###### 同步与异步 同步和异步关注的是,你去做一件事情,获取到这件事情的结果,是自己去取还是别人给你送过来。自己去取就是同步,别人给你推送过来就是异步。是主观上的区别。 ###### 阻塞与非阻塞 阻塞与非阻塞关注的是,你做这件事情的过程中,还能不能去干其它的事情,如果可以就是异步,不能干其它的事情就是阻塞。过程中是不是导致线程挂起,停止工作。 #### 相同点 - 都是用来保证并发线程的同步控制 - 都是获取锁的 #### 不同点 - syncronized是java中的关键字,原生语义,ReentrantLock是JDK中的API - syncronized获取到对象锁之后,有关该对象的所有临界区资源,其它的线程都不能访问。ReentrantLock可以构造不同的锁条件 - syncronized显示获取锁,隐士释放锁。ReentrantLock需要手动获取锁,手动释放锁。 ````java lock.lock(); try{ doSomthing(); } catch(Exception e) { } finally{ lock.unlock(); } ```` - syncronized遇到异常会自动释放锁,不会导致死锁。ReentrantLock不会自动释放锁,可能导致死锁。 - syncronized只能实现非公平锁。ReentrantLock可以实现公平锁和非公平锁 - syncronized获取锁失败会一直等待获取锁,直到其它线程释放所资源,获取成功。ReentrantLock可以相应中断,可以设置等待的时间等。 ## 8. 什么是自旋 自旋指的是在悲观锁情况下,线程获取锁资源失败的情况下,并不是直接进入排队队列,而是进行重试,直到获取锁成功。 #### 优点 没有线程间切换的开销,效率较高 #### 缺点 可能出现一直获取不到锁的情况,一直空转,导致CPU超高 #### 解决方法 申请获取资源的时候,获取锁失败的情况下,进行重试,重试超过指定次数之后,进入排队队列,变为阻塞状态,等待唤醒。 ## 9. 什么是Java内存模型 #### 目标 Java内存模型是Java虚拟机为了能够让多线程正确、高效的协同工作的机制。核心思想是原子性、有序性、可见性。屏蔽了硬件和操作系统的差异,能够让java程序在不同的机器上实现一直的内存效果。 #### 内存分类 JMM中分为主内存和工作内存。 主内存就是临界区资源,共享内存。工作内存为线程私有的。JMM规定所有的变量都必须存储在主内存。工作内存中存储的是当前线程持有变量从主内存中拷贝过来的副本。 所有线程都不能直接读写主内存,读写的都是线程自己的工作内存。线程的所有读写操作都是对工作内存的。这就涉及到工作内存和主内存的一致性问题。 不同线程之间的变量的传递依靠的是共享的主内存。 #### 内存交互 JMM定义了8中原子语义的操作进行内存的交互。 | 操作符 | 作用域 | 描述信息 | | ------ | ---------------- | ------------------------------------------------------------ | | lock | 主内存 | 将主内存的变量标志位线程独有 | | unlock | 主内存 | 将被占用的线程独有的变量释放出来 | | read | 主内存,工作内存 | 将主内存变量读取到工作内存。 | | load | 工作内存 | 将read读取过来的变量拷贝到工作内存副本中 | | use | 工作内存 | 将工作内存变量传递给执行引擎。javac编译之后,使用变量的字节码指令中一般都包含这个 | | assign | 工作内存 | 将执行引擎接收到的值赋值(分配)给工作内存的其它变量。给变量赋值的时候字节码指令包含 | | store | 工作内存,主内存 | 将工作内存的变量的值传递到主内存中 | | write | 主内存 | 将store传递过来的值写入到主内存变量中 | ###### 规则 - read和load、store和write、use和assign必须成对出现。 - assign之后必须同步回主内存。没有assign的变量不能同步到主内存 - 一个变量统一时刻只能由一个线程进行lock操作,但是一个线程可以执行多次lock操作,并且必须执行相同次数的unlock操作 - unlock操作之前,必须同步回主内存 ## 10. 死锁程序 #### 程序代码如下 ````java package com.cll.learn.deadlock; import java.util.concurrent.locks.ReentrantLock; /** * 描述信息: * * @author CLL * @version 1.0 * @date 2021/5/15 20:54 */ public class MyLockThread implements Runnable{ private static ReentrantLock lock1 = new ReentrantLock(); private static ReentrantLock lock2 = new ReentrantLock(); int lockState; public MyLockThread(int lockState) { this.lockState = lockState; } @Override public void run() { try { if (1 == lockState) { lock1.lock(); try { Thread.sleep(500); } catch (InterruptedException e) { } lock2.lock(); } else { lock2.lock(); try { Thread.sleep(500); } catch (InterruptedException e) { } lock1.lock(); } } catch (Exception e) { e.printStackTrace(); } finally { if (lock1.isHeldByCurrentThread()) { lock1.unlock(); } if (lock2.isHeldByCurrentThread()) { lock2.unlock(); } } } } ```` 使用命令查看死锁情况 ```shell jstack -l 3804 ``` ```shell Found one Java-level deadlock: ============================= "Thread-1": waiting for ownable synchronizer 0x000000076b86c418, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-0" "Thread-0": waiting for ownable synchronizer 0x000000076b86c448, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), which is held by "Thread-1" Java stack information for the threads listed above: =================================================== "Thread-1": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076b86c418> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.cll.learn.deadlock.MyLockThread.run(MyLockThread.java:41) at java.lang.Thread.run(Thread.java:748) "Thread-0": at sun.misc.Unsafe.park(Native Method) - parking to wait for <0x000000076b86c448> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) at java.util.concurrent.locks.LockSupport.park(LockSupport.java:175) at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:836) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:870) at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1199) at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:209) at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:285) at com.cll.learn.deadlock.MyLockThread.run(MyLockThread.java:33) at java.lang.Thread.run(Thread.java:748) Found 1 deadlock. ```