volatile关键字是java虚拟机提供的最轻量级的同步机制
JMM:Java Memory Model
可见性:
JMM内存模型中,工作内存和主内存同步延迟现象造成了可见性问题。
为什么要使用缓存?
处理器CPU和存储设备的速度存在数量级上的差距,使用缓存Cache来解决这个问题
使用缓存造成的问题?
每个处理器(线程)都有自己的缓存,但是却需要共享一个主内存(线程之间的通信传值必须通过主内存来完成),也就是缓存一致性问题
原子性:
? 不可分割,完整性,只能同时成功(失败)
```java
n++;
// 1.执行getfield(getstatic)获得原始值
// 2.执行iadd 进行加1操作
// 3.执行putfield写 把累加后的值写回
```
? 保证原子性:
- synchronized
- JUC(atomic)
有序性:
? 简单来说有序性就是禁止指令重排序优化
? 处理器计算和Java虚拟机都会对指令重排进行优化
```java
1.源代码
2.编译器优化重排
3.指令并行重排
4.内存系统重排
5.最终执行的指令
```
- 单线程环境确保程序最终执行结果和代码顺序执行的结果一致
- 处理器进行重排序必须考虑**指令之间的依赖性**
- 多线程中线程交替运行,编译器优化重排,无法保证多个线程中使用变量保持一致性
? 内存屏障:Memory Barrier,是一个CPU指令
当一个变量定义为Volatile后:
class MyData{
volatile int number = 0;
void setNumber(){
this.number = 60;
}
void addNumber(){
number ++;
}
// juc 来保证原子性
AtomicInteger atomicInteger=new AtomicInteger(0);
void addAtomic(){
atomicInteger.getAndIncrement();
}
}
public class VolatileVisible {
public static void main(String[] args) {
//VisibleByVolatile();
AtomicNotByVolatile();
}
/**
* volatile 不保证原子性
*/
public static void AtomicNotByVolatile() {
MyData myData=new MyData();
for(int i=1;i<=20;i++){
new Thread(()->{
for(int j=0;j<1000;j++){
myData.addNumber();
myData.addAtomic();
}
},String.valueOf(i)).start();
}
// 等待线程完全运行
while(Thread.activeCount()>2){
Thread.yield();
}
System.out.println(Thread.currentThread().getName()
+"\t finally number value "+myData.number);
System.out.println(Thread.currentThread().getName()
+"\t atomic number "+myData.atomicInteger);
}
/**
* volatile 保证可见性
*/
public static void VisibleByVolatile() {
MyData myData=new MyData();
new Thread(() -> {
System.out.println(Thread.currentThread().getName()+"\t come in");
try{
TimeUnit.SECONDS.sleep(3);
}catch (InterruptedException e){
e.printStackTrace();
}
myData.setNumber();
System.out.println(Thread.currentThread().getName()
+"\t updated number value "+ myData.number);
},"aaa").start();
// 等待循环
while(myData.number==0){
}
System.out.println(Thread.currentThread().getName()
+"\t mission is over "+myData.number);
}
}
? 在单例模式(Singleton)的双重检测(DCL)
public class SingletonTest {
private volatile static SingletonTest instance=null;
private SingletonTest(){
System.out.println(Thread.currentThread().getName()+"\t 构造方法");
}
// 懒加载模式不能保证在并发情况下的单例
private static SingletonTest getInstance(){
if(instance==null){
instance=new SingletonTest();
}
return instance;
}
// DCL模式 双端检锁模式 instance需要表示为volatile
// 防止指令进行重排
private static SingletonTest DCLGetInstance(){
if(instance==null){
synchronized (SingletonTest.class){
if(instance==null){
instance=new SingletonTest();
}
}
}
return instance;
}
public static void main(String[] args) {
for(int i=1;i<=10;i++){
new Thread(()->{
SingletonTest.DCLGetInstance();
}).start();
}
}
}
CompareAndSwap 比较并交换
CompareAndSwap 是一条并发原语
// java.util.concurrent.atomic
// AtomicInteger.compareAndSet()
public final boolean compareAndSet(int expectedValue, int newValue) {
return U.compareAndSetInt(this, VALUE, expectedValue, newValue);
}
// Unsafe 类下的native方法
public final native boolean compareAndSetInt(Object o, long offset,
int expected,int x);
第一参数拿到的期望值,如果期望值一致,进行newValue赋值;期望值不一致,数据被修改,返回false
java.util.concurrent 包中的许多类都提供比synchronized机制更高的性能和可伸缩性
原子变量+非阻塞的同步机制
Unsafe类直接调用操作系统底层资源执行相关任务
// AtomicInteger.java
private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
public final int getAndIncrement() {
return U.getAndAddInt(this, VALUE, 1);
}
// Unsafe.java
public final int getAndAddInt(Object o, long offset, int delta) {
int v;
do {
// 先根据内存偏移地值 来取到值
v = getIntVolatile(o, offset);
// 如果这个值在运行期间被其他值修改,重新进行读取这个值,
// 保证这个值相等,才会进行增加操作
} while (!weakCompareAndSetInt(o, offset, v, v + delta));
return v;
}
一个变量初次读取的时候是A值,它的值被修改成B,后来又被修改成A,CAS操作会认为其重来没有被修改过
CAS算法实现一个重要的前提需要去除内存中某个时刻的数据并在当下时刻进行比较并替换,在这两个操作的时间间隔会导致数据的变化。
如何解决ABA问题
1)原子引用
2)传统的互斥同步
原子引用
AtomicReference<>(); // 引用类
AtomicStampedReference<>() // 带时间戳的原子引用
直接使用ArrayList / HashSet/ HashMap 在多线程并发的环境下
public class ContainerNotSafeTest {
public static void main(String[] args) {
listNotSafe();
mapSafe();
}
public static void mapSafe() {
Map<String,String> map=new ConcurrentHashMap<>();//new HashMap<>();
for(int i=1;i<=30;i++) {
new Thread(() -> {
map.put(Thread.currentThread().toString(), UUID.randomUUID().toString().substring(0, 8));
System.out.println(map);
}, String.valueOf(i)).start();
}
}
public static void listNotSafe() {
//List<String> list= Arrays.asList("a","b","c");
//list.forEach(System.out::println);
List<String> list=new CopyOnWriteArrayList<>();
for(int i=1;i<=30;i++){
new Thread(()->{
list.add(UUID.randomUUID().toString().substring(0,8));
System.out.println(list);
},String.valueOf(i)).start();
}
}
}
private void checkForComodification(final int expectedModCount) {
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
List<String> list=new Vector<>();
// 加锁synchronized来实现并发安全
public synchronized boolean add(E e) {
modCount++;
add(e, elementData, elementCount);
return true;
}
这是早期JDK的一部分(jdk 1.0),这类同步的封装器由 Collections.synchronizedXxx等工厂方法创建的,实现线程安全的方式:将他们的状态封装起来,对每个共有方法进行同步,是得每次只有一个线程能访问容器的状态
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
}
SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
}
// 实现互斥同步
public int size() {
synchronized (mutex) {return c.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return c.isEmpty();}
}
public boolean contains(Object o) {
synchronized (mutex) {return c.contains(o);}
}
public Object[] toArray() {
synchronized (mutex) {return c.toArray();}
}
}
工厂:封装创建对象(new)的代码
工厂方法: 处理对象的创建,并将这样的行为封装在子类中
abstract Product factoryMethod(String type)
2.使用集合同步方法封装
List<String> list= Collections.synchronizedList(new ArrayList<>());
3.并发容器类
// java.util.concurrent.CopyOnWriteArrayList;
List<String> list= new CopyOnWriteArrayList<>();
// 源码
final transient Object lock = new Object();
// 在写的时候进行加锁
public boolean add(E e) {
synchronized (lock) {
Object[] es = getArray();
int len = es.length;
es = Arrays.copyOf(es, len + 1);
es[len] = e;
setArray(es);
return true;
}
}
ReentrantLock 默认就是非公平锁
Synchronized 也是非公平锁
// 默认的无参构造函数就是非公平锁
public ReentrantLock() {
sync = new NonfairSync();
}
// 传入True就是公平锁
public ReentrantLock(boolean fair) {
sync = fair ? new FairSync() : new NonfairSync();
}
非公平锁的优先在于吞吐量比公平锁要大
可重入锁也叫递归锁,最大的作用就是避免死锁
ReetrantLock/Synchronized 就是典型的可重入锁
// synchronized
class Phone{
// 同一线程外层函数获得锁之后,内层递归函数仍获取该锁代码
public synchronized void sendSMS() throws Exception{
System.out.println(Thread.currentThread().getId()+"\t invoked sendSMS");
// 内层函数
sendEmail();
}
public synchronized void sendEmail() throws Exception{
System.out.println(Thread.currentThread().getId()+"\t invoked sendEmail");
}
}
// ReentrantLock
class Person implements Runnable{
@Override
public void run() {
getPerson();
}
Lock lock=new ReentrantLock();
private void getPerson(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"\t invoked get person");
setPerson();
}finally {
lock.unlock();
}
}
private void setPerson(){
lock.lock();
try{
System.out.println(Thread.currentThread().getName()+"\t invoked set person");
}finally {
lock.unlock();
}
}
}
/**
* 可重入锁
*/
public class ReentrantLockTest {
public static void main(String[] args) {
Phone phone=new Phone();
Person person=new Person();
new Thread(()->{
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"thread1").start();
new Thread(()->{
try {
phone.sendSMS();
} catch (Exception e) {
e.printStackTrace();
}
},"thread2").start();
System.out.println("==========================");
Thread thread3=new Thread(person);
Thread thread4=new Thread(person);
thread3.start();
thread4.start();
}
}
// 运行结果
/**
Thread-0 invoked get person
Thread-0 invoked set person
Thread-1 invoked get person
Thread-1 invoked set person
12 invoked sendSMS
12 invoked sendEmail
13 invoked sendSMS
13 invoked sendEmail
/
尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁
/**
* 自旋锁 spinLock
*/
public class SpinLockTest {
private AtomicReference<Thread> atomicReference=new AtomicReference<>();
private void myLock(){
Thread thread=Thread.currentThread();
System.out.println(Thread.currentThread().getName()+"\t thread come in myLock");
// 旋转
// A 线程调用后 B线程一直执行循环等待
while(!atomicReference.compareAndSet(null,thread)){
}
}
private void myUnLock(){
Thread thread=Thread.currentThread();
atomicReference.compareAndSet(thread,null);
System.out.println(Thread.currentThread().getName()+"\t invoked the myUnLock");
}
public static void main(String[] args) {
SpinLockTest spinLockTest=new SpinLockTest();
new Thread(()->{
spinLockTest.myLock();
// 暂停一会线程 让后面的线程无法取到锁 实现自旋
try{ TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}
spinLockTest.myUnLock();
},"A").start();
// 暂停一会 A线程先于B线程运行
try{ TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();}
new Thread(()->{
spinLockTest.myLock();
spinLockTest.myUnLock();
},"B").start();
}
}
// 线程操作的资源类
class MyCache{
private volatile Map<String,Object> map=new HashMap<>();
// 重入锁
private Lock lock=new ReentrantLock();
// 读写锁
private ReentrantReadWriteLock rwLock=new ReentrantReadWriteLock();
void put(String key, Object value) {
// 写锁
rwLock.writeLock().lock();
try{
System.out.println(Thread.currentThread().getName() + "\t 正在写入" + key);
// 暂停线程模拟 资源
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
map.put(key, value);
System.out.println(Thread.currentThread().getName() + "\t 写入完成");
}catch (Exception e){
e.printStackTrace();
}finally {
rwLock.writeLock().unlock();
}
}
void get(String key){
rwLock.readLock().lock();
try{
System.out.println(Thread.currentThread().getName()+"\t 正在读取"+key);
// 暂停线程模拟资源
try {
TimeUnit.MILLISECONDS.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
Object result=map.get(key);
System.out.println(Thread.currentThread().getName() + "\t 读取完成"+result);
}catch (Exception e){
e.printStackTrace();
}finally {
rwLock.readLock().unlock();
}
}
}
/**
* 读写锁
* 读取共享资源可以同时并发的进行
* 写资源 就需要一个线程独占
*
* 写操作:原子+独占
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
MyCache myCache=new MyCache();
// 写线程
for(int i=0;i<5;i++){
int finalI = i;
new Thread(()->{
myCache.put(finalI +"", finalI +"");
},String.valueOf(i)).start();
}
// 保证写入完全后,再进行读取
try{TimeUnit.SECONDS.sleep(2);}catch (InterruptedException e){e.printStackTrace();}
// 读线程
for(int i=0;i<5;i++){
int finalI = i;
new Thread(()->{
myCache.get(finalI+"");
},String.valueOf(i)).start();
}
}
}
AQS 是一个用于构建锁和同步器的框架,AQS的实现
// AOS 实现类的体现
public class ReentrantLock implements Lock, java.io.Serializable {
/** Synchronizer providing all implementation mechanics */
private final Sync sync;
abstract static class Sync extends AbstractQueuedSynchronizer {
}
//
public class Semaphore implements java.io.Serializable {
/** All mechanics via AbstractQueuedSynchronizer subclass */
private final Sync sync;
/**
* Synchronization implementation for semaphore. Uses AQS state
* to represent permits. Subclassed into fair and nonfair
* versions.
*/
abstract static class Sync extends AbstractQueuedSynchronizer {
}
信号量可以代替Synchronized和Lock
public class SemaphoreTest {
public static void main(String[] args) {
Semaphore semaphore=new Semaphore(3);
for(int i=0;i<6;i++){
new Thread(()->{
try{
semaphore.acquire();
System.out.println(Thread.currentThread().getName()+"\t 抢到车位");
try{ TimeUnit.SECONDS.sleep(3); }catch (InterruptedException ie){ie.printStackTrace();}
System.out.println(Thread.currentThread().getName()+"\t 停车3s后离开车位");
}catch (InterruptedException e){
e.printStackTrace();
}finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
}
/**
* 0 抢到车位
* 1 抢到车位
* 2 抢到车位
* 2 停车3s后离开车位
* 1 停车3s后离开车位
* 0 停车3s后离开车位
* 4 抢到车位
* 5 抢到车位
* 3 抢到车位
* 3 停车3s后离开车位
* 5 停车3s后离开车位
* 4 停车3s后离开车位
*/
层级结构图如下图所示,List 和 BlockingQueue是高度相似的层级接口
常见的实现类
ArrayBlockingQueue ,基于数组实现的有界阻塞队列
LinkedBlockingQueue , 基于链表结构的阻塞队列,吞吐量较ArrayBlockingQueue 要高
//一个很重要的特性,默认的队列长度是 Integer.MAX_VALUE
public LinkedBlockingQueue() {
this(2147483647);
}
public LinkedBlockingQueue(int capacity) {
this.count = new AtomicInteger();
this.takeLock = new ReentrantLock();
this.notEmpty = this.takeLock.newCondition();
this.putLock = new ReentrantLock();
this.notFull = this.putLock.newCondition();
if (capacity <= 0) {
throw new IllegalArgumentException();
} else {
this.capacity = capacity;
this.last = this.head = new LinkedBlockingQueue.Node((Object)null);
}
}
SynchronousQueue ,不存储元素的阻塞队列
PriorityBlockingQueue
LinkedBlockingDeque
LinkedTransferQueue
常见的错误类型
A blocking queue in which each insert operation must wait for a corresponding remove operation by another thread, and vice versa. A synchronous queue does not have any internal capacity, not even a capacity of one
每一个put操作都必须等待一个take操作,否则不能继续添加元素。
/**
* 不存储元素的队列
*/
public class SynchronousQueueTest {
public static void main(String[] args) {
BlockingQueue<String> blockingQueue=new SynchronousQueue<>();
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"\t"+"put 1");
blockingQueue.put("1");
System.out.println(Thread.currentThread().getName()+"\t"+"put 2");
blockingQueue.put("2");
System.out.println(Thread.currentThread().getName()+"\t"+"put 3");
blockingQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A").start();
new Thread(()->{
try{
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
TimeUnit.SECONDS.sleep(5);
System.out.println(Thread.currentThread().getName()+"\t"+blockingQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B").start();
}
}
生产者消费者模型
传统的加锁版本
sync+wait+notify 是一组
lock+await+singalAll 是一组
class ShareData{
private int number=0;
private Lock lock=new ReentrantLock();
private Condition condition=lock.newCondition();
void increment() throws InterruptedException {
// 加锁
lock.lock();
try {
// 判断
// 防止虚假唤醒
while(number!=0){
// 等待
condition.await();
}
// 操作
number++;
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 通知唤醒
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
void decrement() throws Exception{
// 加锁
lock.lock();
try {
// 判断
// 防止虚假唤醒
while(number == 0){
// 等待
condition.await();
}
// 操作
number --;
System.out.println(Thread.currentThread().getName()+"\t"+number);
// 通知唤醒
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
/**
* 传统的生产者消费者模式
*
* 两个线程交替操作,一个加1,一个减1
*/
public class ConsumerProducerTest {
public static void main(String[] args) {
ShareData shareData=new ShareData();
// 两个线程表示 生产者和消费者
new Thread(()->{
for(int i=0;i<5;i++){
try {
shareData.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for(int i=0;i<5;i++){
try {
shareData.decrement();
} catch (Exception e) {
e.printStackTrace();
}
}
},"B").start();
}
}
while()循环
原始构成
synchronized属于JVM,底层使用 monitorenter 和 monitorexit
lock是具体的类,是api层面的锁 java.util.concurrent.lock
使用的方法
synchronized ,monitorexit实现自动退出,不需要用户手动的释放锁,代码块执行结束自动让线程释放对锁的占用
ReentrantLock 需要用户手动的释放锁
等待可中断
synchronized 不可中断,除非抛出异常否则正常运行到结束
ReentrantLock 可中断,设置超时方法tryLock(long timeout,TimeUnit unit) ,调用interrupt()方法可以进行中断
加锁否公平
synchronized 非公平锁
ReentrantLock两者都可以,默认是公平锁,根据构造方法传入的boolean值来实现
锁绑定多个条件Condition
Synchronized 没有
ReentrantLock可以实现精准唤醒
线程池主要是控制运行线程的数量,处理过程将任务放入队列,线程执行完毕后,再从队列中取出任务来执行
Java中的线程池是通过Executor框架实现的主要有Executor、Executors、ExecutorService、ThreadPoolExecutor
常见实现的线程池
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
线程池依托于ThreadPoolExecutor来进行创建
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,
TimeUnit unit,BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
线程池的重要参数:
线程池的工作原理(流程):
线程池的拒绝策略
? 等待队列已经排满,无法容纳新任务,同时线程池的max线程数也到达,无法继续为新任务服务,这个时候就需要拒绝策略机制合理的处理这个问题。
RejectedExecutionHandler
AbortPolicy 默认,直觉抛出异常阻止其正常运行
CallerRunsPolicy , 将某些任务回退到调用者
DiscardOldestPolicy ,抛弃队列中等待最久的任务,当前任务加入队列
DiscardPolicy , 直接丢弃任务
Executors返回的线程池对象的弊端
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
// 这个队列的长度问题
public LinkedBlockingQueue() {
this(Integer.MAX_VALUE);
}
public LinkedBlockingQueue(int capacity) {
if (capacity <= 0) throw new IllegalArgumentException();
this.capacity = capacity;
last = head = new Node<E>(null);
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
死锁:
两个及两个以上的进程在执行过程中,因争夺资源而造成的一种互相等待的现象
class holdLockThread implements Runnable{
private String lockA;
private String lockB;
public holdLockThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName()+"\t 自己持有:"+lockA+"\t 尝试获取"+lockB);
// 模拟线程操作
try{ TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); }
synchronized (lockB){
System.out.println(Thread.currentThread().getName()+"\t 自己持有:"+lockB+"\t 尝试获取"+lockA);
}
}
}
}
/**
* 死锁
* 如何定位死锁的位置
* ps -ef|grep ***
* jps java ps
* jps -l 定位进程号
* jstack 进程号
* 找到死锁查看
*/
public class DeadLockTest {
public static void main(String[] args) {
String lockA="lockA";
String lockB="lockB";
new Thread(new holdLockThread(lockA,lockB),"thread AAA").start();
new Thread(new holdLockThread(lockB,lockA),"thread BBB").start();
}
}
解决死锁问题:
除了查看日志还可以进行定位
dengshuodengshuo@dengMac ~/Code/Project/JVM/src/DeadLock jps -l
9094
12250 DeadLock.DeadLockTest
12251 org.jetbrains.jps.cmdline.Launcher
12252 jdk.jcmd/sun.tools.jps.Jps
dengshuodengshuo@dengMac ~/Code/Project/JVM/src/DeadLock jstack 12250
2020-03-04 09:28:05
Found one Java-level deadlock:
=============================
"thread AAA":
waiting to lock monitor 0x000000010a140a00 (object 0x0000000787e71cb8, a java.lang.String),
which is held by "thread BBB"
"thread BBB":
waiting to lock monitor 0x0000000100b1ae00 (object 0x0000000787e71c88, a java.lang.String),
which is held by "thread AAA"
Java stack information for the threads listed above:
===================================================
"thread AAA":
at DeadLock.holdLockThread.run(DeadLockTest.java:22)
- waiting to lock <0x0000000787e71cb8> (a java.lang.String)
- locked <0x0000000787e71c88> (a java.lang.String)
at java.lang.Thread.run(java.base@13/Thread.java:830)
"thread BBB":
at DeadLock.holdLockThread.run(DeadLockTest.java:22)
- waiting to lock <0x0000000787e71c88> (a java.lang.String)
- locked <0x0000000787e71cb8> (a java.lang.String)
at java.lang.Thread.run(java.base@13/Thread.java:830)
Found 1 deadlock.
原文:https://www.cnblogs.com/GeekDanny/p/12884862.html