帶你快速搞定java并發(fā)庫
一、總覽
計(jì)算機(jī)程序 = 數(shù)據(jù) + 算法。
并發(fā)編程的一切根本原因是為了保證數(shù)據(jù)的正確性,線程的效率性。
Java并發(fā)庫共分為四個(gè)大的部分,如下圖

Executor 和 future 是為了保證線程的效率性
Lock 和數(shù)據(jù)結(jié)構(gòu) 是為了維持?jǐn)?shù)據(jù)的一致性。
Java并發(fā)編程的時(shí)候,思考順序?yàn)椋?/p>
對(duì)自己的數(shù)據(jù)要么加鎖。要么使用提供的數(shù)據(jù)結(jié)構(gòu),保證數(shù)據(jù)的安全性
調(diào)度線程的時(shí)候使用Executor更好的調(diào)度。
二、Executor總覽

Executor 提供一種將任務(wù)提交與每個(gè)任務(wù)將如何運(yùn)行的機(jī)制(包括線程使用的細(xì)節(jié)、調(diào)度等)分離開來的方法。
相當(dāng)于manager,老板讓manager去執(zhí)行一件任務(wù),具體的是誰執(zhí)行,什么時(shí)候執(zhí)行,就不管了。
看上圖的繼承關(guān)系,介紹幾個(gè)

內(nèi)置的線程池基本上都在這里
newScheduledThreadPool 定時(shí)執(zhí)行的線程池
newCachedThreadPool 緩存使用過的線程
newFixedThreadPool 固定數(shù)量的線程池
newWorkStealingPool 將大任務(wù)分解為小任務(wù)的線程池

