双非本科准备秋招(20.2)—— 线程活跃性:死锁、活锁、饥饿

一、死锁

        一个线程需要获得多把锁,就容易出现死锁。

        比如此时有两把锁,分别是A和B。线程1首先需要获得A,然后获得B;线程2首先需要获得B,然后获得A。于是两个线程就一直等待对方释放锁。

双非本科准备秋招(20.2)—— 线程活跃性:死锁、活锁、饥饿_第1张图片

二、死锁之哲学家就餐

双非本科准备秋招(20.2)—— 线程活跃性:死锁、活锁、饥饿_第2张图片

一个圆桌,五个人,五只筷子,每个人吃饭需要拿起左右两边的筷子吃,设计代码:

设计筷子类

class Chopstick{
    String name;

    public Chopstick(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "筷子{" +
                "name='" + name + '\'' +
                '}';
    }
}

设计哲学家类

@Slf4j(topic = "c.phi")
class philosopher extends Thread{
    Chopstick left;
    Chopstick right;

    public philosopher(String name, Chopstick left, Chopstick right) {
        super(name);
        this.left = left;
        this.right = right;
    }

    private void eat(){
        log.debug("吃饭吃饭");
        try {
            Thread.sleep(400);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void run(){
        while (true){
            synchronized (left){
                synchronized (right){
                    eat();
                }
            }
        }
    }
}

main中测试代码如下:

public class testPhilosopher {
    public static void main(String[] args) {
        Chopstick c1 = new Chopstick("1");
        Chopstick c2 = new Chopstick("2");
        Chopstick c3 = new Chopstick("3");
        Chopstick c4 = new Chopstick("4");
        Chopstick c5 = new Chopstick("5");

        new philosopher("柏拉图", c1, c2).start();
        new philosopher("康德", c2, c3).start();
        new philosopher("尼采", c3, c4).start();
        new philosopher("庄子", c4, c5).start();
        new philosopher("马克思", c5, c1).start();
    }
}

可以看到,执行不下去了:

双非本科准备秋招(20.2)—— 线程活跃性:死锁、活锁、饥饿_第3张图片

死锁定位

我们先用jps命令定位进程id,可以看到id为6620

双非本科准备秋招(20.2)—— 线程活跃性:死锁、活锁、饥饿_第4张图片

再使用jstack 6620查看:这样就能看到死锁的准确定位了

Found one Java-level deadlock:
=============================
"马克思":
  waiting to lock monitor 0x0000000020521d58 (object 0x000000076bf84248, a com.smy.day4.Chopstick),
  which is held by "柏拉图"
"柏拉图":
  waiting to lock monitor 0x000000001cdd2868 (object 0x000000076bf84288, a com.smy.day4.Chopstick),
  which is held by "康德"
"康德":
  waiting to lock monitor 0x000000001cdd27b8 (object 0x000000076bf842c8, a com.smy.day4.Chopstick),
  which is held by "尼采"
"尼采":
  waiting to lock monitor 0x000000001cdcff28 (object 0x000000076bf84308, a com.smy.day4.Chopstick),
  which is held by "庄子"
"庄子":
  waiting to lock monitor 0x000000001cdcffd8 (object 0x000000076bf84348, a com.smy.day4.Chopstick),
  which is held by "马克思"

        at com.smy.day4.philosopher.run(testPhilosopher.java:62)
        - waiting to lock <0x000000076bf84288> (a com.smy.day4.Chopstick)
        - locked <0x000000076bf84248> (a com.smy.day4.Chopstick)
"康德":
        at com.smy.day4.philosopher.run(testPhilosopher.java:62)
        - waiting to lock <0x000000076bf842c8> (a com.smy.day4.Chopstick)
        - locked <0x000000076bf84288> (a com.smy.day4.Chopstick)
"尼采":
        at com.smy.day4.philosopher.run(testPhilosopher.java:62)
        - waiting to lock <0x000000076bf84308> (a com.smy.day4.Chopstick)
        - locked <0x000000076bf842c8> (a com.smy.day4.Chopstick)
"庄子":
        at com.smy.day4.philosopher.run(testPhilosopher.java:62)
        - waiting to lock <0x000000076bf84348> (a com.smy.day4.Chopstick)
        - locked <0x000000076bf84308> (a com.smy.day4.Chopstick)

Found 1 deadlock.

三、活锁

活锁出现在两个线程互相改变对方的结束条件,最后谁也无法结束。

比如:cnt=5

线程1一直循环,退出条件是cnt <10,每次-1sleep1秒

线程2一直循环,退出条件是cnt >0,每次+1sleep1秒

这样cnt一直保持加一减一,谁也满足不了条件,就形成了活锁。

四、饥饿

一个线程始终得不到 CPU 调度执行,也不能够结束。

比如哲学家问题,我们可以用顺序加锁的方式解决死锁。

因为他们要持有的筷子是

1 2

2 3

3 4

4 5

5 1

这样,有可能每个人都持有1,2,3,4,5导致死锁,那我们把最后一个改成1 5,这样就破坏了死锁的条件,1这把筷子会被竞争。

但是,当我们这样修改之后,马克思一直吃不上饭,进入了饥饿状态。

这是吃饭时间为300ms的情况:

双非本科准备秋招(20.2)—— 线程活跃性:死锁、活锁、饥饿_第5张图片

吃饭时间为10ms时:可以发现,这时候某个线程总能抢到筷子,但是有的线程永远拿不到,饥饿的线程越来越饥饿。庄子需要4 5筷子,马克思需要1 5,但是马克思抢不到饭,因为他执行的晚,一直抢不到1筷子,所以庄子需要的5筷子就没人用,所以庄子总是能顺利进行。

双非本科准备秋招(20.2)—— 线程活跃性:死锁、活锁、饥饿_第6张图片

你可能感兴趣的:(死锁,活锁,饥饿,并发编程,秋招,java,锁)