设为首页 加入收藏

TOP

深入浅出线程池(二)
2023-09-23 15:43:23 】 浏览:176
Tags:程池
n "task done..."; } }; FutureTask task = new FutureTask(callable); list.add(task); new Thread(task).start(); } list.forEach(future -> { try { System.out.println(future.get()); //处理执行结果 } catch (Exception e) { } }); long end = System.currentTimeMillis(); System.out.println("end...,time = " + (end - start)); } //执行结果 start... task done... task done... task done... task done... task done... end...,time = 2005

2.3、总结

  1. 多线程可以把一个任务拆分为几个子任务,多个子任务可以并发执行,每一个子任务就是一个线程。

  2. 多线程是为了同步完成多项任务,不是为了提高运行效率,而是为了提高资源使用效率来提高系统 的效率。

2.4、多线程的问题

上面示例中我们可以看到,如果每来一个任务,我们就创建一个线程,有很多任务的情况下,我们 会创建大量的线程,可能会导致系统资源的耗尽。同时,我们知道线程的执行是需要抢占CPU资源 的,那如果有太多的线程,就会导致大量时间用在线程切换的开销上。

再有,每来一个任务都需要创建一个线程,而创建一个线程需要调用操作系统底层方法,开销较 大,而线程执行完成后就被回收了。在需要大量线程的时候,创建线程的时间就花费不少了。

三、线程池

1、如何设计一个线程池

由于多线程的开发存在上述的一些问题,那我们是否可以设计一个东西来避免这些问题呢?当然可以! 线程池就是为了解决这些问题而生的。那我们该如何设计一个线程池来解决这些问题呢?或者说,一个线程池该具备什么样的功能?

1.1、线程池基本功能

  1. 多线程会创建大量的线程耗尽资源,那线程池应该对线程数量有所限制,可以保证不会耗尽系统资 源;

  2. 每次创建新的线程会增加创建时的开销,那线程池应该减少线程的创建,尽量复用已创建好的线 程;

1.2、线程池面临问题

  1. 我们知道线程在执行完自己的任务后就会被回收,那我们如何复用线程?

  2. 我们指定了线程的最大数量,当任务数超出线程数时,我们该如何处理?

1.3、创新源于生活

先假设一个场景:假设我们是一个物流公司的管理人员,要配送的货物就是我们的任务,货车就是 我们配送工具,我们当然不能有多少货物就准备多少货车。那当顾客源源不断的将货物交给我们配 送,我们该如何管理才能让公司经营的最好呢?

  1. 最开始货物来的时候,我们还没有货车,每批要运输的货物我们都要购买一辆车来运输;

  2. 当货车运输完成后,暂时还没有下一批货物到达,那货车就在仓库停着,等有货物来了立马就可以 运输;

  3. 当我们有了一定数量的车后,我们认为已经够用了,那后面就不再买车了,这时要是由新的货物来 了,我们就会让货物先放仓库,等有车回来在配送;

  4. 当618大促来袭,要配送的货物太多,车都在路上,仓库也都放满了,那怎么办呢?我们就选择临 时租一些车来帮忙配送,提高配送的效率;

  5. 但是货物还是太多,我们增加了临时的货车,依旧配送不过来,那这时我们就没办法了,只能让发 货的客户排队等候或者干脆不接受了;

  6. 大促圆满完成后,累计的货物已经配送完成了,为了降低成本,我们就将临时租的车都还了;

1.4、技术源于创新

基于上述场景,物流公司就是我们的线程池、货物就是我们的线程任务、货车就是我们的线程。我 们如何设计公司的管理货车的流程,就应该如何设计线程池管理线程的流程。

  1. 当任务进来我们还没有线程时,我们就该创建线程执行任务;

  2. 当线程任务执行完成后,线程不释放,等着下一个任务进来后接着执行;

  3. 当创建的线程数量达到一定量后,新来的任务我们存起来等待空闲线程执行,这就要求线程池有个 存任务的容器;

  4. 当容器存满后,我们需要增加一些临时的线程来提高处理效率;

  5. 当增加临时线程后依旧处理不了的任务,那就应该将此任务拒绝;

  6. 当所有任务执行完成后,就应该将临时的线程释放掉,以免增加不必要的开销;

2、线程池具体分析

上文中,我们讲了该如何设计一个线程池,下面我们看看大神是如何设计的;

2.1、 JAVA中的线程池是如何设计的

2.1.1、 线程池设计

看下线程池中的属性,了解线程池的设计。

public class ThreadPoolExecutor extends AbstractExecutorService {

    //线程池的打包控制状态,用高3位来表示线程池的运行状态,低29位来表示线程池中工作线程的数量 
    private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0)); 
    
    //值为29,用来表示偏移量
     private static final int COUNT_BITS = Integer.SIZE - 3; 

    //线程池的最大容量
     private static final int CAPACITY = (1 << COUNT_BITS) - 1; 

    //线程池的运行状态,总共有5个状态,用高3位来表示 
    private static final int RUNNING = -1 << COUNT_BITS;  //接受新任务并处理阻塞队列中的任务 

    private static final int SHUTDOWN = 0 << COUNT_BITS;  //不接受新任务但会处理阻塞队列中的任务  

    private static final int STOP = 1 << COUNT_BITS;  //不会接受新任务,也不会处理阻塞队列中的任务,并且中断正在运行的任务

    private static final int TIDYING = 2 << COUNT_BITS;  //所有任务都已终止, 工作线程数量为0,即将要执行terminated()钩子方法 

    private static final int TERMINATED =  3 << COUNT_BITS;  // terminated()方法已经执行结束

    //任务缓存队列,用来存放等待执行的任务
    private final BlockingQueue<Runnable> workQueue; 

    //全局锁,对线程池状态等属性修改时需要使用这个锁
    private final ReentrantLock mainLock = new ReentrantLock(); 

    //线程池中工作线程的集合,访问和修改需要持有全局锁
    private final HashSet<Worker> workers = new HashSet<Worker>(); 

    // 终止条件
    private final Condition termination = mainLock.newCondition(); 

    //线程池中曾经出现过的最大线程数 
    private int largestPoolSize; 
    
    //已完成任务的数量
    private long completedTaskCount; 
首页 上一页 1 2 3 4 5 6 下一页 尾页 2/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇2023最全Java面试题及答案汇总 下一篇结对编程队友个人项目互评

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目