三、繼承結(jié)構(gòu)
構(gòu)造函數(shù)
包含一個(gè)定時(shí)的service
public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
return new DelegatedScheduledExecutorService
(new ScheduledThreadPoolExecutor(1));
}
static class DelegatedScheduledExecutorService
extends DelegatedExecutorService
implements ScheduledExecutorService {
private final ScheduledExecutorService e;
DelegatedScheduledExecutorService(ScheduledExecutorService executor) {
super(executor);
e = executor;
}
四、怎么保證只有一個(gè)線程
定時(shí)執(zhí)行的時(shí)候調(diào)用這個(gè)方法,調(diào)用過程如下,注意看其中的注釋,由上往下的調(diào)用順序
public ScheduledFuture<?> scheduleWithFixedDelay(Runnable command,
long initialDelay,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
if (delay <= 0)
throw new IllegalArgumentException();
ScheduledFutureTask<Void> sft =
new ScheduledFutureTask<Void>(command,
null,
triggerTime(initialDelay, unit),
unit.toNanos(-delay));
RunnableScheduledFuture<Void> t = decorateTask(command, sft);
sft.outerTask = t;
// 延遲執(zhí)行
delayedExecute(t);
return t;
}
private void delayedExecute(RunnableScheduledFuture<?> task) {
if (isShutdown())
reject(task);
else {
// 加入任務(wù)隊(duì)列
super.getQueue().add(task);
if (isShutdown() &&
!canRunInCurrentRunState(task.isPeriodic()) &&
remove(task))
task.cancel(false);
else
// 確保執(zhí)行
ensurePrestart();
}
}
// 如果worker數(shù)量小于corePoolSize,創(chuàng)建新的線程,其他情況不處理
void ensurePrestart() {
int wc = workerCountOf(ctl.get());
if (wc < corePoolSize)
addWorker(null, true);
else if (wc == 0)
addWorker(null, false);
}
五、怎么保證時(shí)間可以定時(shí)執(zhí)行
public ScheduledFuture<?> schedule(Runnable command,
long delay,
TimeUnit unit) {
if (command == null || unit == null)
throw new NullPointerException();
RunnableScheduledFuture<?> t = decorateTask(command,
new ScheduledFutureTask<Void>(command, null,
triggerTime(delay, unit)));
delayedExecute(t);
return t;
}
在每次執(zhí)行的時(shí)候會(huì)把下一次執(zhí)行的時(shí)間放進(jìn)任務(wù)中
private long triggerTime(long delay, TimeUnit unit) {
return triggerTime(unit.toNanos((delay < 0) ? 0 : delay));
}
/**
* Returns the trigger time of a delayed action.
*/
long triggerTime(long delay) {
return now() +
((delay < (Long.MAX_VALUE >> 1)) ? delay : overflowFree(delay));
}
FutureTask 定時(shí)是通過LockSupport.parkNanos(this, nanos);LockSupport.park(this);
private int awaitDone(boolean timed, long nanos)
throws InterruptedException {
final long deadline = timed ? System.nanoTime() + nanos : 0L;
WaitNode q = null;
boolean queued = false;
for (;;) {
if (Thread.interrupted()) {
removeWaiter(q);
throw new InterruptedException();
}
int s = state;
if (s > COMPLETING) {
if (q != null)
q.thread = null;
return s;
}
else if (s == COMPLETING) // cannot time out yet
Thread.yield();
else if (q == null)
q = new WaitNode();
else if (!queued)
queued = UNSAFE.compareAndSwapObject(this, waitersOffset,
q.next = waiters, q);
else if (timed) {
nanos = deadline - System.nanoTime();
if (nanos <= 0L) {
removeWaiter(q);
return state;
}
//注意這里
LockSupport.parkNanos(this, nanos);
}
else //注意這里
LockSupport.park(this);
}
}
總結(jié):Executor是通過將任務(wù)放在隊(duì)列中,生成的futureTask。然后將生成的任務(wù)在隊(duì)列中排序,將時(shí)間最近的需要出發(fā)的任務(wù)做檢查。如果時(shí)間不到,就阻塞線程到下次出發(fā)時(shí)間。
注意:newSingleThreadScheduledExecutor只會(huì)有一個(gè)線程,不管你提交多少任務(wù),這些任務(wù)會(huì)順序執(zhí)行,如果發(fā)生異常會(huì)取消下面的任務(wù),線程池也不會(huì)關(guān)閉,注意捕捉異常
六、使用
ScheduledExecutorService single = Executors.newSingleThreadScheduledExecutor();
Runnable runnable1 = () -> {
try {
Thread.sleep(4000);
System.out.println("11111111111111");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Runnable runnable2 = () -> {
try {
Thread.sleep(4000);
System.out.println("222");
} catch (InterruptedException e) {
e.printStackTrace();
}
};
single.scheduleWithFixedDelay(runnable1,0,1, TimeUnit.SECONDS);
single.scheduleWithFixedDelay(runnable2,0,2, TimeUnit.SECONDS);
11111111111111 222 11111111111111 222 11111111111111
在項(xiàng)目中要注意關(guān)閉線程池
actionService = Executors.newSingleThreadScheduledExecutor();
actionService.scheduleWithFixedDelay(() -> {
try {
Thread.currentThread().setName("robotActionService");
Integer robotId = robotQueue.poll();
if (robotId == null) {
// 關(guān)閉線程池
actionService.shutdown();
} else {
int aiLv = robots.get(robotId);
if (actionQueueMap.containsKey(aiLv)) {
ActionQueue actionQueue = actionQueueMap.get(aiLv);
actionQueue.doAction(robotId);
}
}
} catch (Exception e) {
// 捕捉異常
LOG.error("",e);
}
}, 1, 1, TimeUnit.SECONDS);
總結(jié)
本篇文章就到這里了,希望能給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
java Unicode和UTF-8之間轉(zhuǎn)換實(shí)例
這篇文章主要介紹了java Unicode和UTF-8之間轉(zhuǎn)換實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09
IDEA自動(dòng)清理類中未使用的import包的操作方法
在項(xiàng)目開發(fā)中,經(jīng)常會(huì)引入很多未使用的import包,這不僅增加了編譯時(shí)間,還會(huì)使代碼可讀性變差,設(shè)置IDEA自動(dòng)清理未使用的import包,可以提高代碼的可讀性,本文給大家介紹IDEA自動(dòng)清理類中未使用的import包的方法,感興趣的朋友一起看看吧2024-09-09
Java并發(fā)編程深入理解之Synchronized的使用及底層原理詳解 上
在并發(fā)編程中存在線程安全問題,主要原因有:1.存在共享數(shù)據(jù) 2.多線程共同操作共享數(shù)據(jù)。關(guān)鍵字synchronized可以保證在同一時(shí)刻,只有一個(gè)線程可以執(zhí)行某個(gè)方法或某個(gè)代碼塊,同時(shí)synchronized可以保證一個(gè)線程的變化可見(可見性),即可以代替volatile2021-09-09
idea中MavenWeb項(xiàng)目不能創(chuàng)建Servlet的解決方案
這篇文章主要介紹了idea中MavenWeb項(xiàng)目不能創(chuàng)建Servlet的解決方案,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-02-02
java公眾平臺(tái)通用接口工具類HttpConnectUtil實(shí)例代碼
下面小編就為大家分享一篇java公眾平臺(tái)通用接口工具類HttpConnectUtil實(shí)例代碼,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2018-01-01
springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作
這篇文章主要介紹了springboot+mybatis攔截器方法實(shí)現(xiàn)水平分表操作,文章圍繞主題展開詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的朋友可以參考一下2022-08-08
JAVA發(fā)送HTTP請(qǐng)求的多種方式詳細(xì)總結(jié)
目前做項(xiàng)目中有一個(gè)需求是這樣的,需要通過Java發(fā)送url請(qǐng)求,查看該url是否有效,這時(shí)我們可以通過獲取狀態(tài)碼來判斷,下面這篇文章主要給大家介紹了關(guān)于JAVA發(fā)送HTTP請(qǐng)求的多種方式總結(jié)的相關(guān)資料,需要的朋友可以參考下2023-01-01
JavaSwing FlowLayout 流式布局的實(shí)現(xiàn)
這篇文章主要介紹了JavaSwing FlowLayout 流式布局的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12

