动力节点首页 全国咨询热线:400-8080-105

绑定手机号,登录
手机号

验证码

微信登录
手机号登录
手机号

验证码

微信登录与注册
微信扫码登录与注册

扫码关注微信公众号完成登录与注册
手机号登录
首页 > 文章

详解线程饥饿

05-27 14:30 1186浏览
举报 T字号
  • 大字
  • 中字
  • 小字

“不患寡,而患不均”,如果线程优先级“不均”,在CPU繁忙的情况下,优先级低的线程得到执行的机会很小,就可能发生线程“饥饿”;持有锁的线程,如果执行的时间过长,也可能导致“饥饿”问题。简而言之,所谓的线程饥饿就是:一个或者多个线程因为种种原因无法获得所需要的资源,导致一直无法执行的状态。

下面我们举个简单的例子,来一窥线程饥饿:

public class TestStarvation {
                private static final Logger log = LoggerFactory.getLogger(TestStarvation.class);
 
    // return deploy result
    static String deploy() {
        return "deploying"
    }
    
    public static void main(String[] args) {
        //fixed thread
        ExecutorService waiterPool = Executors.newFixedThreadPool(2);
  
        waiterPool.execute(() -> {
            log.debug("get Approval...");
            
            //deploy in another thread
            Future<String> f = waiterPool.submit(() -> {
                log.debug("deploy");
                return deploy();
            });
            
            try {
                log.debug("deploy result: {}", f.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
        waiterPool.execute(() -> {
            log.debug("get Approval..");
            Future<String> f = waiterPool.submit(() -> {
                log.debug("deploy");
                return deploy();
            });
            try {
                log.debug("deploy result: {}", f.get());
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
        });
  
    }
}

当你运行这段代码时,会发现thread就会hung住, 原因是线程池就2个资源, 都被getApproval 抢占了,在方法内层的 deploy 由于和getApproval用的是同一个线程池,所以它需要等getApproval 释放资源,但是getApproval 需要等待内层的deploy 做完才能释放资源。造成了内层的线程饥饿。

当然上面是个极端例子,我们真实的case 中的原理也是一样,他们用了同一个线程池,而且他们的调用有嵌套,造成了大量的等待。处理线程嵌套的模式都是使用不同的线程池,来保证内部和外部没有竞争。知道了原因我们再定义一个fixed thread pool executorSerice, 把它作为  manifestListResultFuture 运行的线程池,这个问题就解决了。

CompletableFuture<List<Map<String, Object>>> manifestListResultFuture =
        CompletableFuture.supplyAsync(() -> manifestService.getManifests(cluster, ns) , executorSerice)
                .thenApply(manifestListResult -> {
                       
                     //do some logic
                });

当然,造成线程饥饿的原因不是单一的,Java 中导致饥饿的原因主要有:

1.高优先级线程吞噬所有的低优先级线程的 CPU 时间。

2.线程被永久堵塞在一个等待进入同步块的状态,因为其他线程总是能在它之前持续地对该同步块进行访问。

3.线程在等待一个本身也处于永久等待完成的对象(比如调用这个对象的 wait 方法),因为其他线程总是被持续地获得唤醒。

那么如何防止出现线程饥饿的情况呢?我们可以采用时间片轮转的方式。可以设置线程的优先级,会映射到下层的系统上面的优先级上,如非特别需要,尽量不要用,防止线程饥饿。想了解更多的解决方案我们可以在动力节点在线的免费视频课程中寻找答案。

0人推荐
共同学习,写下你的评论
0条评论
代码小兵498
程序员代码小兵498

153篇文章贡献528999字

相关课程 更多>

作者相关文章更多>

推荐相关文章更多>

Java面试题及答案整理

提枪策马乘胜追击04-21 20:01

Spring常见面试题

代码小兵92504-17 16:07

Java零基础实战项目——五子棋

代码小兵98804-25 13:57

Java string类详解

杨晶珍05-11 14:54

6道经典算法面试题

杨晶珍05-12 16:39

发评论

举报

0/150

取消