了解线程池之前咱们先了解一个概念并发与并行。
并发:多用户(多线程)执行任务
并行:多线程执行任务
区别:
有无用户参与
了解多线程首先咱们先了解下线程池的分类
线程池分为两类:
ThreadPoolExecutor:管理线程的容器技术
ForkJoinPool:对任务分解与合并
为什么使用多线程池呢?
不了解线程的可以看下这篇文章
咱们都知道,多线程大多应用场景下能够提高程序的运行效率,是调度执行的基本单位,多个线程共享资源。
但是线程的启动和销毁很耗时的,这就造成了咱们执行任务的时候大量的时间在开启与销毁线程。
怎么解决呢?
这么大技术漏洞jdk早就帮咱们想好了,使用线程池管理技术。
线程池:故名思意,管理线程的池子。
ThreadPoolExecutor
线程池简单使用
public static void main(String[] args) throws Exception{
LinkedBlockingQueue<Runnable> blockingQueue = new LinkedBlockingQueue<>(2);
ExecutorService executor =
new ThreadPoolExecutor(2,
4,
10,
TimeUnit.SECONDS,
blockingQueue,
new TestMain.NamedThreadFactory(),
new ThreadPoolExecutor.CallerRunsPolicy()
);
executor.execute(()->System.out.println("无法返回值的线程运行了..."+Thread.currentThread().getName()));
Future<String> submit = executor.submit(() -> {
System.out.println("有法返回值的线程运行了..." + Thread.currentThread().getName());
return "OK";
});
String s = submit.get();
System.out.println("有返回值的线程返回值="+s);
executor.shutdown();
}
static class NamedThreadFactory implements ThreadFactory {
private AtomicInteger atomicInteger = new AtomicInteger();
@Override
public Thread newThread(Runnable r) {
atomicInteger.incrementAndGet();
Thread thread = new MyWorkThread(atomicInteger,r);
return thread;
}
}
static class MyWorkThread extends Thread{
private AtomicInteger atomicInteger;
public MyWorkThread(AtomicInteger atomicInteger,Runnable runnable){
super(runnable);
this.atomicInteger = atomicInteger;
}
@Override
public void run() {
super.run();
}
}
ThreadPoolExecutor 构造
Executors也定义特定参数的线程池,不过它们最终还是调用的这个构造,所以咱们只要了解了这个构造,其他的都自然明白了。
ThreadPoolExecutor(int corePoolSize,//核心线程数
int maximumPoolSize,//最大线程数
long keepAliveTime,//存活时间
TimeUnit unit,//存活时间单位
BlockingQueue<Runnable> workQueue,//任务队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler//拒绝策略
)
Executors工具定义的几种线程池
//缓存线程池,最大能缓存Integer.MAX_VALUE(适合并发波动较大的场景)
ExecutorService cachedService = Executors.newCachedThreadPool();
//固定的线程池 核心数和最大线程数是固定的(适合并发比较稳定的场景)
ExecutorService fixedThreadPool = Executors.newFixedThreadPool(2);
//单线程的线程池
ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();
//周期性操作线程池
ExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(2);
ThreadPoolExecutor重要成员
咱们看图了解下重要的成员
线程池状态与线程池数量
//ctl 有两个作用,1、标识线程池的状态高三位 2、线程池的数量低28位
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
//把线程状态和线程池数量得高位和低位临界值
private static final int COUNT_BITS = Integer.SIZE - 3;
//线程池的容量(正数高位必须是0)
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,将运行状态的线程过渡到终止状态
private static final int TIDYING = 2 << COUNT_BITS;
//终止状态
private static final int TERMINATED = 3 << COUNT_BITS;
// 前面说了,ctl 包括两部分内容,这些方法就是辅助功能
//根据ctl查看线程池的状态
private static int runStateOf(int c) { return c & ~CAPACITY; }
//根据 ctl查看线程池中的任务数量
private static int workerCountOf(int c) { return c & CAPACITY; }
//设置线程池对应的状态
private static int ctlOf(int rs, int wc) { return rs | wc; }
ThreadPoolExecutor.execute()方法
接下来了解下execute方法是如何实现的
//提交任务
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
//获取ctl
int c = ctl.get();
//如果线程池中的数量小于核心线程数,说明需要增加核心线程
if (workerCountOf(c) < corePoolSize) {
//如果增加线程成功直接返回
if (addWorker(command, true))
return;
//如果增加失败了,重新获取ctl
c = ctl.get();
}
//如果线程池是运行中的状态,并且添加工作线程成功
if (isRunning(c) && workQueue.offer(command)) {
//再次获取ctl用于检查
int recheck = ctl.get();
//如果不是运行中的状态 并且 删除成功了 执行拒绝策略,这里主要是解决高并发下带来的问题
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
//如果workerQueue中无task,直接添加worker
addWorker(null, false);
}
//如果不是运行中的状态 或者添加任务队列失败了,尝试增加新任务(最大新任务个数为MaxPoolSize)
//添加非核心线程
else if (!addWorker(command, false))
//如果添加失败(最大线程数-核心线程数的线程),执行拒绝策略
reject(command);
}
ThreadPoolExecutor.addWorker(Runnable firstTask, boolean core)方法
//添加任务,core 是否是核心线程
private boolean addWorker(Runnable firstTask, boolean core) {
//死循环标识
retry:
for (;;) {
//获取ctl
int c = ctl.get();
//获取线程池的状态
int rs = runStateOf(c);
// 验证线程池是否满足添加新任务
//如果线程池处于不接收任务,不处理任务之后的状态并且如果线程池状态不是 SHUTDOWN 或者 任务不为null 或者 workQueue是空的
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
//对工作任务数量进行+1操作
for (;;) {
//获取线程池的工作线程数量
int wc = workerCountOf(c);
//如果超出最大容量 或者 大于等于核心任务数量或者最大任务数量
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
//不需要添加worker
return false;
//对工作任务数量进行+1操作
if (compareAndIncrementWorkerCount(c))
break retry;
//如果对工作任务+1操作失败,则重新获取ctl
c = ctl.get();
//如果线程池的状态改变了,则重新进行添加任务之前的验证操作
if (runStateOf(c) != rs)
continue retry;
}
}
//worker是否运行
boolean workerStarted = false;
//worker是否被添加
boolean workerAdded = false;
//线程池中最大的worker数量为maximumPoolSize
Worker w = null;
try {
//创建任务
w = new Worker(firstTask);
//这个线程是执行worker的线程,而firstTask是执行咱们代码线程
final Thread t = w.thread;
if (t != null) {
//获取到同步对象
final ReentrantLock mainLock = this.mainLock;
//加锁
mainLock.lock();
try {
// Recheck while holding lock.
// Back out on ThreadFactory failure or if
// shut down before lock acquired.
//重新检查线程池的状态
int rs = runStateOf(ctl.get());
//如果线程池是运行状态或者是 关系状态并且任务为null
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
//这里必须要验证下新创建的work不能是运行中
if (t.isAlive())
throw new IllegalThreadStateException();
//把work添加到HashSet,这个workers就是线程池的核心线程
//工作线程是放在BlockingQueue中
workers.add(w);
int s = workers.size();
//对创建的worker进行记录
//largestPoolSize不是Automic的成员是不是存在线程安全?不会的,因为是被主锁锁定中,线程安全
if (s > largestPoolSize)
largestPoolSize = s;
//标志添加成功
workerAdded = true;
}
} finally {
//释放同步锁
mainLock.unlock();
}
//如果添加任务成功了,需要启动worker,只有启动worker之后,工作线程才能被资源进行调度
if (workerAdded) {
//启动worker线程,后台执行task
t.start();
//表示worker启动成功
workerStarted = true;
}
}
} finally {
//如果worker启动失败
if (! workerStarted)
//删除HashSet中的worker,worker数再减1,尝试关闭线程
addWorkerFailed(w);
}
return workerStarted;
}
ThreadPoolExecutor.runWorker(Worker w)方法
执行worker,并且执行当前线程任务task;worker执行完当前任务之后会去workQueue中获取task,然后一直循环执行。如果不获取不到任务,并且worker超过生存时间就会被线程池回收
final void runWorker(Worker w) {
//获取当前线程
Thread wt = Thread.currentThread();
//获取到worker中的线程
Runnable task = w.firstTask;
//清空任务,下次好从任务队列中拿任务
w.firstTask = null;
//允许中断
w.unlock();
boolean completedAbruptly = true;
try {
while (task != null || (task = getTask()) != null) {
w.lock();
//满足条件中断worker
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
beforeExecute(wt, task);
Throwable thrown = null;
try {
//执行咱们自己的代码逻辑
task.run();
} catch (RuntimeException x) {
thrown = x; throw x;
} catch (Error x) {
thrown = x; throw x;
} catch (Throwable x) {
thrown = x; throw new Error(x);
} finally {
afterExecute(task, thrown);
}
} finally {
//线程运行完成,把任务
task = null;
//记录这个worker执行的线程数
w.completedTasks++;
//释放同步资源
w.unlock();
}
}
//完成所有的任务,没有或者获取不到可执行的任务
completedAbruptly = false;
} finally {
//退出当前worker
processWorkerExit(w, completedAbruptly);
}
}
ThreadPoolExecutor.runWorker(Worker w)方法
从workQueue中获取task执行,多个worker并发获取
private Runnable getTask() {
//超时标志
boolean timedOut = false;
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
//如果任务队列为空或者线程池是不执行任务的线程的状态,直接返回null
if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
decrementWorkerCount();
return null;
}
//获取workerQueue中线程线程数
int wc = workerCountOf(c);
//
boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
//判断是否需要从新获取或者关闭当前worker
if ((wc > maximumPoolSize || (timed && timedOut))
&& (wc > 1 || workQueue.isEmpty())) {
if (compareAndDecrementWorkerCount(c))
return null;
continue;
}
try {
//获取workerQueue中task
Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
workQueue.take();
if (r != null)
//获取到任务后直接返回
return r;
timedOut = true;
} catch (InterruptedException retry) {
timedOut = false;
}
}
}
RejectedExecutionHandler
拒绝策略很简单,大家有兴趣稍微看看就行了,大部分使用都是自定义拒绝策略
AbortPolicy:(默认拒接策略)丢掉任务,抛出异常
DiscardPolicy:丢掉任务,什么也不做
DiscardOldestPolicy:删除掉最早加入的任务,尝试再次加入
CallerRunsPolicy:主线程执行这个任务
简单的画个图吧
ForkJoinPool
咱们看张图了解下ForkJoinPool执行流程
从图中可以看出对大任务进行切分然后进行结果汇聚
简单使用
import java.util.Arrays;
import java.util.Random;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.RecursiveAction;
import java.util.concurrent.RecursiveTask;
import java.util.concurrent.TimeUnit;
public class ForkJoinPoolTest {
static int[] nums = new int[1000000];
static final int max_num = 50000;
static Random r = new Random();
static {
long start = System.currentTimeMillis();
for(int i = 0; i < nums.length; i++) {
nums[i] = r.nextInt(100);
}
System.out.println(Arrays.stream(nums).sum());
long end = System.currentTimeMillis();
System.out.println(end-start);
}
static class AddTask extends RecursiveAction{
int start,end;
AddTask(int start,int end){
this.start = start;
this.end = end;
}
@Override
protected void compute() {
if((end - start) <= max_num) {
long sum = 0L;
for(int i = start;i<end;i++) {
sum += nums[i];
}
System.out.println("from:" + start + " to:" + end + " = " + sum);
}else {
//进行切分任务 具体切分规则就是在这里定义
int mid = start + (end - start)/2;
AddTask task1 = new AddTask(start, mid);
AddTask task2 = new AddTask(mid, end);
task1.fork();
task2.fork();
task1.join();
task2.join();
}
}
}
static class AddTaskRes extends RecursiveTask<Long>{
private static final long serialVersionUID = 1L;
int start,end;
AddTaskRes(int start,int end){
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if(end - start <= max_num) {
long sum = 0L;
for(int i = start;i<end;i++) {
sum += nums[i];
}
return sum;
}
//进行切分任务 具体切分规则就是在这里定义
int mid = start + (end - start)/2;
AddTaskRes task1 = new AddTaskRes(start, mid);
AddTaskRes task2 = new AddTaskRes(mid, end);
task1.fork();
task2.fork();
return task1.join() + task2.join();
}
}
public static void main(String[] args) throws Exception {
long start = System.currentTimeMillis();
//无返回值
ForkJoinPool fjp = new ForkJoinPool();
// AddTask task = new AddTask(0, nums.length);
// fjp.execute(task);
// task.join();
// fjp.awaitTermination(2, TimeUnit.SECONDS);
// fjp.submit(task);
//有返回值
AddTaskRes task1 = new AddTaskRes(0, nums.length);
Long sum = fjp.invoke(task1);
System.out.println(sum);
// fjp.execute(task1);
// task1.join();
// fjp.awaitTermination(2, TimeUnit.SECONDS);
long end = System.currentTimeMillis();
System.out.println("耗时:" + (end -start));
fjp.shutdown();
}
好了,今天的分享就到这里了,对文章感兴趣的关注下博主吧~~