📜  Java多线程-线程池(1)

📅  最后修改于: 2023-12-03 14:43:00.336000             🧑  作者: Mango

Java多线程-线程池

1. 简介

在Java多线程编程中,经常需要创建和销毁线程,这是非常消耗资源的操作,如果并发量比较大,会导致CPU和内存使用率飙升,甚至出现OOM等问题。为了解决这一问题,Java提供了线程池的概念。

线程池是一种能够重复利用线程的机制,它维护着若干个工作线程,等待着任务分配执行。线程池中的工作线程可以执行任何队列中的任务。当任务执行完毕后,线程并不会立即销毁,而是会继续等待新的任务。

2. 好处

相对于自己手动创建和管理线程,使用线程池的好处有以下几点:

  • 提高系统的响应速度:线程池中的工作线程已经创建好了,直接使用即可,不需要再花费时间创建和销毁线程。
  • 提高系统的稳定性:线程池中的工作线程是可复用的,不会因为创建和销毁大量线程导致系统崩溃。
  • 提高程序的可扩展性:线程池中可以控制最大线程数,根据系统负载动态调整线程数量。
  • 提高程序运行的效率:线程池中的工作线程可以执行任何队列中的任务,减少了线程上下文切换的开销。
3. 基本用法

Java内置了线程池的实现类ThreadPoolExecutor,它的构造函数如下:

public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue);

这里的五个参数含义如下:

  • corePoolSize:线程池的核心线程数。
  • maximumPoolSize:线程池的最大线程数。
  • keepAliveTime:线程池中空闲线程的存活时间。
  • unitkeepAliveTime的时间单位。
  • workQueue:用于存放任务的阻塞队列。

ThreadPoolExecutor可以按照以下方式创建线程池:

// 创建一个核心线程数为4,最大线程数为10,阻塞队列大小为100的线程池 
ExecutorService executor = new ThreadPoolExecutor(4, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100));

线程池创建后,可以将任务提交给线程池执行:

executor.submit(new Runnable() {
    @Override
    public void run() {
        // 任务具体执行的代码
    }
});

最后,需要注意:使用完线程池之后,一定要调用线程池的shutdown方法,以便让线程池正确地关闭并释放资源。

executor.shutdown();
4. 线程池的常用方法

除了上述的构造方法和submit方法之外,ThreadPoolExecutor还提供了一些常用的方法,如下:

4.1 execute

该方法用于提交一个Runnable任务到线程池中执行。与submit方法不同的是,execute方法没有返回值。

executor.execute(new Runnable() {
    @Override
    public void run() {
        // 任务具体执行的代码
    }
});
4.2 shutdown

该方法用于关闭线程池。调用该方法后,线程池不接收新任务,但会将之前提交的任务继续执行完毕。

executor.shutdown();
4.3 awaitTermination

该方法用于阻塞当前线程,直到线程池中的所有任务都执行完毕。在所有任务执行完毕之前,主线程会被一直阻塞。该方法需要与shutdown方法配合使用。

// 等待所有任务执行完毕
executor.awaitTermination(1, TimeUnit.MINUTES);
4.4 isShutdown

该方法用于判断线程池是否已经关闭。

boolean isShutdown = executor.isShutdown();
4.5 isTerminated

该方法用于判断线程池是否已经关闭并且所有任务已经执行完毕。

boolean isTerminated = executor.isTerminated();
5. 线程池的拒绝策略

当阻塞队列的容量已满并且新增任务到来时,线程池的策略就会发生变化。这时,线程池提供了四种拒绝策略:

  • AbortPolicy:直接抛出RejectedExecutionException异常。
  • CallerRunsPolicy:让提交任务的线程自己去执行该任务。
  • DiscardOldestPolicy:直接丢弃队列中最老的任务,然后再尝试提交新的任务。
  • DiscardPolicy:直接丢弃新的任务,不做任何处理。

默认情况下,线程池采用的是AbortPolicy策略。如果需要更换其他策略,可以通过以下方式设置:

ExecutorService executor = new ThreadPoolExecutor(4, 10, 60L, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>(100), new DiscardPolicy());