多线程—死锁

1、死锁

出现场景:当线程A拥有了A对象的锁,想要去获取B对象的锁;线程B拥有了B对象的锁,想要拥有A对象的锁,两个线程在获取锁的时候,都不会释放已经持有的锁,于是,就造成了死锁。

示例代码:

@Slf4j
public class ThreadTest {
    private static Object objectA = new Object();
    private static Object objectB = new Object();
    public static void main(String[] args) throws InterruptedException {
        Thread t2 = new Thread(()->{
            synchronized (objectA){
                log.debug("线程t2获取到了objectA");
                synchronized (objectB){
                    log.debug("线程t2获取到了objectB");
                }
            }
        },"t2");
        Thread t1 = new Thread(()->{
            synchronized (objectB){
                log.debug("线程t1获取到了objectB");
                synchronized (objectA){
                    log.debug("线程t1获取到了objectA");
                }
            }
        },"t1");
        t2.start();
        t1.start();
    }
}

如何检测死锁:

两种方法

(1)找到本机jconsole程序,直接在windows系统搜索就可以,打开是这个样子。

                

然后在本地进程里面选择你的进程,其实就是你的项目名称。然后点击连接,在点击不安全连接。                

                ​​​​​​​

 进去之后点击线程

                

 再点击检测死锁

        ​​​​​​​        

最后就能看到死锁的线程了

                 

(2)首先是在idea的控制台,打开Terminal,输入【jps】命令查看所有的进程id,找到你自己的java类名称对应的id。

然后输入【jstack + 进程号】 就可以查询到该进程的所有线程信息。在输出信息的最下面,就可以看到如下图所示的线程死锁信息。

         ​​​​​​​        

3、死锁经典问题——哲学家就餐问题 

 经典场景:有四位哲学及在一正方形的桌子上面吃饭,桌子的每个角有一根筷子,一共四根,那么,当每个哲学家都拿起自己左边的筷子之后,再去拿自己右边的筷子的时候,就会发现自己右边没有筷子,这时哲学就就会等右边的哲学家放下筷子,但是每个哲学家都是这个想法,那么都不会放下筷子,并且都拿不到右边的筷子,因此就造成了死锁。

 代码实现例子:

@Slf4j
public class Thread1 {
    public static void main(String[] args) throws InterruptedException {
        //筷子对象
        Chopsticks c1 = new Chopsticks("c1");
        Chopsticks c2 = new Chopsticks("c2");
        Chopsticks c3 = new Chopsticks("c3");
        Chopsticks c4 = new Chopsticks("c4");
        new Philosopher("李云龙",c1,c2).start();
        new Philosopher("赵刚",c2,c3).start();
        new Philosopher("魏和尚",c3,c4).start();
        new Philosopher("张大彪",c4,c1).start();
    }
}
//筷子
class  Chopsticks{
    private String name;

    public Chopsticks(String name) {
        this.name = name;
    }
}
//哲学家
@Slf4j
class Philosopher extends Thread{
    //名字
    private String name;
    //筷子
    private Chopsticks left;
    private Chopsticks right;

    public Philosopher(String name, Chopsticks left, Chopsticks right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    @Override
    public void run() {
        while(true){
            synchronized (right){
                synchronized (left){
                    eat(name);
                }
            }
        }
    }
    private void eat(String name){
        log.debug(name + "正在吃饭");
    }
}

 测试结果:可以实现吃饭操作,但是会出现场景中描述的问题,出现线程死锁。

 

Guess you like

Origin blog.csdn.net/qq_42251944/article/details/120879781