ExecutorService executorService = new ThreadPoolExecutor(
8,
9,
1000l,
TimeUnit.SECONDS,
new LinkedBlockingDeque<Runnable>(),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
for (int i = 0; i <10; i++) {
executorService.submit(() -> {
System.out.println("执行多线程任务");
});
}
也可以使用
//固定大小线程池 底层ThreadPoolExecutor
Executors.newFixedThreadPool(7);
//缓存线程池核心线程数为0 先试图将任务加入队列,交给空闲线程处理,使用的是SynchronousQueue(只能有一直取才能的往里面放,但是不能存)
// 没有空闲在创建一个工作线程 底层ThreadPoolExecutor
Executors.newCachedThreadPool();
//定时线程池,继承ThreadPoolExecutor,使用的是DelayedWorkQueue(定时队列,只能容纳RunnableScheduledFutures)
Executors.newScheduledThreadPool(7);
//将任务放到本地,当本地队列(先进后取)执行完了 再去窃取别人的队列(后往前取),线程数是默认和cpu核数相等
Executors.newWorkStealingPool();
但是不建议这样使用,其实这个底层也是new ThreadPoolExecutor,但是参数不可控,可能导致OOM
参数注释
public ThreadPoolExecutor(int corePoolSize,//核心线程
int maximumPoolSize,//最大线程数
long keepAliveTime,//线程空闲时间
TimeUnit unit,//时间单位
BlockingQueue<Runnable> workQueue,//阻塞队列
ThreadFactory threadFactory,//线程工厂
RejectedExecutionHandler handler) //队列满后拒绝策略
为什么使用多线程
现在电脑都是多核CPU,使用多线程能提高并发效率,也能避免资源浪费
线程是CPU执行的最小单元,java多线程是多个线程竞争cpu执行时间片
多线程能提高效率,但并不一定能提高线程效率,线程过多就会增加线程切换保存线程状态等操作,也会影响计算效率,使用线程池将线程控制和CPU相差不多的情况,就会减少这样的切换;
因为java创建的线程是基于系统的内核线程,创建过程比较消耗性能,使用线程池可以减少创建线程的消耗
线程池大概原理就是线程内部维护了一个阻塞队列
1.内部维护了一个队列
2.当线程任务提交后判断工作线程是否到核心线程
2.1没有就创建一个线程,线程内部循环线执行本次任务,队列中取一个执行,如果队列中没有就阻塞
2.2 到达了核心线程数就将任务加入队列,如果队列也满了就调用拒绝策略
基本概念:
workerCount, 有效线程数,不一定是工作线程数
workerCount是已被允许启动且未被允许停止的工作线程数量。该值可能与实际的活动线程数暂时不同,例如,当ThreadFactory在被询问时未能创建线程,而当退出的线程仍在执行终止之前的簿记操作时,该值可能会暂时不同。用户可见的池大小报告为*工作集的当前大小
为了将它们打包为一个int,我们将workerCount限制为(2 ^ 29)-1(约5亿)个线程,而不是(2 ^ 31)-1(2 *十亿)可以表示的线程。如果将来可能出现此问题,可以将该变量更改为AtomicLong ,并在下面调整shift / mask常数。但是直到需要出现时,使用int的这段代码才更快,更简单。
runState, 线程池状态是否正在运行,正在关闭等
runState提供主要的生命周期控制,并具有以下值:
runState随时间单调增加,但不必达到每个状态。过渡是:
状态为TERMINATED时,在awaitTermination()中等待的线程将返回。 * 检测从SHUTDOWN到TIDYING的转换比您想要的要简单得多因为在SHUTDOWN状态期间队列在非空之后可能变为空,反之亦然,所以队列可能会变为空,但是如果只有在看到队列为空后才能终止,我们看到 workerCount为0
ThreadPoolExecutor中 有个属性
AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
这个设计个人感觉设计非常巧妙
它既能标识线程池状态能标识工作线程数量,前3位就是表示线程池状态,后面29位表示工作线程数量,RUNNING状态值最小,每次增加一个工作线程就+1,当工作线程2的29次方时就不能增加了 就进入SHUTDOWN状态
线程执行submit是其实调用的java.util.concurrent.AbstractExecutorService#submit(java.lang.Runnable)
这里其实比较简单:
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
//1.创建任务
RunnableFuture<Void> ftask = newTaskFor(task, null);
//2.执行任务
execute(ftask);
return ftask;
}
执行任务在execute
int c = ctl.get();
//判断工作线程数是否达到核心线程数
if (workerCountOf(c) < corePoolSize) {
//创建一个工作线程
if (addWorker(command, true))
return;
c = ctl.get();
}
//检查状态、加入队列
if (isRunning(c) && workQueue.offer(command)) {
int recheck = ctl.get();
//再次判断,如果死了的话本次任务就不会执行了
//不是活跃,就回滚队列,并调用拒接策略
if (! isRunning(recheck) && remove(command))
reject(command);
else if (workerCountOf(recheck) == 0)
addWorker(null, false);
}
else if (!addWorker(command, false))
//失败就调用拒接策略
reject(command);
添加工作线程源码
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
int c = ctl.get();
int rs = runStateOf(c);
// 如果工作线程满了 就不能添加了
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
int wc = workerCountOf(c);
//核心线程满了也不能添加了
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
//cas 修改ct1计数线程
if (compareAndIncrementWorkerCount(c))
break retry;
c = ctl.get(); // Re-read ctl
if (runStateOf(c) != rs)
continue retry;
// else CAS failed due to workerCount change; retry inner loop
}
}
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
//创建一个工作线程,Worker内部维护了一个Thread ,在构造方法通过ThreadFactory创建
w = new 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());
if (rs < SHUTDOWN ||
(rs == SHUTDOWN && firstTask == null)) {
if (t.isAlive()) // precheck that t is startable
throw new IllegalThreadStateException();
//添加工作线程
workers.add(w);
int s = workers.size();
if (s > largestPoolSize)
largestPoolSize = s;
workerAdded = true;
}
} finally {
mainLock.unlock();
}
if (workerAdded) {
//开启线程
t.start();
workerStarted = true;
}
}
} finally {
if (! workerStarted)
addWorkerFailed(w);
}
return workerStarted;
}
线程工作源码
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
//取出当前添加的任务
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//循环执行任务,第一次不为空,执行完成后为空,在从队列取,队列空就阻塞,队列添加后就再次苏醒执行
while (task != null || (task = getTask()) != null) {
w.lock();
// If pool is stopping, ensure thread is interrupted;
// if not, ensure thread is not interrupted. This
// requires a recheck in second case to deal with
// shutdownNow race while clearing interrupt
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;
w.completedTasks++;
w.unlock();
}
}
completedAbruptly = false;
} finally {
processWorkerExit(w, completedAbruptly);
}
}
常规设置
1.如果线程是密集计算型,设置cpu核数+1,为了充分利用资源,1核CPU处理1个线程减少CPU线程切换,多一个防止浪费
2.如果io密集型,设置cpu核两倍,因为现在io大部分工作是分派给DMA(Direct Memory Access)直接内存存取 完成的,可以多个线程去切换 cpu
线程大小 具体根据实际需求设置才是最好的;