重温线程学习

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、静态代理

代理对象可以做真实对象做不了的事情

  1. 真实对象和代理对象都要实现同一个接口

  2. 代理对象要代理真实对象

3、线程安全性

多个线程操作同一个资源的时候,线程安全性问题

  1. synchronized

    同步方法,锁的是当前对象(在方法上用synchronized修饰)

    同步块:synchronized(obj){},可以锁任何对象

    synchronized 锁的对象是方法的调用者

  2. 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);
        }
    }
}
  1. 使用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();
        }
    }
}
二、关于锁的几个问题
  1. 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();
        }
    
  2. 如果在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("打电话");
        }
    }
    
  3. 普通的方法和同步方法,先调用普通方法,同一个对象而言

  4. 关键在于看,锁的对象是否是同一个,即是否使用的是同一把锁

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)禁止指令重排
  1. 保证特定操作的执行顺序
  2. 可以保证某些变量的内存可见性

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.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值