1、线程创建的几种方式
-
继承Thread
单继承,静态代理的实现
-
实现Runnable接口
-
实现Callable接口
package com.demo.thread; import java.util.concurrent.*; public class CallableTest implements Callable<String> { @Override public String call() throws Exception { return Thread.currentThread().getName(); } public static void main(String[] args) throws ExecutionException, InterruptedException { CallableTest call = new CallableTest(); // 创建执行服务 ExecutorService executorService = Executors.newFixedThreadPool(1); // 提交执行 Future<String> future = executorService.submit(call); // 获取结果 String s = future.get(); System.out.println(s); // 关闭服务 executorService.shutdownNow(); } }
2、静态代理
代理对象可以做真实对象做不了的事情
真实对象和代理对象都要实现同一个接口
代理对象要代理真实对象
3、线程安全性
多个线程操作同一个资源的时候,线程安全性问题
-
synchronized
同步方法,锁的是当前对象(在方法上用synchronized修饰)
同步块:synchronized(obj){},可以锁任何对象
synchronized 锁的对象是方法的调用者
-
Lock
两者的区别:
-
Lock是显式锁(手动开启和关闭锁),synchronized是隐式锁,出了作用域自动释放
-
Lock只有代码块锁,synchronized又代码块锁和方法锁
-
使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类)
-
使用顺序:
Lock > 同步代码块(已经进入了方法体,分配了相应资源) > 同步方法(在方法体外)
1.使用Synchronized
package com.demo.sync;
public class UnsafeBank {
public static void main(String[] args) {
Account a = new Account(100, "基金");
Drawing d1 = new Drawing(a, 50, "A");
Drawing d2 = new Drawing(a, 100, "B");
d1.start();
d2.start();
}
}
//账户
class Account{
int money; // 余额
String name; // 账户名称
public Account(int money, String name) {
this.money = money;
this.name = name;
}
}
class Drawing extends Thread{
Account account;
int drawingMoney; // 取了多少钱
int nowMoney; // 手里多少钱
public Drawing(Account account, int drawingMoney, String name){
super(name);
this.account = account;
this.drawingMoney = drawingMoney;
}
@Override
public void run() {
synchronized (account) { // 避免线程安全性问题
if (account.money - drawingMoney < 0) {
System.out.println(this.getName() + "钱不够,取不了");
return;
}
try {
Thread.sleep(2000); //sleep可以放大问题的发生性
} catch (InterruptedException e) {
e.printStackTrace();
}
account.money = account.money - drawingMoney;
nowMoney = nowMoney + drawingMoney;
System.out.println("线程:" + this.getName() + "的" + account.name + "余额为:" + account.money);
System.out.println(this.getName() + " 手里的钱:" + nowMoney);
}
}
}
- 使用ReentrantLock锁
package com.demo.lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockTest {
public static void main(String[] args) {
TestTicket ticket = new TestTicket();
new Thread((ticket), "张三").start();
new Thread((ticket), "李四").start();
new Thread((ticket), "王五").start();
}
}
class TestTicket implements Runnable{
int ticketNums = 1000;
private final ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
while(true){
try {
lock.lock();// 加锁
if (ticketNums > 0) {
//Thread.sleep(500);
System.out.println(Thread.currentThread().getName() + "买了第" + ticketNums-- + "票");
} else {
break;
}
}catch(Exception e){
// do nothing
}finally {
lock.unlock();//解锁
}
}
}
}
4、生产者消费者问题
package com.demo.gaoji;
// 生产者,消费者,产品,缓冲区
public class TestProducerConsumer {
public static void main(String[] args) {
SynContainer synContainer = new SynContainer();
new Producer(synContainer).start();
new Consumer(synContainer).start();
}
}
// 产品:鸡
class Chicken{
int id; // 产品编号
public Chicken(int id) {
this.id = id;
}
}
// 生产者
class Producer extends Thread{
// 往缓冲区中生产产品
SynContainer synContainer;
public Producer(SynContainer synContainer){
this.synContainer = synContainer;
}
// 生产
@Override
public void run() {
for (int i = 0; i < 100; i++) {
synContainer.push(new Chicken(i));
System.out.println("生产了" + i + "只鸡");
}
}
}
// 消费者
class Consumer extends Thread{
// 消费缓冲区中的产品
SynContainer synContainer;
public Consumer(SynContainer synContainer){
this.synContainer = synContainer;
}
// 消费
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("消费了-->第" + synContainer.pop().id + "只鸡");
}
}
}
// 等待,业务,通知
// 缓冲区
class SynContainer{
// 缓冲区大小:容器大小
Chicken[] chickens = new Chicken[10];
// 缓冲区计数器
int count = 0;
// 生产者放入产品
public synchronized void push(Chicken chicken){
// 如果缓冲区满了
if (count == chickens.length){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
chickens[count] = chicken;
count++;
// 通知消费者消费
this.notifyAll();
}
// 消费者消费产品
public synchronized Chicken pop(){
// 如果缓冲区中没有数据
if (count == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 消费
count--;
Chicken chicken = chickens[count];
// 通知生产者生产
this.notifyAll();
return chicken;
}
}
问题:这里等待唤醒是用的if判断,为了防止虚假唤醒,等待应该总是出现在循环中
解决方案:将if改为while
5、线程池
6、高级——JUC
一、生产者与消费者问题扩展
业务,判断-> 执行-> 通知
(1)传统的方式:
package com.demo.gaoji;
public class A {
public static void main(String[] args) {
Data data = new Data();
int count = 500;
new Thread(() -> {
for (int i = 0; i < count; i++) {
data.increment();
}
}, "线程A").start();
new Thread(() -> {
for (int i = 0; i < count; i++) {
data.decrement();
}
}, "线程B").start();
new Thread(() -> {
for (int i = 0; i < count; i++) {
data.increment();
}
}, "线程C").start();
new Thread(() -> {
for (int i = 0; i < count; i++) {
data.decrement();
}
}, "线程D").start();
}
}
class Data{
private int number = 0;
public synchronized void increment(){
while (number != 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(Thread.currentThread().getName() + "加" + number + "了");
// 加1 了可以通知其他线程了
this.notifyAll();
}
public synchronized void decrement(){
while (number == 0){
try {
this.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(Thread.currentThread().getName() + "减" + number + "了");
// 减1 了可以通知其他线程了
this.notifyAll();
}
}
(2)使用condition:
可以精准的控制唤醒线程
class Data2{
private int number = 0;
private final Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
public void increment(){
lock.lock();
try {
while (number != 0){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number++;
System.out.println(Thread.currentThread().getName() + "加" + number + "了");
// 加1 了可以通知其他线程了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement(){
lock.lock();
try {
while (number == 0){
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
number--;
System.out.println(Thread.currentThread().getName() + "减" + number + "了");
// 减1 了可以通知其他线程了
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
/**
* 控制唤醒进程
* 精准唤醒,A -> B -> C -> A...
*/
class Data3{
private int number = 0;
private final Lock lock = new ReentrantLock();
Condition conditionA = lock.newCondition();
Condition conditionB = lock.newCondition();
Condition conditionC = lock.newCondition();
public void printA(){
lock.lock();
try {
while (number != 0){
conditionA.await();
}
number = 1;
System.out.println(Thread.currentThread().getName() + "执行完了,该轮到下一个了");
conditionB.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (number != 1){
conditionB.await();
}
number = 2;
System.out.println(Thread.currentThread().getName() + "执行完了,该轮到下一个了");
conditionC.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (number != 2){
conditionC.await();
}
number = 0;
System.out.println(Thread.currentThread().getName() + "执行完了,该轮到下一个了");
conditionA.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
二、关于锁的几个问题
-
synchronized修饰的方法,锁的对象是方法的调用者
// 下面的运行结果:先打印发短信,不是谁先调用的问题,这里是同一个对象phone public static void main(String[] args) { Phone phone = new Phone(); new Thread(() -> { phone.sendSms(); }, "A").start(); // 休眠一会 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { phone.call(); }, "B").start(); } class Phone{ public synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public synchronized void call(){ System.out.println("打电话"); } } // 以下的运行结果:先打印打电话,这里不再是同一个对象了 public static void main(String[] args) { Phone phone1 = new Phone(); Phone phone2 = new Phone(); new Thread(() -> { phone1.sendSms(); }, "A").start(); // 休眠一会 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { phone2.call(); }, "B").start(); }
-
如果在synchronized修饰的方法前再加上static
static修饰的同步方法,锁的是类class
// 运行结果:先打印发短信,这里是两个对象phone1,phone2,但是static修饰的同步方法,将类锁住了 public static void main(String[] args) { Phone1 phone1 = new Phone1(); Phone1 phone2 = new Phone1(); new Thread(() -> { phone1.sendSms(); }, "A").start(); // 休眠一会 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } new Thread(() -> { phone2.call(); }, "B").start(); } class Phone1{ public static synchronized void sendSms(){ try { TimeUnit.SECONDS.sleep(4); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("发短信"); } public static synchronized void call(){ System.out.println("打电话"); } }
-
普通的方法和同步方法,先调用普通方法,同一个对象而言
-
关键在于看,锁的对象是否是同一个,即是否使用的是同一把锁
7、volatile关键字
一个共享变量被volatile修饰之后,
- 保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,新值对其他线程来说是立即可见的
- 禁止进行指令的重排
(1)保证可见性
public class VolatileDemo01 {
private volatile static int num = 0;
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
while (num == 0){
}
}, String.valueOf(i)).start();
}
TimeUnit.SECONDS.sleep(1);
num = 1;
System.out.println(num);
}
}
(2)不能保证原子性
public class VolatileDemo02 {
private static int num = 0;
public static void add(){
num++;
}
public static void main(String[] args) {
// 10个线程
for (int i = 0; i < 10; i++) {
new Thread(()->{
// 每个线程执行1000次
for (int j = 0; j < 1000; j++) {
add();
}
}).start();
}
while(Thread.activeCount() > 2){ // main线程,gc线程
Thread.yield();
}
System.out.println(Thread.currentThread().getName() + " " + num);
}
}
运行结果:main 9666,而不是期望的:10000
可见性只能保证每次读取的是最新的值,没法保证对变量的操作的原子性
为了保证原子性:3种方式
-
采用synchronized
public class VolatileDemo02 { private static int num = 0; public static synchronized void add(){ num++; } public static void main(String[] args) { // 10个线程 for (int i = 0; i < 10; i++) { new Thread(()->{ // 每个线程执行1000次 for (int j = 0; j < 1000; j++) { add(); } }).start(); } while(Thread.activeCount() > 2){ // main线程,gc线程 Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + num); } }
-
采用Lock
public class VolatileDemo02 { private static int num = 0; private static Lock lock = new ReentrantLock(); public static void add(){ lock.lock(); try { num++; } finally { lock.unlock(); } } public static void main(String[] args) { // 10个线程 for (int i = 0; i < 10; i++) { new Thread(()->{ // 每个线程执行1000次 for (int j = 0; j < 1000; j++) { add(); } }).start(); } while(Thread.activeCount() > 2){ // main线程,gc线程 Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + num); } }
-
采用AtomicInteger
public class VolatileDemo02 { private static AtomicInteger num = new AtomicInteger(); public static void add(){ num.getAndIncrement(); } public static void main(String[] args) { // 10个线程 for (int i = 0; i < 10; i++) { new Thread(()->{ // 每个线程执行1000次 for (int j = 0; j < 1000; j++) { add(); } }).start(); } while(Thread.activeCount() > 2){ // main线程,gc线程 Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + num); } }
(3)禁止指令重排
- 保证特定操作的执行顺序
- 可以保证某些变量的内存可见性
8、单例模式
构造方法私有化
1、饿汉式单例
加载类的时候已经创建了对象的实例,可能会浪费内存空间
public class SingletonTest {
private SingletonTest(){
}
private static final SingletonTest instance = new SingletonTest();
public static SingletonTest getInstance(){
return instance;
}
}
2、懒汉式单例
public class SingletonTest {
private SingletonTest(){
}
private static SingletonTest instance;
public static SingletonTest getInstance(){
if (instance == null){
instance = new SingletonTest();
}
return instance;
}
}
注:在多线程下存在问题
示例:
public class SingletonTest {
private SingletonTest(){
System.out.println(Thread.currentThread().getName() + " 创建实例成功");
}
private static SingletonTest instance;
public static SingletonTest getInstance(){
if (instance == null){
instance = new SingletonTest();
}
return instance;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
SingletonTest.getInstance();
}).start();
}
}
}
// 运行结果:
Thread-1 创建实例成功
Thread-3 创建实例成功
Thread-0 创建实例成功
Thread-2 创建实例成功
3、采用双重检测锁的懒汉式单例(DCL懒汉式)
对上面的代码改进:
public class SingletonTest {
private SingletonTest(){
System.out.println(Thread.currentThread().getName() + " 创建实例成功");
}
// volatile保证其他线程可见性,同时禁止指令重排
private static volatile SingletonTest instance;
public static SingletonTest getInstance(){
if (instance == null){
synchronized (SingletonTest.class){
if (instance == null) {
instance = new SingletonTest();
}
}
}
return instance;
}
}
5、枚举式单例
上面的几种模式在==反射==面前都是不安全的
public enum EnumSingleton {
INSTANCE;
public static EnumSingleton getInstance(){
return INSTANCE;
}
public static void main(String[] args) throws Exception {
EnumSingleton instance = EnumSingleton.getInstance();
Constructor<EnumSingleton> declaredConstructor = EnumSingleton.class.getDeclaredConstructor(String.class, int.class);
declaredConstructor.setAccessible(true);
EnumSingleton instance2 = declaredConstructor.newInstance();
System.out.println(instance);
System.out.println(instance2);
}
}
//运行结果:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at org.jdbc.demo.EnumSingleton.main(EnumSingleton.java:17)
附newInstance()的源码:
@CallerSensitive
public T newInstance(Object ... initargs)
throws InstantiationException, IllegalAccessException,
IllegalArgumentException, InvocationTargetException
{
if (!override) {
if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
Class<?> caller = Reflection.getCallerClass();
checkAccess(caller, clazz, null, modifiers);
}
}
if ((clazz.getModifiers() & Modifier.ENUM) != 0)
throw new IllegalArgumentException("Cannot reflectively create enum objects");
ConstructorAccessor ca = constructorAccessor; // read volatile
if (ca == null) {
ca = acquireConstructorAccessor();
}
@SuppressWarnings("unchecked")
T inst = (T) ca.newInstance(initargs);
return inst;
}
9、死锁排查
package com.demo.deadLock;
import java.util.concurrent.TimeUnit;
public class DeadLockTest {
public static void main(String[] args) {
String lockA = "lockA";
String lockB = "lockB";
new Thread(new MyThread(lockA, lockB), "T1").start();
new Thread(new MyThread(lockB, lockA), "T2").start();
}
}
class MyThread implements Runnable{
private String lockA;
private String lockB;
public MyThread(String lockA, String lockB) {
this.lockA = lockA;
this.lockB = lockB;
}
@Override
public void run() {
synchronized (lockA){
System.out.println(Thread.currentThread().getName() + "lock:" + lockA + "==>get" + lockB);
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (lockB){
System.out.println(Thread.currentThread().getName() + "lock:" + lockB + "==>get" + lockA);
}
}
}
}
使用 jps -l 查看进程号
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zBJWiiCT-1625034562678)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210617150250666.png)]
使用jstack查看相应进程
E:\workspace\demo1>jstack 9592
2021-06-17 15:02:20
Full thread dump Java HotSpot(TM) 64-Bit Server VM (25.181-b13 mixed mode):
"DestroyJavaVM" #14 prio=5 os_prio=0 tid=0x0000000000fde800 nid=0x3980 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"T2" #13 prio=5 os_prio=0 tid=0x000000001eb8d800 nid=0x42e4 waiting for monitor entry [0x000000002084e000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.demo.deadLock.MyThread.run(DeadLockTest.java:33)
- waiting to lock <0x000000076be900c8> (a java.lang.String)
- locked <0x000000076be90100> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
"T1" #12 prio=5 os_prio=0 tid=0x000000001eb8d000 nid=0x3988 waiting for monitor entry [0x000000002074f000]
java.lang.Thread.State: BLOCKED (on object monitor)
at com.demo.deadLock.MyThread.run(DeadLockTest.java:33)
- waiting to lock <0x000000076be90100> (a java.lang.String)
- locked <0x000000076be900c8> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
"Service Thread" #11 daemon prio=9 os_prio=0 tid=0x000000001ea82000 nid=0x4020 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C1 CompilerThread3" #10 daemon prio=9 os_prio=2 tid=0x000000001ea60800 nid=0x3778 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread2" #9 daemon prio=9 os_prio=2 tid=0x000000001ea5c800 nid=0x3f0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread1" #8 daemon prio=9 os_prio=2 tid=0x000000001ea5c000 nid=0x1e88 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"C2 CompilerThread0" #7 daemon prio=9 os_prio=2 tid=0x000000001ea53000 nid=0x41a4 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Monitor Ctrl-Break" #6 daemon prio=5 os_prio=0 tid=0x000000001ea4a000 nid=0x434c runnable [0x000000002004e000]
java.lang.Thread.State: RUNNABLE
at java.net.SocketInputStream.socketRead0(Native Method)
at java.net.SocketInputStream.socketRead(SocketInputStream.java:116)
at java.net.SocketInputStream.read(SocketInputStream.java:171)
at java.net.SocketInputStream.read(SocketInputStream.java:141)
at sun.nio.cs.StreamDecoder.readBytes(StreamDecoder.java:284)
at sun.nio.cs.StreamDecoder.implRead(StreamDecoder.java:326)
at sun.nio.cs.StreamDecoder.read(StreamDecoder.java:178)
- locked <0x000000076bfcaea0> (a java.io.InputStreamReader)
at java.io.InputStreamReader.read(InputStreamReader.java:184)
at java.io.BufferedReader.fill(BufferedReader.java:161)
at java.io.BufferedReader.readLine(BufferedReader.java:324)
- locked <0x000000076bfcaea0> (a java.io.InputStreamReader)
at java.io.BufferedReader.readLine(BufferedReader.java:389)
at com.intellij.rt.execution.application.AppMainV2$1.run(AppMainV2.java:61)
"Attach Listener" #5 daemon prio=5 os_prio=2 tid=0x000000001e9af000 nid=0x25a0 waiting on condition [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Signal Dispatcher" #4 daemon prio=9 os_prio=2 tid=0x000000001ea02800 nid=0x3eb8 runnable [0x0000000000000000]
java.lang.Thread.State: RUNNABLE
"Finalizer" #3 daemon prio=8 os_prio=1 tid=0x000000001e993000 nid=0x3160 in Object.wait() [0x000000001fcee000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076bd08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:144)
- locked <0x000000076bd08ed0> (a java.lang.ref.ReferenceQueue$Lock)
at java.lang.ref.ReferenceQueue.remove(ReferenceQueue.java:165)
at java.lang.ref.Finalizer$FinalizerThread.run(Finalizer.java:216)
"Reference Handler" #2 daemon prio=10 os_prio=2 tid=0x000000001d2bc800 nid=0x3090 in Object.wait() [0x000000001fbef000]
java.lang.Thread.State: WAITING (on object monitor)
at java.lang.Object.wait(Native Method)
- waiting on <0x000000076bd06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.Object.wait(Object.java:502)
at java.lang.ref.Reference.tryHandlePending(Reference.java:191)
- locked <0x000000076bd06bf8> (a java.lang.ref.Reference$Lock)
at java.lang.ref.Reference$ReferenceHandler.run(Reference.java:153)
"VM Thread" os_prio=2 tid=0x000000001d2b7000 nid=0x4370 runnable
"GC task thread#0 (ParallelGC)" os_prio=0 tid=0x00000000036a8800 nid=0x3350 runnable
"GC task thread#1 (ParallelGC)" os_prio=0 tid=0x00000000036aa000 nid=0x2d58 runnable
"GC task thread#2 (ParallelGC)" os_prio=0 tid=0x00000000036ab800 nid=0x37a4 runnable
"GC task thread#3 (ParallelGC)" os_prio=0 tid=0x00000000036ad000 nid=0x2d28 runnable
"GC task thread#4 (ParallelGC)" os_prio=0 tid=0x00000000036b0800 nid=0x3518 runnable
"GC task thread#5 (ParallelGC)" os_prio=0 tid=0x00000000036b1800 nid=0x1094 runnable
"GC task thread#6 (ParallelGC)" os_prio=0 tid=0x00000000036b4800 nid=0x2ad0 runnable
"GC task thread#7 (ParallelGC)" os_prio=0 tid=0x00000000036b6000 nid=0x10fc runnable
"GC task thread#8 (ParallelGC)" os_prio=0 tid=0x00000000036b7000 nid=0x34b8 runnable
"GC task thread#9 (ParallelGC)" os_prio=0 tid=0x00000000036b8000 nid=0x3a60 runnable
"VM Periodic Task Thread" os_prio=2 tid=0x000000001eb54800 nid=0x2ee0 waiting on condition
JNI global references: 12
Found one Java-level deadlock:
=============================
"T2":
waiting to lock monitor 0x000000001d2c08d8 (object 0x000000076be900c8, a java.lang.String),
which is held by "T1"
"T1":
waiting to lock monitor 0x000000001d2c2df8 (object 0x000000076be90100, a java.lang.String),
which is held by "T2"
Java stack information for the threads listed above:
===================================================
"T2":
at com.demo.deadLock.MyThread.run(DeadLockTest.java:33)
- waiting to lock <0x000000076be900c8> (a java.lang.String)
- locked <0x000000076be90100> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
"T1":
at com.demo.deadLock.MyThread.run(DeadLockTest.java:33)
- waiting to lock <0x000000076be90100> (a java.lang.String)
- locked <0x000000076be900c8> (a java.lang.String)
at java.lang.Thread.run(Thread.java:748)
Found 1 deadlock.