为什么用线程池,在Java中创建线程无非。就是new Thread和实现Runable接口来创建线程,但是这种方法如果不去控制数量,一味的去new 的话是很损耗系统资源的。所以对于线程的优化就有了线程池的出现,它是可以把线程归纳到一个线程池统一管理和使用。
Java线程池的核心就是使用ThreadPoolExecutor类看看它的构造方法有什么参数
public ThreadPoolExecutor(int corePoolSize, 线程池中核心线程数量
int maximumPoolSize, 最大线程数量
long keepAliveTime, 保存活动的时间,不过它起作用必须在一个前提下,就是当线程池中的线程数量超过了corePoolSize时,它表示多余的空闲线程的存活时间,
即:多余的空闲线程在超过keepAliveTime时间内没有任务的话则被销毁。而这个主要应用在缓存线程池中
TimeUnit unit, 表示keepAliveTime的单位,常用的如:TimeUnit.SECONDS(秒)、TimeUnit.MILLISECONDS(毫秒)
BlockingQueue<Runnable> workQueue, 任务队列,主要用来存储已经提交但未被执行的任务,不同的线程池采用的排队策略不一样
ThreadFactory threadFactory, 线程工厂,用来创建线程池中的线程,通常用默认的即可
RejectedExecutionHandler handler 通常叫做拒绝策略,1、在线程池已经关闭的情况下 2、任务太多导致最大线程数和任务队列已经饱和,无法再接收新的任务 。在上面两种情况下,只要满足其中一种时,
在使用execute()来提交新的任务时将会拒绝,而默认的拒绝策略是抛一个RejectedExecutionException异常
) {
Executors提供五种队列对应的Queue类型
newFixedThreadPool()--LinkedBlockingQueue
newSingleThreadExecutor()--LinkedBlockingQueue
newCachedThreadPool()--SynchronousQueue
newScheduledThreadPool()--DelayedWorkQueue
newSingleThreadScheduledExecutor()--DelayedWorkQueue
LinkedBlockingQueue:无界的队列
SynchronousQueue:直接提交的队列
DelayedWorkQueue:等待队列
五种队列的作用
-
newFixedThreadPool() :
作用:该方法返回一个固定线程数量的线程池,该线程池中的线程数量始终不变,即不会再创建新的线程,也不会销毁已经创建好的线程,自始自终都是那几个固定的线程在工作,所以该线程池可以控制线程的最大并发数。
栗子:假如有一个新任务提交时,线程池中如果有空闲的线程则立即使用空闲线程来处理任务,如果没有,则会把这个新任务存在一个任务队列中,一旦有线程空闲了,则按FIFO方式处理任务队列中的任务。 -
newCachedThreadPool() :
作用:该方法返回一个可以根据实际情况调整线程池中线程的数量的线程池。即该线程池中的线程数量不确定,是根据实际情况动态调整的。
栗子:假如该线程池中的所有线程都正在工作,而此时有新任务提交,那么将会创建新的线程去处理该任务,而此时假如之前有一些线程完成了任务,现在又有新任务提交,那么将不会创建新线程去处理,而是复用空闲的线程去处理新任务。那么此时有人有疑问了,那这样来说该线程池的线程岂不是会越集越多?其实并不会,因为线程池中的线程都有一个“保持活动时间”的参数,通过配置它,如果线程池中的空闲线程的空闲时间超过该“保存活动时间”则立刻停止该线程,而该线程池默认的“保持活动时间”为60s。 -
newSingleThreadExecutor() :
作用:该方法返回一个只有一个线程的线程池,即每次只能执行一个线程任务,多余的任务会保存到一个任务队列中,等待这一个线程空闲,当这个线程空闲了再按FIFO方式顺序执行任务队列中的任务。 -
newScheduledThreadPool() :
作用:该方法返回一个可以控制线程池内线程定时或周期性执行某任务的线程池。 -
newSingleThreadScheduledExecutor() :
作用:该方法返回一个可以控制线程池内线程定时或周期性执行某任务的线程池。只不过和上面的区别是该线程池大小为1,而上面的可以指定线程池的大小。
- 固定线程数,如果超过了设置的数量,后续将不会继续创建线程而是FIFO方式去处理队列。
ExecutorService executorService = Executors.newFixedThreadPool(3);
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService.execute(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + " 线程正在执行第" + finalI + "个任务");
}
});
}
- 这个线程池就存在一个线程实例,不管有多少个都是走队列处理
ExecutorService executorService1 = Executors.newSingleThreadExecutor();
for (int i = 0; i < 10; i++) {
final int finalI = i;
executorService1.execute(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + " 线程正在执行第" + finalI + "个任务");
}
});
}
- 这个会根据线池实际使用情况来创建线程,数量不能确定。
ExecutorService executorService2 = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int finalI = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
executorService2.execute(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() + " 线程正在执行第" + finalI + "个任务");
try {
Thread.sleep(finalI *500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
});
}
- 可以定时的队列
ScheduledExecutorService scheduledExecutorService = Executors
.newScheduledThreadPool(3);
scheduledExecutorService.schedule(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName());
}
}, 1, TimeUnit.MILLISECONDS);
ScheduledExecutorService scheduledExecutorService4 = Executors
.newScheduledThreadPool(3);
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() );
}
},1,2, TimeUnit.MILLISECONDS);
ScheduledExecutorService scheduledExecutorService3 = Executors
.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
Log.i(TAG, Thread.currentThread().getName() );
}
},1,2,TimeUnit.SECONDS);
优化线程池ThreadPoolExecutor
虽说线程池极大改善了系统的性能,不过创建线程池也是需要资源的,所以线程池内线程数量的大小也会影响系统的性能,大了反而浪费资源,小了反而影响系统的吞吐量,所以我们创建线程池需要把握一个度才能合理的发挥它的优点,通常来说我们要考虑的因素有CPU的数量、内存的大小、并发请求的数量等因素,按需调整。 通常核心线程数可以设为CPU数量+1,而最大线程数可以设为CPU的数量*2+1。获取CPU数量的方法为:
Runtime.getRuntime().availableProcessors();
shutdown()和shutdownNow()的区别
关于线程池的停止,ExecutorService为我们提供了两个方法:shutdown和shutdownNow,这两个方法各有不同,可以根据实际需求方便的运用,如下:
1.shutdown()方法在终止前允许执行以前提交的任务。
2.shutdownNow()方法则是阻止正在任务队列中等待任务的启动并试图停止当前正在执行的任务。
网友评论