03.线程活跃性问题

85 阅读4分钟

线程活跃性问题

1.介绍

活跃性是指某件正确的事情最终会发生,当某个操作无法继续下去的时候,就会发生活跃性问题。在多线程中一般有死锁、活锁和饥饿问题

2.死锁

介绍

死锁是指两个或两个以上的线程相互等待对方释放已经持有的锁,从而导致所有涉及的线程都无法继续执行的情况

出现条件

这四个条件是形成死锁的必要条件,非充分条件,即同时满足这四个条件也不一定会产生死锁,但是产生了死锁,肯定是满足了这四个条件。只有四个条件之一不满足,肯定不会产生死锁

  1. 互斥:线程对资源的访问是互斥的,即一次只能一个线程访问资源
  2. 占有并等待:线程已经占有至少一个资源,并且在等待其他线程占有的资源
  3. 不可剥夺:资源不能被强制性地从一个线程中剥夺,只能由占有它的线程释放。
  4. 循环等待条件:有一组等待线程{P0,P1,…,Pn},P0等待的资源为P1占有,P1等待的资源为P2占有,形成一个循环等待链

代码示例

public class DeadLockDemo {
	public static void main(String[] args) throws Exception{
	     Object lock1 = new Object();
	     Object lock2 = new Object();
	     Thread t1 = new Thread(() -> {
	         synchronized (lock1) {
	             System.out.println("t1获取lock1");
	             try {
	                 Thread.sleep(1000);
	             } catch (InterruptedException e) {
	                 e.printStackTrace();
	             }
	             synchronized (lock2) {
	                 System.out.println("t1获取lock2");
	             }
	         }
	     });
	     Thread t2 = new Thread(() -> {
	         synchronized (lock2) {
	             System.out.println("t2获取lock2");
	             try {
	                 Thread.sleep(1000);
	             } catch (InterruptedException e) {
	                 e.printStackTrace();
	             }
	             synchronized (lock1) {
	                 System.out.println("t2获取lock1");
	             }
	         }
	     });
	     t1.start();
	     t2.start();
	     t1.join();
	     t2.join();
	     System.out.println("执行完毕");
	}
}

如何避免死锁

  1. 开放调用:不要在持有锁的情况下调用其他方法,所以要尽量缩小锁的范围,只对共享可变变量的操作上锁即可,这种方法是避免了循环等待条件的形成
  2. 固定获取资源的顺序:对于线程获取资源的顺序进行限制,可以对资源进行大小排序,获取资源时要从最大的资源开始获取,这种方法也是避免了循环等待条件的形成
  3. 使用超时锁:超时锁可以在锁使用一段时间后,自动释放锁,这种方法是打破了不剥夺条件
  4. 每次只获取一个锁:每次只获取一个锁,避免锁顺序死锁,循环等待条件

死锁的检测

省略,在JVM里面写

3.活锁

介绍

所谓活锁,就是两个以上的线程在执行的时候,因为相互谦让资源,结果都拿不到资源,没法运行程序

两个线程互相改变对象的结束条件,最后谁也无法结束

代码示例

/**
 * 演示活锁问题
 */
public class LiveLock {
    //勺子类
    static class Spoon {
        //勺子所属者
        private Diner owner;

        public Spoon(Diner owner) {
            this.owner = owner;
        }

        public Diner getOwner() {
            return owner;
        }

        public void setOwner(Diner owner) {
            this.owner = owner;
        }

        public synchronized void use() {
            System.out.printf("%s has eaten!", owner.name);
        }
    }
    //就餐者类
    static class Diner {
        //就餐者名字
        private String name;
        //就餐或饥饿
        private boolean isHungry;

        public Diner(String name) {
            this.name = name;
            isHungry = true;
        }

        //就餐者吃饭方法,spoon 勺子,spouse 和我吃饭的人
        public void eatWith(Spoon spoon, Diner spouse) {
            //如果自己是饥饿的
            while (isHungry) {
                //判断勺子的持有者是不是自己
                if (spoon.owner != this) {
                    try {
                        Thread.sleep(1);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    continue;
                }
                //判断陪我吃饭的人是不是饥饿,如果是就把勺子给他
                if (spouse.isHungry) {
                    System.out.println(name + "亲爱的" + spouse.name + "你先吃");
                    spoon.setOwner(spouse);
                    continue;
                }

                spoon.use();
                isHungry = false;
                System.out.println(name + ":我吃完了");
                //吃完吧勺子给对方
                spoon.setOwner(spouse);
            }
        }
    }

    public static void main(String[] args) {
        Diner husband = new Diner("牛郎");
        Diner wife = new Diner("织女");

        Spoon spoon = new Spoon(husband);
        new Thread(new Runnable() {
            @Override
            public void run() {
                husband.eatWith(spoon, wife);
            }
        }).start();
        new Thread(new Runnable() {
            @Override
            public void run() {
                wife.eatWith(spoon, husband);
            }
        }).start();
    }
}

避免死锁方式(加入随机因素)

	//判断陪我吃饭的人是不是饥饿,如果是就把勺子给他
	//用随机数是其产生一个打破僵局的可能
	Random random = new Random();
	if (spouse.isHungry && random.nextInt(10)<9) {
		System.out.println(name + "亲爱的" + spouse.name + "你先吃");
		spoon.setOwner(spouse);
		continue;
	}

4.饥饿

介绍

当线程需要某些资源(例如CPU),但是却始终得不到,造成一直得不到执行

产生的情况

  1. 高优先级线程吞噬所有的低优先级线程的CPU时间
  2. 线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问

解决思路

  1. 保证资源充足:上面的例子就是不要只有一个饭勺
  2. 公平地分配资源:把非公平换为公平,让每个线程都有执行到的机会

5.总结

asdasdxzczasd20250610170811.png