Java 线程池

类型

Java通过Executors提供四种线程池,分别为:

  • newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。

  • newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。

  • newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。

  • newSingleThreadExecutor创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

创建

虽然可以通过Executors.newFixedThreadPool(size)的方式来创建四种线程池,但是不推荐这种做法。因为线程队列 LinkedBlockingQueue没有设置固定容量大小,而且newCachedThreadPool是没有缓存队列的,直接开启新的线程,最终都会导致其无限增大,内存压力山大。

推荐创建方式,自定义ThreadPoolExecutor创建

new ThreadPoolExecutor(8, 16, 60, TimeUnit.SECONDS,
                            new LinkedBlockingQueue(32), new ThreadPoolExecutor.CallerRunsPolicy());
                            
public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler)

构造函数的参数:

  • corePoolSize 核心线程数

  • maximumPoolSize 线程池中的最大线程数量

  • keepAliveTime 当线程池中空闲线程数量超过corePoolSize时,多余的线程会在多长时间内被销毁;

  • unit:keepAliveTime的单位

  • workQueue 任务队列,它一般分为直接提交队列、有界任务队列、无界任务队列、优先任务队列几种;

  • threadFactory:线程工厂,用于创建线程,一般用默认即可;

  • handler:拒绝策略

AbortPolicy:直接抛出异常,这是默认策略CallerRunsPolicy:用调用者所在的线程来执行任务DiscardOldestPolicy:丢弃阻塞队列中靠最前的任务,并执行当前任务DiscardPolicy:直接丢弃任务

解析
  • 当线程池中线程数小于corePoolSize时,新提交任务将创建一个新线程执行任务,即使此时线程池中存在空闲线程。
  • 当线程池中线程数达到corePoolSize时,新提交任务将被放入workQueue中,等待线程池中任务调度执行 。
  • 当workQueue已满,且maximumPoolSize > corePoolSize时,新提交任务会创建新线程执行任务。
  • 当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理。
  • 当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收这些线程。
  • 当设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收。

状态

生命周期

线程池的运行状态:

  • Running:能接受新提交的任务,并且也能处理阻塞队列中的任务;

  • Shutdown:关闭状态,不再接受新提交的任务,但却可以继续处理阻塞队列中已保存的任务。在线程池处于 RUNNING 状态时,调用 shutdown()方法会使线程池进入到该状态。(finalize()方法在执行过程中也会调用shutdown()方法进入该状态);

  • Stop:不能接受新任务,也不处理队列中的任务,会中断正在处理任务的线程。在线程池处于

  • RUNNING 或 SHUTDOWN 状态时,调用 shutdownNow() 方法会使线程池进入到该状态;

  • Tidying:如果所有的任务都已终止了,workerCount (有效线程数) 为0,线程池进入该状态后会调用 terminated() 方法进入TERMINATED 状态。

  • Terminated:在terminated()方法执行完后进入该状态,默认terminated()方法中什么也没有做。进入terminated的条件如下:线程池不是RUNNING状态;线程池状态不是TIDYING状态或T- ERMINATED状态;如果线程池状态是SHUTDOWN并且workerQueue为空;workerCount为0;设置TIDYING状态成功。

任务提交

使用线程池,可以使用两个方法向线程池提交任务:

  • submit() 返回一个Future对象,通过调用这个对象的get()阻方法,获得返回结果。get()方法会一直阻塞,直到返回结果返回。为了避免任务执行异常被吞掉的问题,我们需要调用Future的get()方法处捕获异常

  • execute()方法。其中submit有返回值

  • shutdown() 不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会接受新的任务

  • shutdownNow()立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

  • awaitTermination(long timeout, TimeUnit unit)作用是执行完字线程再执行主线程,首先该方法会被阻塞,这时会执行子线程中的任务,子线程执行完毕后该方法仍然会被阻塞,因为shutdown()方法还未被调用,而代码中将shutdown的请求放在了awaitTermination之后,这样就导致了只有awaitTermination方法执行完毕后才会执行shutdown请求,这样就造成了死锁。 解决办法自然就很简单了,调换下位置就可以了。

  • isTerminated() 判断所有子线程是否全部执行完毕并关闭,如果全部执行完毕则返回true。注意除非首先调用shutdown或shutdownNow,否则isTerminated永不为true。若关闭后所有任务都已完成,则返回true。

public class Test {
    public static void main(String[] args) {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10,
                1, TimeUnit.MINUTES, new LinkedBlockingQueue(30));

        FutureTask<String> task = new FutureTask<String>(() -> {
            return "sumbit示例";
        });

        pool.submit(task);
        pool.shutdown(); //关闭线程池
        try {
            String str = task.get();//阻塞
            System.out.println(str);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

public class Test {
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        ThreadPoolExecutor pool = new ThreadPoolExecutor(5, 10,
                1, TimeUnit.MINUTES, new LinkedBlockingQueue(30));

        pool.execute(() -> {
            System.out.println("execute示例");
        });
        pool.shutdown();
    }
}

总结

  1. 线程池不要申明为本地变量,应该将线程池申明为全局的。

  2. 自定义ThreadPoolExecutor创建创建线程池,不要使用Executors创建、

  3. 合理设置线程数。
    CPU密集型任务 尽量使用较小的线程池,一般为CPU核心数+1
    IO密集型任务 可以使用稍大的线程池,一般为2*CPU核心数

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值