多线程饥饿现象,饥饿与死锁区别

饥饿和饿死的概念、饥饿与死锁区别

  1. 饥饿是指系统不能保证某个进程的等待时间上界,从而使该进程长时间等待,当等待时间给进程推进和响应带来明显影响时,称发生了进程饥饿。当饥饿到一定程度的进程所赋予的任务即使完成也不再具有实际意义时称该进程被饿死。
  2. 死锁是指在多道程序系统中,一组进程中的每一个进程都无限期等待被该组进程中的另一个进程所占有且永远不会释放的资源。关于死锁具体介绍大家可以看我另外一篇的总结死锁
  • 死锁和饥饿的不同
  • 相同点:二者都是因为竞争资源引起的。
  • 不同点:我觉得可以这么理解死锁和饥饿的区别,首先死锁是同步的,饥饿时异步的。也就是说,死锁可以认为是两个及以上线程或进程同时在请求对方占有的资源。饥饿可以认为是一个或以上线程或是进程在无限的等待另外两个或多个线程或进程占有的但是不会往外释放的资源。就是死锁里资源的占有方和资源的拥有方互相请求对方的资源,但是饥饿时一方请求不知道哪一方的资源,就是饿了需要进食,但是谁给都行。
  1. 从进程状态考虑,死锁进程都处于等待状态,忙等待(处于运行或就绪状态)的进程并非处于等待状态,但却可能被饿死;

  2. 死锁进程等待永远不会被释放的资源,饿死进程等待会被释放但却不会分配给自己的资源,表现为等待时限没有上界(排队等待或忙式等待);

  3. 死锁一定发生了循环等待,而饿死则不然。这也表明通过资源分配图可以检测死锁存在与否,但却不能检测是否有进程饿死;

  4. 死锁一定涉及多个进程,而饥饿或被饿死的进程可能只有一个。

  5. 在饥饿的情形下,系统中有至少一个进程能正常运行,只是饥饿进程得不到执行机会。而死锁则可能会最终使整个系统陷入死锁并崩溃

介绍“死锁”的例子1:如果线程A锁住了记录R1并等待记录R2,而线程B锁住了记录R2并等待记录R1,这样两个线程A和B就发生了死锁现象。
介绍“死锁”的例子2:两个山羊过一个独木桥,两只羊同时走到桥中间,一个山羊等另一个山羊过去了然后再过桥,另一个山羊等这一个山羊过去,结果两只山羊都堵在中间动弹不得。
介绍饥饿的例子:资源在其中两个或以上线程或进程相互使用,第三方线程或进程始终得不到。想像一下三个人传球,其中两个人传来传去,第三个人始终得不到。

案例导引

让有限多的工作线程来轮流异步处理无限多的工作任务,也可以将其归类为分工模式,典型实现就是线程池,也体现经典设计模式中的享元模式。
例如:饭店给每个客人来了,都配备一个新的服务员那么开销资源消耗太大。

注意:不同的类型应该使用不同的线程池,这样能避免饥饿,提升效率。
例如:饭店切菜就是切菜的员工,服务员就是服务员。

产生饥饿

  • 下面代码会产生饥饿问题,线程池中两个线程都去招待客人,没有
public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(2);
        pool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"招待顾客");
            Future<String> submit = pool.submit(() -> {
                System.out.println(Thread.currentThread().getName()+"做宫保鸡丁");
                return "去做宫保鸡丁";
            });
        });
        pool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"招待顾客");
            Future<String> submit = pool.submit(() -> {
                System.out.println(Thread.currentThread().getName()+"做水煮鱼");
                return "去做水煮鱼";
            });
        });
    }

在这里插入图片描述

饥饿解决

暂时解决饥饿问题

  • 这里我们修改一下,讲线程池中的线程数改为3这样就可以暂时解决饥饿问题。通过扩大线程池中线程数方法来解决。
public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        pool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"招待顾客");
            Future<String> submit = pool.submit(() -> {
                System.out.println(Thread.currentThread().getName()+"做宫保鸡丁");
                return "去做宫保鸡丁";
            });
        });
        pool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"招待顾客");
            Future<String> submit = pool.submit(() -> {
                System.out.println(Thread.currentThread().getName()+"做水煮鱼");
                return "去做水煮鱼";
            });
        });
    }

在这里插入图片描述
这里我们来说饥饿问题,大家要把饥饿和死锁问题区分开
这里我们能够看到,饥饿它是两个或者几个线程需要继续向下执行但是需要别的资源来保证自己向下执行,死锁需要最少两个线程他们之间的运行需要对方的数据资源,来帮助自己向下执行,饥饿时不能通过Jconcle检测出来的因为他们之间不竞争资源,但是死锁可以通过工具检测出来,因为线程间存在资源的竞争。

解决饥饿问题

多创建一个线程池解决问题,做菜线程池就是做菜线程池,服务员线程池就是服务员线程池。各个线程池各司其职。

public static void main(String[] args) throws ExecutionException, InterruptedException {
        ExecutorService waiterpool = Executors.newFixedThreadPool(1);
        ExecutorService cookpool = Executors.newFixedThreadPool(1);
        waiterpool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"招待顾客");
            Future<String> submit = cookpool.submit(() -> {
                System.out.println(Thread.currentThread().getName()+"做宫保鸡丁");
                return "去做宫保鸡丁";
            });
        });
        waiterpool.execute(()->{
            System.out.println(Thread.currentThread().getName()+"招待顾客");
            Future<String> submit = cookpool.submit(() -> {
                System.out.println(Thread.currentThread().getName()+"做水煮鱼");
                return "去做水煮鱼";
            });
        });
    }

在这里插入图片描述

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值