多线程的常用实现/常用的线程池
根据阿里对使用线程池的规范:可以参考Executors的实现,按照业务实现自己的线程池。
注意线程池等资源还是要想着关闭。
初始化时,来一个任务新建一个一个线程;直到核心线程数满,再往队列里面放任务;如果队列也满了就继续新建线程到最大线程数量;如果最大线程数满就使用拒绝策略;Executor的默认拒绝策略是AbortPolicy;Spring线程池默认是同步执行。
public static void main(String[] args) {
int threadCount = 10;
// 依次设置核心线程数、最大线程数、(大于核心数部分)空闲存活时间、存活时间单位、队列
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(threadCount, threadCount, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());
//固定大小线程池、
ExecutorService executorService = Executors.newFixedThreadPool(10);
//无界线程池
Executors.newCachedThreadPool();
//单个线程池 只有一个线程池的固定大小线程池FIFO
Executors.newSingleThreadExecutor();
/** ---------------- 其他线程池--------------**/
//用于任务调度的线程池
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//工作窃取线程池
ExecutorService executorServiceFk = Executors.newWorkStealingPool();
}
拒绝策略
- CallerRunsPolicy 同步执行(调用当前线程池的所在的线程去执行被拒绝的任务)
- AbortPolicy 抛出个RejectedExecutionException异常,不执行此任务
- DiscardPolicy 直接抛弃任务
- DiscardOldestPolicy 会抛弃最旧的任务
- 自定义拒绝策略
线程池常用方法
execute(Runnable) //无返回值
submit(Runnable/Callable) // 有返回值
shutdown:平滑的关闭ExecutorService,不可以提交新的task,已经提交的将继续执行(包含提交正在执行和提交未执行)
当所有已提交任务执行完毕,线程池即被关闭。
提交使用拒绝策略处理。
awaitTermination:当等待超过设定时间时,会监测ExecutorService是否已经关闭,
若关闭则返回true,否则返回false。一般情况下会和shutdown方法组合使用
接收timeout和unit两个参数,用于设定超时时间及单位。
shutdownNow :
(1)线程池的状态立刻变成STOP状态,此时不能再往线程池中添加新的任务。
(2)终止等待执行的线程,并返回它们的列表;
(3)试图停止所有正在执行的线程,试图终止的方法是调用Thread.interrupt(),但是大家知道,如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以,ShutdownNow()并不代表线程池就一定立即就能退出,它可能必须要等待所有正在执行的任务都执行完成了才能退出。
线程池数量设置
cpu密集型:核心线程数 = cpu(处理器)数量
Io密集型:核心线程数 = cpu(处理器)数量 * 2
其他线程池
线程池主要分两类,ThreadPoolExecutor和ForkJoinPool;使用较多的是ThreadPoolExecutor,也可以通过Executors来创建需要的线程池;
线程池解决了两个问题: 一是在执行大量的异步任务时,因为线程池减少了任务开始前的准备工作,如频繁创建线程,启动线程等工作,提升了性能表现;二是提供了一种绑定资源和管理资源的途径,可以进行一些基础的统计分析,比如已经完成的任务数量等。
任务调度线程池
ScheduledExecutorService使用示例
@Slf4j
public class ScheduledThreadPoolDemo implements Runnable{
public static void main(String[] args) throws ExecutionException, InterruptedException {
//两种创建方式,都是一个代码
//ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(10);
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);
//使用ScheduledThreadPool来延迟执行Callable或者Runnable,返回一个对象可用来取消或者查询任务执行情况
//ScheduledFuture future = scheduledExecutorService.schedule(Callable<V> callable, long delay, TimeUnit unit);
// Callable c1 = new CallableDemo("A"); RunnableDemo r1 = new RunnableDemo();
// ScheduledFuture future = scheduledExecutorService.schedule(c1, 3, TimeUnit.SECONDS);
// future.isDone();
//循环执行任务,在等待initialDelay.unit后执行,然后每隔period.unit后实行;同样返回ScheduledFuture对象;
//如果此任务的任何一个执行要花费比其周期更长的时间,则将推迟(一定时间)后续执行,但不会同时执行。周期为4秒,执行耗时3秒,每次开始打印间隔一秒;
//如果任务的任何一个执行遇到异常,则后续执行都会被取消。否则,只能通过执行程序的取消或终止方法来终止该任务。
//scheduledExecutorService.scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit);
ScheduledThreadPoolDemo scheduledThreadPoolDemo = new ScheduledThreadPoolDemo();
//scheduledExecutorService.scheduleAtFixedRate(scheduledThreadPoolDemo, 5, 4, TimeUnit.SECONDS);
//和上一个方法差不多,但是会等上一个任务结束,然后再等待delay时间去执行
//scheduledExecutorService.scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
scheduledExecutorService.scheduleWithFixedDelay(scheduledThreadPoolDemo, 5, 4, TimeUnit.SECONDS);
}
@Override
public void run() {
log.info("----执行方法开始---");
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
log.info("----执行方法结束---");
}
}
工作窃取线程池 ForkJoinPool
ForkJoin并发框架:Fork=分解 + Join=合并
ForkJoinPool:ForkJoin线程池,实现了ExecutorService接口和工作窃取算法,用于线程调度与管理。
ForkJoinTask:ForkJoin任务,提供了fork()方法和join()方法。通常不直接使用,而是使用以下子类: RecursiveAction:无返回值的任务,通常用于只fork不join的情形。RecursiveTask:有返回值的任务,通常用于fork+join的情形。
根据ForkJoinTask的两种类型,可以将ForkJoin并发框架划分为两种用法:
only fork:递归划分子任务,分别执行,但是并不需要合并各自的执行结果。
fork+join:递归划分子任务,分别执行,然后递归合并计算结果。
参考转载文章:https://blog.csdn.net/hanchao5272/article/details/79982095
public class TestForkJoinPool {
public static void main(String[] args) {
MyTask mt = new MyTask(1);
// Executors和new ForkJoinPool实现是一样的,默认情况下Runtime.getRuntime().availableProcessors()得到线程数
//ExecutorService executorServiceFk = Executors.newWorkStealingPool();
ForkJoinPool forkJoinPool = new ForkJoinPool(10);
Future<Integer> result = forkJoinPool.submit(mt);
try {
System.out.println(result.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
forkJoinPool.shutdown();
}
public static class MyTask extends RecursiveTask<Integer> {
int i;
public MyTask(int i) {
this.i = i;
}
@Override
protected Integer compute() {
if (i >= 100) {
return i * i;
}
MyTask newTask2 = new MyTask(i + 1);
newTask2.fork();
return i*i + newTask2.join();
}
}
}