线程的创建方式
- 继承 Thread
- 实现runnable
- 实现Callable ( 前三种实现 )
- 线程池(java提供了Executors可以去创建,推荐手动创建线程池)
Executors创建线程池
//创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。
Executors.newCachedThreadPool();
//创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待
Executors.newFixedThreadPool(5);
//创建一个定长线程池,支持定时及周期性任务执行
Executors.newScheduledThreadPool(10);
//创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,
//保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行
Executors.newSingleThreadExecutor();
ThreadPoolExecutor线程池
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
/**
* @param corePoolSize 核心线程数
* @param maximumPoolSize 最大线程数
* @param keepAliveTime keepAliveTime当线程数大于核心时,这是多余空闲线程在终止前等待新任务的最长时间
* @param unit keepAliveTime参数的时间单位
* @param workQueue 在执行任务之前用于容纳任务的队列。此队列将只包含由{@code execute}方法提交的{@codeRunnable}任务
* @param threadFactory 执行器创建新线程时要使用的工厂
* @param handler 当由于达到线程边界和队列容量而阻止执行时要使用的处理程序
*/
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.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
可以看到,其需要如下几个参数:
1.corePoolSize(必需):核心线程数。默认情况下,核心线程会一直存活,但是当将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
2.maximumPoolSize(必需):线程池所能容纳的最大线程数。当活跃线程数达到该数值后,后续的新任务将会阻塞。
3.keepAliveTime(必需):线程闲置超时时长。如果超过该时长,非核心线程就会被回收。如果将 allowCoreThreadTimeout 设置为 true 时,核心线程也会超时回收。
4.unit(必需):指定 keepAliveTime 参数的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。
5.workQueue(必需):任务队列。通过线程池的 execute() 方法提交的 Runnable 对象将存储在该参数中。其采用阻塞队列实现。
6.threadFactory(可选):线程工厂。用于指定为线程池创建新线程的方式。
7.handler(可选):拒绝策略。当达到最大线程数时需要执行的饱和策略。
线程池的使用流程如下
// 创建线程池
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAXIMUM_POOL_SIZE,
KEEP_ALIVE,
TimeUnit.SECONDS,
sPoolWorkQueue,
sThreadFactory);
// 向线程池提交任务
threadPool.execute(new Runnable() {
@Override
public void run() {
... // 线程执行的任务
}
});
// 关闭线程池
threadPool.shutdown(); // 设置线程池的状态为SHUTDOWN,然后中断所有没有正在执行任务的线程
threadPool.shutdownNow(); // 设置线程池的状态为 STOP,然后尝试停止所有的正在执行或暂停任务的线程,并返回等待执行任务的列表
线程池五种状态
线程池ThreadPoolExecutor分别有五种状态分别为:RUNNING、SHUTDOWN、STOP、TIDYING、TERMINATED使用一个AtomicInteger类型的ctl字段来描述线程池地运行状态和线程数量,通过ctl的高三位来表示线程池的5种状态,低29位表示线程池中现有的线程数量。
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
/**
* 线程池线程数地bit数
*/
private static final int COUNT_BITS = Integer.SIZE - 3;
/**
* 线程池中最大线程容量
*/
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
/**
* 表示可接受新任务,且可执行队列中的任务;
*/
private static final int RUNNING = -1 << COUNT_BITS;
/**
* 表示不接受新任务,但可执行队列中的任务;
*/
private static final int SHUTDOWN = 0 << COUNT_BITS;
/**
* 表示不接受新任务,且不再执行队列中的任务,且中断正在执行的任务;
*/
private static final int STOP = 1 << COUNT_BITS;
/**
* 所有任务已经中止,且工作线程数量为0,最后变迁到这个状态的线程将
* 要执行terminated()钩子方法,只会有一个线程执行这个方法;
*/
private static final int TIDYING = 2 << COUNT_BITS;
/**
* TERMINATED,中止状态,已经执行完terminated()钩子方法;
*/
private static final int TERMINATED = 3 << COUNT_BITS;
线程池处理流程
/**
* 在将来某个时候执行给定的任务。该任务可以在新线程中执行,也可以在现有的池线程中执行。如果由于此执行
* 器已关闭或已达到其容量,任务无法提交执行,则由当前RejectedExecutionHandler处理该任务。
* Params:
* 命令–要执行的任务
* Throws:
* RejectedExecutionException–如果任务无法接受执行,则由RejectedExecutionHandler自行决定
* NullPointerException–如果命令为null
*/
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
/*
分三步走:
1.如果运行的线程少于corePoolSize,请尝试以给定的命令作为第一个任务来启动一个新线程。
对addWorker的调用原子地检查runState和workerCount,因此,通过返回false,可以防止
在不应该添加线程的情况下添加线程的错误警报。
2.如果一个任务可以成功排队,那么我们仍然需要仔细检查我们是否应该添加一个线程(因为自上
次检查以来已有的线程已经失效),或者池是否在进入该方法后关闭。因此,如果有必要,我们重
新检查状态,如果停止,则回滚排队,如果workerCount为0,则启动一个新线程。
3.如果我们不能对任务进行排队,那么我们尝试添加一个新线程。如果它失败了,我们知道线程池
被关闭或饱和了,所以拒绝了这项任务。
*/
int c = ctl.get();
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true))
return;
c = ctl.get();
}
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
if (!isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
} else if (!addWorker(command, false))
reject(command);
}
处理流程如下如图所示
实现示例
public static void main(String[] args) {
ArrayBlockingQueue abq = new ArrayBlockingQueue<>(8);
ThreadPoolExecutor executor= new ThreadPoolExecutor(5,10,20,TimeUnit.SECONDS,abq);
//创建线程任务
/* i<15 5个核心线程,8个队列,2个非核心线程 */
/* i<20 5个核心线程,8个队列,5个非核心线程 */
for (int i = 0; i < 15; i++) {
MyRunnable myRunnable = new MyRunnable();
executor.execute(myRunnable);
}
executor.shutdown();
}
public class MyRunnable implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"******"+new Date());
}
}
}
线程池大小优化
线程池的最佳大小取决于可用的处理器数量和待处理任务的性质。
- 对于CPU密集型任务,假设系统有N个逻辑处理核心
N 或 N+1 的最大线程池数量大小将实现最大效率 - 对于 I/O密集型任务,需要考虑请求的等待时间(W)和服务处理时间(S)的比例,
线程池最大大小为 N*(1+ W/S)会实现最高效率
具体可以根据反复测试调试更优结果