JAVA多线程(二)--线程池
一、线程池概念
顾名思义,线程池是管理线程的池子。使用线程池有以下优点:
- 降低线程创建和销毁的开销。
- 提高响应速度。用到时创建和直接使用已创建好的线程,速度肯定是不一样的。
- 提高线程可管理性。线程是稀缺资源,使用线程池可对线程进行统一分配、调优和监控。
二、JUC架构
1、Executor接口
Executor
接口是JUC架构的顶级接口,它只包含一个void execute(Runnable command)
方法,是一个执行线程的工具。
public interface Executor {
void execute(Runnable command);
}
2、ExecutorService接口
ExecutorService
接口继承于Executor
接口,向外提供了接收异步任务的服务。
public interface ExecutorService extends Executor {
// 接收单个异步任务
<T> Future<T> submit(Callable<T> task);
<T> Future<T> submit(Runnable task, T result);
Future<?> submit(Runnable task);
// 批量接收异步任务
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks)
throws InterruptedException;
<T> List<Future<T>> invokeAll(Collection<? extends Callable<T>> tasks,
long timeout, TimeUnit unit)
throws InterruptedException;
}
3、AbstractExecutorService抽象类
抽象类,实现了ExecutorService
接口
4、ScheduledExecutorService接口
ScheduledExecutorService
接口继承了ExecutorService
接口,是一个可以完成'延时'和'周期性'任务的调度线程池接口。
5、ThreadPoolExecutor类
ThreadPoolExecutor
类继承了AbstractExecutorService
抽象类,是线程池中核心实现类。
6、ScheduledThreadPoolExecutor类
ScheduledThreadPoolExecutor
类实现了ScheduledExecutorService
接口,继承了ThreadPoolExecutor
类。拓展实现了延时执行和周期执行等抽象方法。
7、Executors
静态工厂类,它通过静态工厂方法返回ExecutorService
、ScheduledExecutorService
等线程池示例对象
三、使用Executors静态工厂类创建线程池
1、newFixedThreadPool 固定线程数的线程池
创建一个可重用固定线程数的线程池,以共享的无界队列方式来运行这些线程。如果所有线程处于活动状态时提交附加任务,则在有可用线程之前,附加任务将在队列中等待。如果在执行期间有由于失败导致任何线程终止,那么一个新线程将代替它执行后续任务。在某个线程被显示的关闭之前,池中的线程将一直存在。
弊端:阻塞队列是无界队列,大量任务涌入时,会导致队列很大,容易导致JVM出现OOM异常,即内存溢出。
2、newSingleThreadExecutor 单个线程的线程池
创建一个只有一个线程的线程池。这个线程池可以在线程死后(或发生异常),重新启动一个线程来替代原先的线程继续执行后续任务。
弊端:同固定数量线程池,阻塞队列无界。
3、newCachedThreadPool 缓存线程池
创建一个可根据需要创建新线程的线程池,但是在以前线程可用时将重用它们。当前线程池中有可用线程时将重用已有线程,当线程池中无可用线程将创建新的线程加入到线程池中执行新的任务。终止并移除那些已有60s未被使用的线程。
弊端:线程数没有限制,由于其maximumPoolSize的值为Integer.MAX_VALUE(非常大),可以认为可以无限创建线程,如果任务提交较多,就会造成大量的线程被启动,很有可能造成OOM异常,甚至导致CPU线程资源耗尽
4、newScheduledThreadPool 可调度线程池
创建一个可安排在给定延迟后执行任务的线程池。线程池中包含最小限度固定数量的线程corePoolSize
,即使空闲状态也一直存在,除非设置了allowCoreThreadTimeOut
。当初始线程不够时会创建新的线程加入线程池,这部分线程会因为限制状态被释放
弊端:线程数不设上限
5、newSingleThreadScheduledExecutor 单个线程的可调度线程池
创建一个corePoolSize 为1
的可安排在给定延迟后执行任务的线程池。
弊端:线程数不设上限
6、newWorkStealingPool 工作窃取式线程池
Java8新增的创建线程池方法。实际返回ForkJoinPool
对象,创建时如果不设置任何参数,则以当前机器处理器个数作为线程个数,此线程池会并行处理任务,不能保证执行顺序。使用所有可用的处理器作为其目标并行度级别创建一个窃取工作的线程池。
使用场景:能够合理的使用CPU进行对任务操作(并行操作),适合使用在很耗时的任务中。底层用的ForkJoinPool 来实现的。 ForkJoinPool的优势在于,可以充分利用多cpu,多核cpu的优势,把一个任务拆分成多个“小任务”分发到不同的cpu核心上执行,执行完后再把结果收集到一起返回。
四、使用ThreadPoolExecutor创建线程池
其实Executors
中除去Java8新加的newWorkStealingPool
方法外(调用的ForkJoinPool
),其他的创建线程池的方法基本上都是调用了ThreadPoolExecutor
的构造函数。
ThreadPoolExecutor
提供了一系列属性,供我们根据实际业务来创建合适的线程池;
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
int corePoolSize
: 核心线程最大数量,通俗点来讲就是,线程池中常驻线程的最大数量int maximumPoolSize
: 线程池中运行最大线程数(包括核心线程和非核心线程)long keepAliveTime
: 线程池中空闲线程(仅适用于非核心线程)所能存活的最长时间TimeUnit unit
: 存活时间单位,与keepAliveTime搭配使用BlockingQueue<Runnable> workQueue
:存放任务的阻塞队列ThreadFactory threadFactory
:新线程的产生方式RejectedExecutionHandler handler
: 线程池拒绝策略
注:若调用了allowCoreThreadTimeOut(boolean)方法,并且传入了参数true,则keepAliveTime参数所设置的Idle超时策略也将被应用于核心线程
五、向线程池提交任务
1、execute()
void execute(Runnable command)
Executor
接口中的方法, ThreadPoolExecutor
中实现,ScheduledThreadPoolExecutor
中重写等。
只能接收Runnable
,无返回值
2、submit()
<T> Future<T> submit(Callable<T> task)
<T> Future<T> submit(Runnable task, T result)
Future<?> submit(Runnable task)
这3个submit()
方法都是ExecutorService
接口中定义的方法, AbstractExecutorService
中实现,ScheduledThreadPoolExecutor
中重写等。
可以接收Callable``Runnable
,有返回值。
3、invokeAny()
批量提交,返回第一个返回值。
4、invokeAll()
批量提交,返回所有返回值。