Java 线程池学习
线程创建的3种方式
继承 Thread 类
package ThreadPool;
public class ThreadExample extends Thread{
// 重写 run 方法
@Override
public void run() {
for (int i = 0; i < 100; i++) {
System.out.println("Thread:: " + i);
}
}
public static void main(String[] args) {
ThreadExample thread = new ThreadExample();
thread.start();
}
}
- run() 方法是线程的具体逻辑操作
- start() 方法调用一个 native start0() 方法,通过操作系统启动一个线程
实现 Runnable 接口
package ThreadPool;
import java.util.Date;
public class RunnableExample implements Runnable{
private String command;
public RunnableExample(String command) {
this.command = command;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + " Start Time: " + new Date());
this.process();
System.out.println(Thread.currentThread().getName() + " End Time: " + new Date());
}
public void process() {
try {
Thread.sleep(5000);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Runnable runnable = new RunnableExample("Runnable Thread");
Thread thread = new Thread(runnable);
thread.start();
}
}
- 重写 run() 方法
- 创建 Thread 类实例,传入 Runnable 参数
- 通过 start() 方法启动线程
实现 Callable 接口
public class CallableExample implements Callable<String>{
private String name;
public CallableExample(String name) {
this.name = name;
}
@Override
public String call() throws Exception {
// TODO Auto-generated method stub
return this.name;
}
public static void main(String[] args) {
Callable callable = new CallableExample("Callable Thread");
FutureTask<String> futureTask = new FutureTask<>(callable);
for (int i = 0; i < 100; i++) {
System.out.println(Thread.currentThread().getName() + ": " + i);
if (i == 20) {
new Thread(futureTask, "ThreadName").start();
}
}
try {
System.out.println("Return: " + futureTask.get());
}
catch(InterruptedException e) {
e.printStackTrace();
}
catch (ExecutionException e) {
e.printStackTrace();
}
}
}
- 重写 call() 方法
- 创建 FutureTask 实例异步接收线程返回结果
- 创建 Thread 实例,传入 FutureTask 参数
- 打印返回值
Runnable 与 Callable 接口
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@FunctionalInterface
public interface Callable<V> {
V call() throws Exception;
}
- Runnable自JDK1.0开始就一直存在,Callable是JDK1.5引入的
- Runnable 不会返回结果和抛出异常,Callable可以返回结果和抛出异常
- Executors 可以将 Runnable 对象转换为 Callable 对象
public class Executors {
public static <T> Callable<T> callable(Runnable task, T result) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<T>(task, result);
}
public static Callable<Object> callable(Runnable task) {
if (task == null)
throw new NullPointerException();
return new RunnableAdapter<Object>(task, null);
}
static final class RunnableAdapter<T> implements Callable<T> {
final Runnable task;
final T result;
RunnableAdapter(Runnable task, T result) {
this.task = task;
this.result = result;
}
public T call() {
task.run();
return result;
}
}
}
线程池技术
线程池提供了一种限制和管理资源的方式,每个线程池也维护一些基本信息。
使用线程池的好处:
- 降低资源消耗: 重复利用已创建的线程降低线程创建和销毁造成的消耗
- 提高响应速度: 当任务到达时,不需要等待线程的创建就能立即执行
- 提高线程的可管理性: 线程是稀缺资源,使用线程池可以进行统一的分配、调优和监控
Executor 框架
使用 Executor
来启动线程比 Thread.start()
更好,除了更易管理,效率更高外,还能避免 this 逃逸问题。
/**
* this 逃逸问题
* 在构造函数返回之前,其它线程就持有该对象的引用,调用尚未构造完全的对象的方法
*/
public class ThisEscape {
final String a;
static ThisEscape t;
public ThisEscape() {
t = this;
try {
Thread.sleep(200);
}
catch(InterruptedException e) {
e.printStackTrace();
}
a = "Lzy";
}
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
new ThisEscape();
});
Thread t2 = new Thread(() -> {
System.out.printLn(ThisEscape.t.a); // 输出null, ThisEscape还未构造完成
});
t1.start();
t2.start();
}
}
Executor 框架构成
- 任务(Runnable/Callable)
- 任务的执行(Executor)
- 异步计算的结果(Future): 调用 submit() 方法执行返回 FutureTask 对象
ThreadPoolExecutor 类
// ctl: 保存线程池的状态,原子整数
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
private static final int COUNT_BITS = Integer.SIZE - 3; //29
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;
private static final int TIDYING = 2 << COUNT_BITS;
private static final int TERMINATED = 3 << COUNT_BITS;
/**
* runStateOf: 返回线程池状态
* workerCountOf: 返回线程池内有效线程数量
*/
private static int runStateOf(int c) { return c & ~CAPACITY; }
private static int workerCountOf(int c) { return c & CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
// 任务队列
private final BlockingQueue<Runnable> workQueue;
/**
* ThreadPoolExecutor 构造方法
*
* @param corePoolSize: 线程池的核心线程数(即池中保留的线程数,即使线程处于空闲状态)
* @param maximumPoolSize: 线程池的最大线程数
* @param keepAliveTime: 当线程数大于 corePoolSize 时,多余的空闲线程最大存活时间
* @param unit: keepAliveTime 的时间单位
* @param workQueue: 任务队列,存储待执行的任务(只保存 Runnable 任务)
* @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. AbortPolicy: 抛出 RejectedExecutionException 异常
* 2. CallerRunsPolicy: 在调用者的线程执行任务r, 如果执行程序被关闭, 则丢弃该任务
* 3. DiscardPolicy: 直接丢弃任务, 不做任何处理
* 4. DiscardOldestPolicy: 丢弃最早未被处理的任务
*/
public static class AbortPolicy implements RejectedExecutionHandler {
public AbortPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() + " rejected from " + e.toString());
}
}
public static class CallerRunsPolicy implements RejectedExecutionHandler {
public CallerRunsPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
public static class DiscardPolicy implements RejectedExecutionHandler {
public DiscardPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
public DiscardOldestPolicy() { }
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
// workQueue.poll();
e.getQueue().poll();
e.execute(r);
}
}
}
线程池使用案例
Runnable 接口实现类
package thread_pool;
import java.util.Date;
public class RunnableExample implements Runnable {
private String command;
public RunnableExample(String command) {
this.command = command;
}
public String getCommand() {
return this.command;
}
public void setCommand(String command) {
this.command = command;
}
@Override
public void run() {
// TODO Auto-generated method stub
System.out.println(Thread.currentThread().getName() + " Start Time: " + new Date());
process();
System.out.println(Thread.currentThread().getName() + " End Time: " + new Date());
}
public void process() {
try {
Thread.sleep(5000);
}
catch(InterruptedException e) {
e.printStackTrace();
}
}
}
ThreadPoolExecutorExample类
package thread_pool;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
public class ThreadPoolExecutorExample {
private static final int CORE_POOL_SIZE = 5;
private static final int MAX_POOL_SIZE = 10;
private static final int QUEUE_CAPACITY = 100; // 任务队列容量
private static final long KEEP_ALIVE_TIME = 1L;
public static void main(String[] args) {
ThreadPoolExecutor executor = new ThreadPoolExecutor
(CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new ThreadPoolExecutor.CallerRunsPolicy());
// 创建 RunnableExample 实例
for (int i = 0; i < 10; i++) {
Runnable worker = new RunnableExample(i + "");
executor.execute(worker);
}
executor.shutdown();
// 如果 excutor 未结束,将进入死循环(验证线程池已关闭)
while (!executor.isTerminated()) {}
System.out.println("Finshed all threads");
}
}
线程池原理
excutor.execute(worker)
public void execute(Runnable command) {
if (command == null) {
throw new NullPointerException();
}
int c = ctl.get();
/**
* 判断当前线程池中线程数量是否小于 corePoolSize
* 如果小于,则通过 addWorker 创建一个新的线程,并将任务 command 添加到该线程
*/
if (workerCountOf(c) < corePoolSize) {
if (addWorker(command, true)) // true 表示线程上限为 corePoolSize
return;
c = ctl.get();
}
/**
* 如果线程数量 >= corePoolSize, 则判断线程池的状态
* 如果线程池处于 RUNNING 状态,且队列可以加入任务
*/
if (isRunning(c) && workQueue.offer(command)) {
// 再次获取线程池状态
int recheck = ctl.get();
// 如果线程池不是 RUNNING 状态,需要从任务队列中移除任务,并尝试判断线程是否全部执行完毕
if (! isRunning(recheck) && remove(command))
reject(command); // 对任务执行拒绝策略
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
/**
* 如果任务不能加入队列,则通过 addWorker 创建新线程
* 如果线程创建失败,则对任务执行拒绝策略
*/
else if (!addWorker(command, false)) // false表示线程上限为 maxPoolSize
reject(command);
}
execute() 与 submit() 方法
// submit 方法, execute 方法如上所示
public abstract class AbstractExecutorService implements ExecutorService {
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
protected <T> RunnableFuture<T> newTaskFor(Callable<T> callable) {
return new FutureTask<T>(callable);
}
}
- execute() 用于提交不需要返回值的任务,无法判断任务是否被线程执行成功
- submit() 用于提交需要返回值的任务,线程池会返回一个
Future
类型的对象 Future
对象的get()
方法可以获取返回值
public interface Future<V> {
/**
* 尝试取消任务执行
* @return false: 由于任务已经正常执行完成, 任务没有被中断
* @return true: 其它情况
*/
boolean cancel(boolean mayInterruptIfRunning);
/**
* @return true: 任务在正常执行完成之前被取消
* @return false: 任务正常执行完成
*/
boolean isCancelled();
/**
* 任务是否完成
* @return true: 正常结束、异常、任务取消都返回 true
* @return false:
*/
boolean isDone();
/**
* 等待获取执行结果
*/
V get() throws InterruptedException, ExecutionException;
/**
* 等待获取执行结果
* @param timeout: 最大等待时长
* @param unit: timeout单位
*/
V get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, TimeoutException;
}
shutdown() 与 shutdownNow()
shutdown(): 关闭线程池,线程池不再接受新的任务,但是等待队列的任务会执行完毕。
shutdownNow(): 关闭线程池,线程池会终止当前正在运行的任务,停止处理等待队列的任务并返回List。
public class ThreadPoolExecutor extends AbstractExecutorService {
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(SHUTDOWN); // 线程池状态变为 SHUTDOWN
interruptIdleWorkers(); // 中断空闲线程
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP); // 线程池状态变为 STOP
interruptWorkers(); // 中断所有线程
tasks = drainQueue(); // 返回待执行任务列表
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
}
Executors 工具类创建线程池
FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newFixedThreadPool(int nThreads, ThreadFactory threadFactory) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory);
}
可重用固定线程数的线程池,核心线程数 = 最大线程数
FixedThreadPool
使用无界队列作为线程池的工作队列
-
public LinkedBlockingQueue() { this(Integer.MAX_VALUE); }
-
运行中的
FixedThreadPool
线程池不会拒绝任务,在任务比较多时会导致OOM溢出
SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newSingleThreadExecutor(ThreadFactory threadFactory) {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>(),
threadFactory));
}
SingleThreadExecutor
只有一个线程的线程池,核心线程数 = 最大线程数 = 1。
SingleThreadExecutor
也使用无界队列作为线程池的工作队列,与 FixedThreadPool
相同也会导致内存溢出问题。
CachedThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool(ThreadFactory threadFactory) {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
threadFactory);
}
CachedThreadPool
根据需要创建新的线程,核心线程数 = 0,最大线程数 = Integer.MAX_VALUE
CachedThreadPool
的线程数是无界的,即主线程提交任务的速度大于线程池中线程处理任务的速度,则会不断有新的线程创建,可能导致 cpu资源和内存资源耗尽。
ScheduledThreadPoolExecutor
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
// 固定周期执行
public ScheduledFuture<?> scheduleAtFixedRate(Runnable command,
long initialDelay,
long period,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (period <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(period));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t); //super.getQueue().add(t);
return t;
}
// 固定延时执行
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
delayedExecute(t);
return t;
}
}
- 当调用
scheduleAtFixedRate()
或scheduleWithFixedDelay()
方法时,向线程池的DelayQueue
添加一个RunnableScheduledFuture
实例。 - 线程池从线程从
DelayQueue
中获取RunnableScheduledFuture
任务执行。
DelayQueue
public class ScheduledThreadPoolExecutor extends ThreadPoolExecutor implements ScheduledExecutorService {
private static final int INITIAL_CAPACITY = 16;
private RunnableScheduledFuture<?>[] queue =
new RunnableScheduledFuture<?>[INITIAL_CAPACITY];
private final ReentrantLock lock = new ReentrantLock();
private int size = 0;
// 指定在队列头部等待任务的线程
private Thread leader = null;
// 表示一个新的任务在队列头部变得可用 或 一个新的线程需要成为 leader
private final Condition available = lock.newCondition();
}
DelayQueue 本质是一个优先级队列,任务延时时间短的放在前面先被执行,如果延时时间相同则先提交的任务先被执行。
线程池大小设置
- CPU密集型任务(N+1):N为CPU核心数
- I/O密集型任务(2N)
参考文章
[1] Java线程池详解
[2] this引用逃逸问题