1.文章目錄
- 如何使用線程,線程池實現異步編程,以及其各自的優缺點;
- 線程池的原理,源碼導讀;
2.使用線程實現異步編程
任務類
public class Task {
// taskA
public static void doSomethingA() {
try {
// 模擬耗時
Thread.sleep(200);
System.out.println("taskA done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// taskA
public static void doSomethingB() {
try {
// 模擬耗時
Thread.sleep(200);
System.out.println("taskB done");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
同步編程:
測試:耗時約4s左右
public class SyncExample {
public static void main(String[] argv) throws InterruptedException {
long start = System.currentTimeMillis();
Task.doSomethingA();
Task.doSomethingB();
System.out.println(System.currentTimeMillis() - start);
}
}
顯式使用線程:
package AsynchronousProgramming;
import java.util.concurrent.CountDownLatch;
/**
* @Author: SoftWareKang
* @Name:JAVALEARN
* @Date: 2020/5/30 15:16
*/
public class SyncExample {
private static CountDownLatch countDownLatch = new CountDownLatch(2);
public static void main(String[] argv) throws InterruptedException {
long start = System.currentTimeMillis();
// do A
new Thread(() -> {
try {
Task.doSomethingA();
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
).start();
// do B
new Thread(() -> {
try {
Task.doSomethingB();
countDownLatch.countDown();
} catch (Exception e) {
e.printStackTrace();
}
}
).start();
countDownLatch.await();
System.out.println(System.currentTimeMillis() - start);
}
}
測試耗時:約2.5s左右
如上代碼,我們使用lamba創建Java.lang.Runnable接口的實現類;上述代碼,耗時2.5左右,可以看出異步編程可以大大縮減任務,當任務數多的時候效果更明顯;
JAVA中Deamon與No Deamon的區別,默認情況下我們創建的線程是No Deamon的,線程的類型與JVM退出條件有關,在JAVA中當JVM進程中不存在No Deamon線程就會退出。我們可以顯式的setDaemon(true)方法設置線程爲Deamon線程;
顯示用線程編程的缺點:
- 每次執行異步任務,會直接創建一個Thread來執行異步任務,生產環境是不可以的,因爲線程創建,銷燬是有開銷的,隨意的濫用會消耗完系統線程,從而出錯;推薦使用線程池來執行異步任務,線程池也有效的限制線程數量;
- 上述的線程執行異步任務沒有返回值,如果需要我們可以用JDK的Future;
3.線程池實現異步編程
public class ThreadPoolTest {
// 定義線程池
private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
private final static ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(
AVALIABLE_PROCESSORS, AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(5), new ThreadPoolExecutor.CallerRunsPolicy()
);
public static void main(String[] argv) throws InterruptedException {
long start = System.currentTimeMillis();
POOL_EXECUTOR.execute(() -> {
Task.doSomethingA();
});
// Task.doSomethingB();
POOL_EXECUTOR.execute(() -> {
Task.doSomethingB();
});
System.out.println(System.currentTimeMillis() - start);
Thread.currentThread().join();
}
}
- 上述代碼創建了線程池:核心線程數爲CPU核數,最大線程數爲2*CPU核數;線程池阻塞隊列大小爲5;拒絕策略爲CallerRunsPolicy,當線程池任務飽和,不會丟棄新任務,而是使用調用線程執行;
- 上述代碼,我們減輕了main線程的負擔,把任務交給線程池處理,我們再去完成其他任務;
測試結果:我們發現JVM任務執行完,沒有退出,因爲線程池的線程不是Deamon線程,JVM檢測到存在NO Deamon線程所以不退出;
因此我們可以調用POOL_EXECUTOR.shutdown(); //POOL_EXECUTOR.shutdownNow();關閉線程池;
- 以上的沒有實現異步返回的任務,我們測試下;
4.線程池實現異步返回任務編程
創建任務:返回“taskC"
// TaskC
public static String doSomethingC() {
try {
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" taskC done");
return "taskC";
}
測試:
public class AsncThreadPoolTest {
// 定義線程池
private final static int AVALIABLE_PROCESSORS = Runtime.getRuntime().availableProcessors();
private final static ThreadPoolExecutor POOL_EXECUTOR = new ThreadPoolExecutor(
AVALIABLE_PROCESSORS, AVALIABLE_PROCESSORS * 2, 1, TimeUnit.MINUTES,
new LinkedBlockingQueue<>(),new ThreadPoolExecutor.CallerRunsPolicy()
);
public static void main(String[] argv) throws ExecutionException, InterruptedException {
Future<?> future = POOL_EXECUTOR.submit(() -> Task.doSomethingC());
// 阻塞,等待異步任務結果
System.out.println(future.get());
POOL_EXECUTOR.shutdown();
}
}
- 後續我們針對Future原理,源碼角度來看如果實現;
5.線程池源碼,原理解析
基本屬性
// 線程池狀態(高3位) 線程個數(低29)
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
// 線程掩碼位
private static final int COUNT_BITS = Integer.SIZE - 3;
// 容量
private static final int CAPACITY = (1 << COUNT_BITS) - 1;
// runState is stored in the high-order bits
// 線程池狀態
// 111
private static final int RUNNING = -1 << COUNT_BITS;
// 000
private static final int SHUTDOWN = 0 << COUNT_BITS;
// 001
private static final int STOP = 1 << COUNT_BITS;
// 010
private static final int TIDYING = 2 << COUNT_BITS;
// 011
private static final int TERMINATED = 3 << COUNT_BITS;
// 獲取運行狀態
private static int runStateOf(int c) { return c & ~CAPACITY; }
//線程個數
private static int workerCountOf(int c) { return c & CAPACITY; }
// 計算ctl新值
private static int ctlOf(int rs, int wc) { return rs | wc; }
// 任務阻塞隊列
private final BlockingQueue<Runnable> workQueue;
// lock
private final ReentrantLock mainLock = new ReentrantLock();
// woker集合
private final HashSet<Worker> workers = new HashSet<Worker>();
// codition條件集合
private final Condition termination = mainLock.newCondition();
// 最大線程數
private int largestPoolSize;
// 任務完成個數
private long completedTaskCount;
// 線程工廠
private volatile ThreadFactory threadFactory;
// 拒絕策略
private volatile RejectedExecutionHandler handler;
// 線程空閒時間
private volatile long keepAliveTime;
// 是否允許timeount
private volatile boolean allowCoreThreadTimeOut;
// 核心線程數
private volatile int corePoolSize;
//
private volatile int maximumPoolSize;
/**
* The default rejected execution handler
*/
private static final RejectedExecutionHandler defaultHandler =
new AbortPolicy();
- corePoolSize:線程池核心線程個數.
- workQueue:用於保存等待執行任務的阻塞隊列;
- maximunPoolSize:線程池最大線程數量;
- threadFactory:線程工廠類;
- defaultHandler:飽和策略,當任務隊列滿後,線程個數達到了maximunPoolSize就執行飽和策略,默認AbortPolicy(拋出異常),Caller Runs Policy(使用調用者所在的線程來運行任務),DiscardOldestPolicy(丟棄一個任務,執行當前的任務),DiscardPolicy(直接丟棄);
初始化方法:
// 使用默認threadFactory,拒絕策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
// 自定義線程工廠使用
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
threadFactory, defaultHandler);
}
// 自定義線程工廠,拒絕策略
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
RejectedExecutionHandler handler) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), handler);
}
// 核心方法
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {
// 參數校驗賦值
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
默認線程工廠:defaultThreadFactory
static class DefaultThreadFactory implements ThreadFactory {
// 原子類
private static final AtomicInteger poolNumber = new AtomicInteger(1);
// 線程組
private final ThreadGroup group;
// 技術
private final AtomicInteger threadNumber = new AtomicInteger(1);
// threadname前綴
private final String namePrefix;
DefaultThreadFactory() {
SecurityManager s = System.getSecurityManager();
group = (s != null) ? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
// 創建一個線程
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
// 如果線程位Daemon,置爲非Daemon
if (t.isDaemon())
t.setDaemon(false);
// 重置線程優先級爲正常5
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
execut:線程池執行任務的方法:
public void execute(Runnable command) {
if (command == null)
throw new NullPointerException();
// 獲取ctl(運行狀態&線程數)
int c = ctl.get();
// 如果線程數小於核心線程數,直接創建一個線程
if (workerCountOf(c) < corePoolSize) {
// true表示,當前創建線程數量應<=核心線程數
if (addWorker(command, true))
return;
// 如果沒有添加成功,獲取新的ctl
c = ctl.get();
}
// 如果線程池處於Running狀態,添加任務到阻塞隊列
if (isRunning(c) && workQueue.offer(command)) {
// 二次校驗
int recheck = ctl.get();
// 如果不是running則從隊列刪除任務,並執行拒絕策略
if (! isRunning(recheck) && remove(command))
reject(command);
// 如果線程數爲0
else if (workerCountOf(recheck) == 0)
// 添加一個線程
addWorker(null, false);
}
// 如果線程數達到了核心線程數,且添加阻塞隊列失敗,則創建一個線程
else if (!addWorker(command, false))
// 失敗則,拒絕
reject(command);
}
addWorker(Runnable, bool)方法:創建一個線程,運行runnable任務,bool決定最大線程爲核心線程數,還是最大線程數來執行拒絕策略
private boolean addWorker(Runnable firstTask, boolean core) {
retry:
for (;;) {
// 獲取ctl
int c = ctl.get();
// 獲取運行狀態
int rs = runStateOf(c);
// 檢測運行狀態以及隊列狀態
if (rs >= SHUTDOWN &&
! (rs == SHUTDOWN &&
firstTask == null &&
! workQueue.isEmpty()))
return false;
for (;;) {
// 獲取線程數
int wc = workerCountOf(c);
// 如果線程數大於容量/(核心線程數/最大線程數)core決定
if (wc >= CAPACITY ||
wc >= (core ? corePoolSize : maximumPoolSize))
return false;
// CAS算法,增加線程數,成功跳出循環
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
}
}
// 任務start,worker添加成功標誌
boolean workerStarted = false;
boolean workerAdded = false;
Worker w = null;
try {
// worker包裝firsrTask
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;
}
幾種拒絕策略:
// 使用調用者的線程運行
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
// 拋出異常
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
// 默認不管
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
// 拋出隊列中的一個,然後運行他
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
前置,後置方法;子類可以進行擴展
protected void beforeExecute(Thread t, Runnable r) { }
protected void afterExecute(Runnable r, Throwable t) { }
線程池提供的三種異步Future方式:都是包裝爲RunnableFuture執行
public Future<?> submit(Runnable task) {
if (task == null) throw new NullPointerException();
RunnableFuture<Void> ftask = newTaskFor(task, null);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Runnable task, T result) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task, result);
execute(ftask);
return ftask;
}
/**
* @throws RejectedExecutionException {@inheritDoc}
* @throws NullPointerException {@inheritDoc}
*/
public <T> Future<T> submit(Callable<T> task) {
if (task == null) throw new NullPointerException();
RunnableFuture<T> ftask = newTaskFor(task);
execute(ftask);
return ftask;
}
Worker:我們線程池調度這東西,看下他源碼;
// run方法
public void run() {
runWorker(this);
}
// 核心方法
final void runWorker(Worker w) {
Thread wt = Thread.currentThread();
Runnable task = w.firstTask;
w.firstTask = null;
w.unlock(); // allow interrupts
boolean completedAbruptly = true;
try {
//如果task爲null 任務隊列獲取的也是null
while (task != null || (task = getTask()) != null) {
w.lock();
// shutDownNow可以打斷當前線程,可以自己測試下
if ((runStateAtLeast(ctl.get(), STOP) ||
(Thread.interrupted() &&
runStateAtLeast(ctl.get(), STOP))) &&
!wt.isInterrupted())
wt.interrupt();
try {
// 執行前
beforeExecute(wt, task);
Throwable thrown = null;
try {
// 核心,也是我們寫的task
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);
}
}
- 可以看出來,我們的worker就是一個thread,不斷的執行任務;
任務運行完:執行清理
private void processWorkerExit(Worker w, boolean completedAbruptly) {
if (completedAbruptly) // If abrupt, then workerCount wasn't adjusted
decrementWorkerCount();
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 完成任務數
completedTaskCount += w.completedTasks;
// woker集合刪除此woker
workers.remove(w);
} finally {
mainLock.unlock();
}
// 嘗試設置線程池狀態爲terminated
tryTerminate();
int c = ctl.get();
if (runStateLessThan(c, STOP)) {
if (!completedAbruptly) {
int min = allowCoreThreadTimeOut ? 0 : corePoolSize;
if (min == 0 && ! workQueue.isEmpty())
min = 1;
if (workerCountOf(c) >= min)
return; // replacement not needed
}
addWorker(null, false);
}
}
關閉線程池:
ShutDown:關閉線程池,線程池不會接受新任務,工作隊列的任務執行完;
public void shutdown() {
final ReentrantLock mainLock = this.mainLock;
// 保證線程同步
mainLock.lock();
try {
checkShutdownAccess();
// 設置線程狀態
advanceRunState(SHUTDOWN);
// 設置中斷標誌
interruptIdleWorkers();
onShutdown(); // hook for ScheduledThreadPoolExecutor
} finally {
mainLock.unlock();
}
tryTerminate();
}
private void advanceRunState(int targetState) {
for (;;) {
int c = ctl.get();
if (runStateAtLeast(c, targetState) ||
ctl.compareAndSet(c, ctlOf(targetState, workerCountOf(c))))
break;
}
}
private void interruptIdleWorkers(boolean onlyOne) {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
for (Worker w : workers) {
Thread t = w.thread;
// 如果線程沒有被中斷,並且沒有運行
if (!t.isInterrupted() && w.tryLock()) {
try {
// 中斷
t.interrupt();
} catch (SecurityException ignore) {
} finally {
w.unlock();
}
}
if (onlyOne)
break;
}
} finally {
mainLock.unlock();
}
}
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;
}
private void interruptWorkers() {
final ReentrantLock mainLock = this.mainLock;
mainLock.lock();
try {
// 全部打斷
for (Worker w : workers)
w.interruptIfStarted();
} finally {
mainLock.unlock();
}
}
6.總結
- 本文描述了異步執行的好處,以及其具體實現;
- 顯式線程的優缺點,線程池優點以及其實現原理,重要源碼導讀;
- 線程池線程增加策略:如果當前線程數<=核心線程數,新建一個線程;然後給阻塞隊列添加,隊列滿了,當線程數<=最大線程數則新建線程;
- 上述的異步並不是比較好的實現,Future必須調用Get阻塞當前線程纔可以拿到任務返回值,後續文章,會對Future源碼,以及JDK新增的CompletableAbleFuture實現異步編程;