并发编程16
ThreadPoolExcutor源码
关闭线程池
shutdown()
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 是否允许shutdown
checkShutdownAccess();
// 设置成shutdown状态
advanceRunState(SHUTDOWN);
// 不会打断正在执行的任务
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
shutdownNow()
public List<Runnable> shutdownNow() {
List<Runnable> tasks;
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
checkShutdownAccess();
advanceRunState(STOP);
interruptWorkers();
// 取出队列中的任务
tasks = drainQueue();
} finally {
mainLock.unlock();
}
tryTerminate();
return tasks;
}
常规线程池
1.测试存活的线程池数目
-
private static void method1() { threadPoolExecutor = new ThreadPoolExecutor(1,2, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(5)); for (int i = 0; i <6 ; i++) { //这里永远只有一个线程在工作? // 并发编程的艺术 -----因为队列能够存放的下任务所以不会启用空闲线程 threadPoolExecutor.execute(()->{ log.debug("worker thread size[{}]",threadPoolExecutor.getPoolSize()); }); } }
2.队列数远大于任务数,无核心线程,最大线程很大,此时任务是马上执行,还是不会
-
public static void main(String[] args) throws InterruptedException, ExecutionException { threadPoolExecutor = new ThreadPoolExecutor(0,1000, 3, TimeUnit.SECONDS, new ArrayBlockingQueue<>(10000)); for (int i = 0; i <6 ; i++) { //执行这个任务的线程是什么线程? 并不核心线程? /** * 核心线程和空闲线程测区别? * 1、空闲线程可能会销毁(存活时间) * 2、核心线程是不会销毁的 * 50 * 线程池是如何保证核心线程不会被销毁的---->空闲线程数为什么会销毁 */ threadPoolExecutor.execute(() -> { log.debug("worker thread size[{}]", threadPoolExecutor.getPoolSize()); }); } }
-
队列数远大于任务数,无核心线程,最大线程很大,此时任务是马上执行,还是不会?
- 按道理应该是不会,因为队列没有满,会一直往队列中添加,此时都没有创建空闲线程,哪来的线程执行任务?但是模拟后,发现执行了,所以执行这些任务的线程是什么线程?
- 核心线程和空闲线程的区别—是否会被销毁
threadPoolExecutor.execute()
execute
-
public void execute(Runnable command) { if (command == null) throw new NullPointerException(); /* * Proceed in 3 steps: * * 1. If fewer than corePoolSize threads are running, try to * start a new thread with the given command as its first * task. The call to addWorker atomically checks runState and * workerCount, and so prevents false alarms that would add * threads when it shouldn't, by returning false. * * 2. If a task can be successfully queued, then we still need * to double-check whether we should have added a thread * (because existing ones died since last checking) or that * the pool shut down since entry into this method. So we * recheck state and if necessary roll back the enqueuing if * stopped, or start a new thread if there are none. * * 3. If we cannot queue task, then we try to add a new * thread. If it fails, we know we are shut down or saturated * and so reject the task. */ int c = ctl.get(); // workerCountOf 获取工作线程数量 // 当核心线程数没满时走这里 if (workerCountOf(c) < corePoolSize) { // addWorker // HashSet<Worker> workers是存放任务的集合 // Worker是任务,同时这个类本身也是一个线程 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); }
addWorker
-
private boolean addWorker(Runnable firstTask, boolean core) { retry: for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. // 检查状态 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; // 改变工作线程数量 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 { // 把传进来的任务实例化 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; }
runWorker
-
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); } }
getTask
-
private Runnable getTask() { boolean timedOut = false; // Did the last poll() time out? for (;;) { int c = ctl.get(); int rs = runStateOf(c); // Check if queue empty only if necessary. if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) { decrementWorkerCount(); return null; } int wc = workerCountOf(c); // Are workers subject to culling? boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; if ((wc > maximumPoolSize || (timed && timedOut)) && (wc > 1 || workQueue.isEmpty())) { if (compareAndDecrementWorkerCount(c)) return null; continue; } try { // 核心线程不会销毁 Runnable r = timed ? workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : workQueue.take(); if (r != null) return r; timedOut = true; } catch (InterruptedException retry) { timedOut = false; } } }
-
核心线程—addWorker(command, true) 取队列任务是通过take方法
-
空闲线程—addWorker(null, false),没有把任务传给他,而是从队列中去取,是通过poll方法取
-
线程池是如何保证核心线程不会被销毁的---->空闲线程数为什么会销毁?
-
excute() | V 判断任务是否是null --(T)-> NullPointerException | F | V 获取工作的线程数量 | V 工作线程数量小于核心线程数量 --F-> 添加到队列 --T-> 判断线程状态 -!running-> reject | | | T F running | | | v V V addWordk1 addWorker3 工作线程是否为0 | | | T | | V V 实例化一个核心线程 addWorker2 | | | V 实例化一个空闲线程
定时任务
Excutors.newScheduledThreadPool()
1.延迟3秒执行
-
可以对任务执行时间做延迟(第一次执行之前)
-
public static void executeTask(){ log.debug("start---task1"); scheduledExecutorService.schedule(()->{ executeTask(); },3, TimeUnit.SECONDS); }
2.nacos心跳机制
-
newScheduledThreadPool里面再套一个newScheduledThreadPool
-
static ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1); public static void executeTask(){ log.debug("start---task1"); scheduledExecutorService.schedule(()->{ executeTask(); },3, TimeUnit.SECONDS); } private static void executeTask() { log.debug("main start"); scheduledExecutorService.schedule(()->{ executeTask(); },3, TimeUnit.SECONDS); }
3.定时每隔相同时间执行scheduleAtFixedRate
-
//period 是包含在线程的执行时间当中的 private static void atFixedRate() { scheduledExecutorService = Executors.newScheduledThreadPool(2); scheduledExecutorService.scheduleAtFixedRate(()->{ log.debug("start---task1"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } },2,1,TimeUnit.SECONDS); }
-
这个方法是固定每秒执行一次,但是任务执行需要耗费两秒,最后的结果实际是每隔两秒执行一次
4.定时每隔固定的延迟时间执行scheduleWithFixedDelay
-
//delay 正在的延迟执行 private static void withFixedDelay() { scheduledExecutorService = Executors.newScheduledThreadPool(2); scheduledExecutorService.scheduleWithFixedDelay(()->{ log.debug("start---task1"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } },2,1,TimeUnit.SECONDS); }
-
此处实际是每隔三秒执行一次,因为任务执行需要花费2秒,然后又延迟了1秒,所以是每隔3秒执行一次
5.每周的周三 22点0分0秒去执行一个任务 同步redis mysql 只有一个线程
-
package BingFaBianCheng.bingFaBianCheng16.shadow.threadPool; import lombok.extern.slf4j.Slf4j; import java.time.DayOfWeek; import java.time.Duration; import java.time.LocalDateTime; import java.util.concurrent.ExecutionException; import java.util.concurrent.ScheduledThreadPoolExecutor; import java.util.concurrent.TimeUnit; /** * @Author 钢牌讲师--子路 * 每周的周三 22点0分0秒去执行一个任务 同步redis mysql 只有一个线程 **/ @Slf4j(topic = "e") public class TestThreadExecutorPool10 { public static void main(String[] args) throws InterruptedException, ExecutionException { //一个线程 ScheduledThreadPoolExecutor scheduled = new ScheduledThreadPoolExecutor(1); /** * 1、当前在编码的时候距离周三还有多久------initialDelay * 当前时间 周三的时间 * 2、下一次多久之后执行(周期时间)delay * 3、单位 */ //LocalDateTime---java8的新时间工具类 //当前时间 LocalDateTime currentTime = LocalDateTime.now(); //周三的时间 // 22点0分0秒 // DayOfWeek.WEDNESDAY--星期三 LocalDateTime targetTime = currentTime.withHour(22).withMinute(0).withSecond(0).withNano(0).with(DayOfWeek.WEDNESDAY); // 当前时间在周三之后,则获取下周三 if(targetTime.compareTo(currentTime)<0){ targetTime = targetTime.plusWeeks(1); } // 获取时间差,转换成秒 long l = Duration.between(currentTime, targetTime).toMillis(); long delay = 1000 * 60 *60 *24 *7; log.debug("当前时间:[{}]",l); log.debug("目标时间:[{}]",targetTime); scheduled.scheduleWithFixedDelay(()->{ log.debug("redis---mysql async"); },l,delay,TimeUnit.MILLISECONDS); } static class MyTask implements Runnable { private int taskNum; public int getTaskNum() { return taskNum; } public MyTask(int num) { this.taskNum = num; } @Override public void run() { log.debug(getTaskNum()+""); } } }