文章目录
- 一、线程池
- 二、线程池的8个参数
- 三、JDK中4种内置的拒绝策略
- 四、自定义线程工厂
- 五、自定义拒绝策略
- 六、任务实现的接口
- 6.1、Runnable
- 6.2、Callable
- 七、线程池新任务处理流程
- 八、线程池实例
- 九、线程数的设定
- 十、JDK默认线程池
- 10.1、CacheThreadPool
- 10.2、FixedThreadPool
- 10.3、SingleThreadExecutor
- 10.4、ScheduledThreadPool
一、线程池
二、线程池的8个参数
- corePoolSize: 核心线程数,默认不会被回收
- maximumPoolSize: 最大线程数
- keepAliveTime: 非核心线程存活时间
- TimeUnit: 非核心线程存活时间的时间单位
- BlockingQueue: 存储任务的阻塞队列
- ThreadFactory: 生产线程的工厂
- RejectedExecutionHandler: 拒绝策略
三、JDK中4种内置的拒绝策略
- CallerRunsPolicy: submit任务的线程处理,如果是main线程调用了submit,则有main线程处理任务
- AbortPolicy 丢弃任务并抛出RejectedExecutionException异常
- DiscardPolicy 直接丢弃任务,不抛出异常
- DiscardOldestPolicy 丢弃存放在任务队列中最久的任务
四、自定义线程工厂
自定义线程工厂需要实现接口ThreadFactory
重写Thread newThread(Runnable r);
方法。
class MyThreadFactory implements ThreadFactory{
int count = 0;
@Override
public Thread newThread(Runnable r) {
System.out.println("创建线程");
return new Thread(r, "我的自定义线程池创建的线程" + (++count));
}
}
五、自定义拒绝策略
自定义拒绝策略需要实现接口RejectedExecutionHandler
重写void rejectedExecution(Runnable r, ThreadPoolExecutor executor)
方法。
class MyRejectStrategy implements RejectedExecutionHandler{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 一般会将线程池无法处理的任务先放进Kafka
}
}
六、任务实现的接口
6.1、Runnable
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
Runnable runnable = () -> {
try {
System.out.println("运行线程: " + Thread.currentThread().getName());
// 阻塞
int read = System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
};
6.2、Callable
@FunctionalInterface
public interface Callable<V> {
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
V call() throws Exception;
}
Callable<String> callable = () -> {
System.out.println("运行线程: " + Thread.currentThread().getName());
// 阻塞
Thread.sleep(5000);
return "Hello, Callable!";
};
七、线程池新任务处理流程
八、线程池实例
import java.io.IOException;
import java.util.concurrent.*;
/**
* @author IT00ZYQ
* @date 2021/5/31 23:00
**/
public class T02_ExecutorService {
/**
* 核心线程数
*/
public static final int CORE_POOL_SIZE = 2;
/**
* 最大线程数
*/
public static final int MAX_POOL_SIZE = 4;
/**
* 非核心线程空闲存活时间
*/
public static final long KEEP_ALIVE_TIME = 60;
public static void main(String[] args) {
Runnable runnable = () -> {
try {
System.out.println("运行线程: " + Thread.currentThread().getName());
// 阻塞
int read = System.in.read();
} catch (IOException e) {
e.printStackTrace();
}
};
Callable<String> callable = () -> {
System.out.println("运行线程: " + Thread.currentThread().getName());
Thread.sleep(5000);
return "Hello, Callable!";
};
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<Runnable>(5),
new MyThreadFactory(),
new MyRejectStrategy());
// 拒绝策略
// CallerRunsPolicy submit任务的线程处理,这里由main线程处理
// AbortPolicy 丢弃任务并抛出RejectedExecutionException异常
// DiscardPolicy 直接丢弃任务,不抛出异常
// DiscardOldestPolicy 丢弃存放在任务队列中最久的任务
int count = 0;
for (int i = 0; i < 4; i++) {
System.out.println("提交第" + (++count) + "个任务");
threadPool.execute(runnable);
}
for (int i = 0; i < 5; i++) {
System.out.println("提交第" + (++count) + "个任务");
threadPool.submit(callable);
}
System.out.println("任务队列任务数: " + threadPool.getQueue().size());
System.out.println("线程池线程数: " + threadPool.getPoolSize());
System.out.println("提交第" + (++count) + "个任务");
threadPool.submit(runnable);
}
}
class MyThreadFactory implements ThreadFactory{
int count = 0;
@Override
public Thread newThread(Runnable r) {
System.out.println("创建线程");
return new Thread(r, "我的自定义线程池创建的线程" + (++count));
}
}
class MyRejectStrategy implements RejectedExecutionHandler{
@Override
public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
// 一般会将线程池无法处理的任务先放进Kafka
// ...
System.out.println("执行了拒绝策略");
}
}
九、线程数的设定
- CPU 密集型任务:有大量时间用于计算,CPU密集型任务消耗的主要是 CPU 资源,将线程数设置为
CPU 核数 + 1
,线程数设定为比CPU核数多一个的原因是当线程发生中断等不需要CPU资源的情况时,CPU资源能得到充分利用。 - I/O 密集型任务:有大量时间用于I/O,I/O密集型任务大量的时间用于 I/O 交互,而线程进行 I/O 的时间段内不会占用 CPU 资源,为了充分利用CPU资源,将线程数设定为
2 * CPU核数
。
十、JDK默认线程池
10.1、CacheThreadPool
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
核心线程数为0,最大线程数为Integer.MAX_VALUE,非核心线程空闲存活时间60s,也就是说CachedThreadPool
中只要有线程空闲达到60s就会被回收,每次有新的任务加入时,没有空闲线程则创建新的线程去执行任务。
10.2、FixedThreadPool
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
核心线程数为nThreads,最大线程数也为nThreads,核心线程数与最大线程数相等,也就是说,一旦线程被创建出来,它就是核心线程,所有线程都不会被回收。
10.3、SingleThreadExecutor
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
核心线程数与最大线程数都为1,线程池只有一个线程,且这个线程不会被回收。
为什么需要单线程的线程池呢?
线程池维护了一个任务队列,使用单线程的线程池时不需要自己维护任务队列。
10.4、ScheduledThreadPool
public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
return new ScheduledThreadPoolExecutor(corePoolSize);
}
public ScheduledThreadPoolExecutor(int corePoolSize) {
return new ThreadPoolExecutor(corePoolSize, Integer.MAX_VALUE,
0, NANOSECONDS,
new DelayedWorkQueue());
}
定时任务或延迟任务线程池,用于处理定时/延迟任务。核心线程数为corePoolSize,最大线程数为MAX_VALUE,线程一空闲就会被回收。