线程池
线程池分类
1. Executors.newSingleThreadExecutor(); // 创建只有一个线程的线程池
2. Executors.newCachedThreadPool(); // 创建可以存放多个线程的线程池
3. Executors.newFixedThreadPool(线程数); // 创建拥有固定线程数的线程池
4. Executors.newSingleThreadScheduledExecutor(); // 一个可以用于“线程调度”的单个线程的线程池(即可以指定时间来执行)
5. Executors.newScheduledThreadPool(); // 一个可以用于“线程调度”的多个线程的线程池(即可以指定时间来执行)
前三种创建ExecutorService类型的线程池,继承于Executor
后两种创建ScheduledExecutorService类型的线程池,继承于ExecutorService
线程池使用步骤
① 创建线程池
② 将任务通过submit()或scheduled()提交到线程池中的线程
③ 线程通过run()方法或call()方法执行任务,并将执行结果返回到Future对象
④ Future对象通过get()方法以闭锁的方式拿到真实处理后的返回值
自定义线程池
基本介绍
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
[ThreadFactory threadFactory],
RejectedExecutionHandler handler)
参数说明:
corePoolSize核心线程数量
- 即使没有任务执行,核心线程也会一直存活
- 线程数小于核心线程时,即使有空闲线程,线程池也会创建新线程执行任务
- 设置
allowCoreThreadTimeout= true时,核心线程会超时关闭
maximumPoolSize最大线程数
- 当所有核心线程都在执行任务,且任务队列已满时,线程池会创建新线程执行任务
- 当线程数=
maxPoolSize,且任务队列已满,此时添加任务时会触发RejectedExecutionHandler进行处理
keepAliveTime和TimeUnit线程空闲时间 及 单位
- 指的是线程池中非核心线程的最大空闲时长
- 如果非核心线程空闲时间达到
keepAliveTime时,线程会销毁- 如果设置
allowCoreThreadTimeout= true时,核心线程执行完任务也会销毁直到数量 = 0
workQueue任务队列
ArrayBlockingQueue有界队列,需要指定队列大小LinkedBlockingQueue若指定大小则和ArrayBlockingQueue类似,若不指定大小则默认能存储Integer.MAX_VALUE个任务,相当于无界队列,此时maximumPoolSize值其实是无意义的SynchronousQueue同步阻塞队列,当有任务添加进来后,必须有线程从队列中取出,当前线程才会被释放,newCachedThreadPool就使用这种队列handler 拒绝策略
饱和定义为:任务数 >
maximumPoolSize+workQueue.size()
AbortPolicy默认的拒绝策略,如果饱和直接抛出异常CallerRunsPolicy直接调用execute去执行DiscardPolicy饱和后直接抛弃任务,不抛出异常DiscardOldestPolicy抛弃队列中最先加入的任务,然后再添加新任务
代码实验
- 自定义线程
public class MyThread implements Runnable{
private String threadName;
public MyThread(String threadName){
this.threadName = threadName;
}
@Override
public void run() {
try {
System.out.println("threadName: " + this.threadName);
System.out.println(threadName + "执行了1s");
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public String getThreadName() {
return threadName;
}
public void setThreadName(String threadName) {
this.threadName = threadName;
}
}
- 测试类
public class TestMyThreadPool {
public static void main(String[] args) {
ThreadPoolExecutor pool = new ThreadPoolExecutor(1, 2, 10,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(3),
new ThreadPoolExecutor.AbortPolicy()
);
MyThread t1 = new MyThread("t1");
MyThread t2 = new MyThread("t2");
MyThread t3 = new MyThread("t3");
MyThread t4 = new MyThread("t4");
MyThread t5 = new MyThread("t5");
MyThread t6 = new MyThread("t6");
pool.execute(t1);
pool.execute(t2);
pool.execute(t3);
pool.execute(t4);
pool.execute(t5);
pool.execute(t6);
}
}
分析
任务到达时,将其先尝试分配给核心线程,如果核心线程满了,则放到工作队列中,工作队列也满了才将任务交给非核心进程执行,此实验核心进程数为1,非核心进程数为1,工作队列进程容量为3,任务为6,刚好可以测出所有的情况。
threadName: t1
t1执行了1s
threadName: t5
t5执行了1s
Exception in thread "main" java.util.concurrent.RejectedExecutionException: Task diyPool.MyThread@677327b6 rejected from java.util.concurrent.ThreadPoolExecutor@14ae5a5[Running, pool size = 2, active threads = 2, queued tasks = 3, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2063)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:830)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1379)
at diyPool.TestMyThreadPool.main(TestMyThreadPool.java:27)
threadName: t2
t2执行了1s
threadName: t3
t3执行了1s
threadName: t4
t4执行了1s
扩展
使用自定义策略
- 定义
public class MyRejectPolicy implements RejectedExecutionHandler {
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
System.out.println("被拒绝的线程名:" + ((MyThread)r).getThreadName());
}
}
- 使用(在对应位置更换拒绝策略)
// new ThreadPoolExecutor.AbortPolicy()
new MyRejectPolicy()