介绍
什么是线程池,顾名思义,就是一个容纳线程的池子。线程需要被创建和销毁,在一个多线程的环境下需要很多的线程来同时执行一些事情,线程池可以存储线程,定义线程的执行数量,时间,间隔等。
在java 中存在以下5种线程池
- CachedThreadPool
- FixedThreadPool
- ScheduledThreadPool
- SingleThreadExecutor
- WorkStealingPool
其中WorkStealingPool是java1.8新增的线程池
其中前4种的创建都调用了下面的方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
其中的参数
参数名 | 含义 |
---|---|
corePoolSize | 线程池中核心的线程数目,就算没事干也不会被销毁 |
maximumPoolSize | 线程池中最大线程数,当创建的线程多于此数会进入阻塞状态直到有空闲线程 |
keepAliveTime | 活跃的时间 为0时表示永久 |
unit | 时间单位 (分,秒之类的) |
workQueue | 任务的阻塞队列,当任务阻塞就进入该队列之后可以取出 |
threadFactory | 执行创建线程时使用的线程工厂 一般默认就好 |
defaultHandler | 对阻塞任务采取的方法 一般默认就好 |
并且在遇到下列情况时候会抛出异常
/** @throws IllegalArgumentException if one of the following holds:<br>
* {@code corePoolSize < 0}<br>
* {@code keepAliveTime < 0}<br>
* {@code maximumPoolSize <= 0}<br>
* {@code maximumPoolSize < corePoolSize}
* @throws NullPointerException if {@code workQueue} is null
* /
CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
可以进行缓存的线程池,它的容量是最大的并且核心线程数目为0,意味着可以创建无限个线程,但这不太符合实际而且要考虑到线程的销毁,但可以灵活的回收空闲的线程,默认存活时间60秒。
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for (int i=0;i<100;i++)
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("threadName : "+Thread.currentThread().getName());
}
});
}
执行一百个任务当创建线程空闲时复用线程如果没有空闲的就创建个新的 最终的结果最多用到了11个
FixedThreadPool
固定线程池,也就是规定了几个线程就是几个线程定义如下
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
核心线程池数目与最大线程池数目相同,可以控制线程并发的数目,超出时的线程进入阻塞队列
public static void main(String[] args) {
ExecutorService service = Executors.newFixedThreadPool(3);
for (int i=0;i<10;i++)
service.execute(new Runnable() {
@Override
public void run() {
System.out.println("threadName : "+Thread.currentThread().getName());
Thread.yield();
}
});
}
这里使用了Thread.yield()方法合理分配cpu的时间片最终线程池中的线程会每次执行3个任务
ScheduledThreadPool
这个就是一个定长的可以计划延迟执行任务并且间隔一段时间执行一次的线程池类似于一个定时器
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
--------------------------------------------------------------------------------------
public ScheduledThreadPoolExecutor(int corePoolSize) {
super(corePoolSize, Integer.MAX_VALUE, 0, NANOSECONDS,
new DelayedWorkQueue());
}
ScheduledThreadPoolExecutor继承了ThreadPoolExecutor实现了能定时完成任务的方法
ScheduledExecutorService scheduleThreadPool = Executors.newScheduledThreadPool(3);
scheduleThreadPool.schedule(new Runnable() {
@Override
public void run() {
System.out.println("3 second later");
}
},3,TimeUnit.SECONDS);
scheduleThreadPool.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
System.out.println(new Date());
}
},3,2,TimeUnit.SECONDS);
再上面的代码中先是执行了一个三秒后执行的任务 ,然后是一个延迟3秒后执行并且每两秒执行一次的任务,最终的执行结果如下
SingleThreadExecutor
这个线程池看名字就知道是一个单线程的线程池,它会有一个生命时间无线的线程来逐个执行任务
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
static class FinalizableDelegatedExecutorService
extends DelegatedExecutorService {
FinalizableDelegatedExecutorService(ExecutorService executor) {
super(executor);
}
protected void finalize() {
super.shutdown();
}
}
可以看到线程池的核心线程数为1 最大线程数为1 时间为0即一直存在 但是它外面套了个FinalizableDelegatedExecutorService其实是实现了finalize()方法,也就是虚拟机在执行垃圾回收机制的时候会掉用该方法,它掉用了shutdown()方法,也就不需要手动的调用来关闭线程池了。下面是线程池的执行情况,全都由一个线程来执行。
ExecutorService singleThreadPool = Executors.newSingleThreadExecutor();
for (int i = 0;i<10;i++){
singleThreadPool.execute(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
});
}
WorkStealingPool
/**
* Creates a work-stealing thread pool using all
* {@link Runtime#availableProcessors available processors}
* as its target parallelism level.
* @return the newly created thread pool
* @see #newWorkStealingPool(int)
* @since 1.8
*/
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
这个线程池是java1.8新增的线程池,大致意思就是抢占式的线程池,谁抢到了就让谁来执行,并且是通过子类ForkJoinPool来创建。ForkJoinPool可以将一个大任务分成多个小任务执行并且返回合并。具体使用这里不多细说先给个详细介绍的连接
ForkJoinPool介绍
ExecutorService workStealingPool = Executors.newWorkStealingPool();
for (int i = 0; i < 100; i++) {
workStealingPool.execute(() -> {
try {
System.out.println("aaa"+Thread.currentThread().getName());
} catch (Exception e) {
e.printStackTrace();
}
}
);
}
/**
* Creates a work-stealing thread pool using all
* {@link Runtime#availableProcessors available processors}
* as its target parallelism level.
* @return the newly created thread pool
* @see #newWorkStealingPool(int)
* @since 1.8
*/
public static ExecutorService newWorkStealingPool() {
return new ForkJoinPool
(Runtime.getRuntime().availableProcessors(),
ForkJoinPool.defaultForkJoinWorkerThreadFactory,
null, true);
}
/**
* Returns the number of processors available to the Java virtual machine.
*
* <p> This value may change during a particular invocation of the virtual
* machine. Applications that are sensitive to the number of available
* processors should therefore occasionally poll this property and adjust
* their resource usage appropriately. </p>
*
* @return the maximum number of processors available to the virtual
* machine; never smaller than one
* @since 1.4
*/
public native int availableProcessors();
由于是抢占式的每次的结果都会不一样谁执行好了就执行下面的任务,它会先获取运行时候可以使用的进程processors,所有没有的时候也经常会什么都没有输出。
总结
存在即合理,java线程池有它的存在的意义.
1 方便了线程的创建,一个线程可以被重复的利用,降低了创建和销毁时候的消耗
2 任务可以迅速的得到执行,不用等待创建线程时间,即使只有几毫秒。
3 线程的管理更加的便捷,可以很明显的控制线程存在的数量,核心数量,执行时间等。
4 希望有不正确的地方大家可以直接指出,一起学习进步