摘要:的線程機(jī)制是搶占式。會(huì)讓出當(dāng)多個(gè)線程并發(fā)的對(duì)主存中的數(shù)據(jù)進(jìn)行操作時(shí),有且只有一個(gè)會(huì)成功,其余均失敗。和對(duì)象只有在困難的多線程問(wèn)題中才是必須的。
并發(fā)簡(jiǎn)述
并發(fā)通常是用于提高運(yùn)行在單處理器上的程序的性能。在單 CPU 機(jī)器上使用多任務(wù)的程序在任意時(shí)刻只在執(zhí)行一項(xiàng)工作。
并發(fā)編程使得一個(gè)程序可以被劃分為多個(gè)分離的、獨(dú)立的任務(wù)。一個(gè)線程就是在進(jìn)程中的一個(gè)單一的順序控制流。java的線程機(jī)制是搶占式。
線程的好處是提供了輕量級(jí)的執(zhí)行上下文切換,只改變了程序的執(zhí)行序列和局部變量。
多線程的主要缺陷:
volatile關(guān)鍵字等待共享資源的時(shí)候性能降低。
需要處理線程的額外 CPU 花費(fèi)。
糟糕的程序設(shè)計(jì)導(dǎo)致不必要的復(fù)雜度。
有可能產(chǎn)生一些病態(tài)行為,若餓死、競(jìng)爭(zhēng)、死鎖和活鎖。
不同平臺(tái)導(dǎo)致的不一樣。
源來(lái):
當(dāng)程序運(yùn)行,JVM會(huì)為每一個(gè)線程分配一個(gè)獨(dú)立的緩存用于提高執(zhí)行效率,每一個(gè)線程都在自己獨(dú)立的緩存中操作各自的數(shù)據(jù)。一個(gè)線程在緩沖中對(duì)數(shù)據(jù)進(jìn)行修改,寫入到主存后,其他線程無(wú)法得知數(shù)據(jù)已被更改,仍在操作緩存中已過(guò)時(shí)的數(shù)據(jù),為了解決這個(gè)問(wèn)題,提供了volatile關(guān)鍵字,實(shí)現(xiàn)內(nèi)存可見,一旦主存數(shù)據(jù)被修改,便致使其他線程緩存數(shù)據(jù)行無(wú)效,強(qiáng)制前往主存獲取新數(shù)據(jù)。
Example:內(nèi)存不可見,導(dǎo)致主線程無(wú)法結(jié)束。
class ThreadDemo implements Runnable { //添加volatile關(guān)鍵字可實(shí)現(xiàn)內(nèi)存可見性 public volatile boolean flag = false; public boolean flag = Boolean.false; @Override public void run() { try { Thread.sleep(200); } catch (InterruptedException e) { } flag = Boolean.true; System.out.println("ThreadDemo over"); } public boolean isFlag() { return flag; } } public class TestVolatile { public static void main(String[] args) { ThreadDemo demo = new ThreadDemo(); new Thread(demo).start(); while (true) { if (demo.flag || demo.isFlag()) { System.out.println("Main over"); break; } } } }/*output:打印ThreadDemo over,主線程持續(xù)循環(huán)*/
作用:
當(dāng)多個(gè)線程操作共享數(shù)據(jù)時(shí),保證內(nèi)存中的數(shù)據(jù)可見性。采用底層的內(nèi)存柵欄,及時(shí)的將緩存中修改的數(shù)據(jù)刷新到主存中,并導(dǎo)致其他線程所緩存的數(shù)據(jù)無(wú)效,使得這些線程必須去主存中獲取修改的數(shù)據(jù)。
優(yōu)缺點(diǎn):
保證內(nèi)存可見性,讓各個(gè)線程能夠彼此獲取最新的內(nèi)存數(shù)據(jù)。
較傳統(tǒng)synchronized加鎖操作提高了效率,若有線程正在操作被synchronized修飾的代碼塊數(shù)據(jù)時(shí),其他線程試圖進(jìn)行操作,發(fā)現(xiàn)已被其他線程占用,試圖操作的線程必須掛起,等到下一次繼續(xù)嘗試操作。
對(duì)volatile修飾的數(shù)據(jù)被修改后,其他線程必須前往主存中讀取,若修改頻繁,需要不斷讀取主存數(shù)據(jù),效率將會(huì)降低。
使用volatile,底層采用內(nèi)存柵欄,JVM將不會(huì)對(duì)其提供指令重排序及其優(yōu)化。
不具備互斥性。多個(gè)線程可以同時(shí)對(duì)數(shù)據(jù)進(jìn)行操作,只是由原來(lái)的在緩存操作轉(zhuǎn)變成了直接在主存中操作。(synchronized是互斥的,一個(gè)線程正在執(zhí)行,其他線程必須掛起等待)
不保證變量的原子性。使用volatile僅僅是一個(gè)能保證可見性的輕量級(jí)同步策略。
原子變量與 CAS 算法Example:使用volatile修飾,number自增問(wèn)題。
class ThreadDemo implements Runnable { public volatile int number = 0; @Override public void run() { try { Thread.sleep(200); } catch (Exception e) { } System.out.print(getIncrementNumber() + " "); } public int getIncrementNumber() { return ++number; } } public class TestAtomic { public static void main(String[] args) { ThreadDemo demo = new ThreadDemo(); for (int i = 0; i < 10; i++) { new Thread(demo).start(); } } }/*output: 1 5 4 7 3 9 2 1 8 6 */
// ++number底層原理思想 int temp = number; // ① number = number + 1; // ② temp = number; // ③ return temp; // ④
由 ++number 可知,返回的是 temp 中存儲(chǔ)的值,且自增是一個(gè)多步操作,當(dāng)多個(gè)線程調(diào)用 incrementNumber方法時(shí),方法去主存中獲取 number 值放入 temp 中,根據(jù) CPU 時(shí)間片切換,當(dāng) A 線程完成了 ③ 操作時(shí),時(shí)間片到了被中斷,A 線程開始執(zhí)行 ① 時(shí)不幸被中斷,接著 A 獲取到了CPU執(zhí)行權(quán),繼續(xù)執(zhí)行完成 ④ 操作更新了主存中的值,緊接著 B 線程開始執(zhí)行,但是 B 線程 temp中存儲(chǔ)的值已經(jīng)過(guò)時(shí)了。注意:自增操作為四步,只有在第四步的時(shí)候才會(huì)刷新主存的值,而不是number = number + 1 操作就反映到主存中去。如圖所示:
源來(lái):
volatile只能保證內(nèi)存可見性,對(duì)多步操作的變量,無(wú)法保證其原子性,為了解決這個(gè)問(wèn)題,提供了原子變量。
作用:
原子變量既含有volatile的內(nèi)存可見性,又提供了對(duì)變量原子性操作的支持,采用底層硬件對(duì)并發(fā)操作共享數(shù)據(jù)的 CAS(Compare-And-Swap)算法,保證數(shù)據(jù)的原子性。
提供的原子類:
類 | 描述 |
---|---|
AtomicBoolean | 一個(gè) boolean值可以用原子更新。 |
AtomicInteger | 可能原子更新的 int值。 |
AtomicIntegerArray | 一個(gè) int數(shù)組,其中元素可以原子更新。 |
AtomicIntegerFieldUpdater |
基于反射的實(shí)用程序,可以對(duì)指定類的指定的 volatile int字段進(jìn)行原子更新。 |
AtomicLong | 一個(gè) long值可以用原子更新。 |
AtomicLongArray | 可以 long地更新元素的 long數(shù)組。 |
AtomicLongFieldUpdater |
基于反射的實(shí)用程序,可以對(duì)指定類的指定的 volatile long字段進(jìn)行原子更新。 |
AtomicMarkableReference |
AtomicMarkableReference維護(hù)一個(gè)對(duì)象引用以及可以原子更新的標(biāo)記位。 |
AtomicReference |
可以原子更新的對(duì)象引用。 |
AtomicReferenceArray |
可以以原子方式更新元素的對(duì)象引用數(shù)組。 |
AtomicReferenceFieldUpdater |
一種基于反射的實(shí)用程序,可以對(duì)指定類的指定的 volatile volatile引用原子更新。 |
AtomicStampedReference |
AtomicStampedReference維護(hù)對(duì)象引用以及可以原子更新的整數(shù)“印記”。 |
DoubleAccumulator | 一個(gè)或多個(gè)變量一起維護(hù)使用提供的功能更新的運(yùn)行的值 double 。 |
DoubleAdder | 一個(gè)或多個(gè)變量一起保持初始為零 double和。 |
LongAccumulator | 一個(gè)或多個(gè)變量,它們一起保持運(yùn)行 long使用所提供的功能更新值。 |
LongAdder | 一個(gè)或多個(gè)變量一起保持初始為零 long總和。 |
CAS算法:
CAS(Compare-And-Swap)是底層硬件對(duì)于原子操作的一種算法,其包含了三個(gè)操作數(shù):內(nèi)存值(V),預(yù)估值(A),更新值(B)。當(dāng)且僅當(dāng) V == A 時(shí), 執(zhí)行 V = B 操作;否則不執(zhí)行任何結(jié)果。這里需要注意,A 和 B 兩個(gè)操作數(shù)是原子性的,同一時(shí)刻只能有一個(gè)線程進(jìn)行AB操作。
優(yōu)缺點(diǎn):
操作失敗時(shí),直接放棄結(jié)果,并不釋放對(duì)CPU的控制權(quán),進(jìn)而可以繼續(xù)嘗試操作,不必掛起等待。(synchronized會(huì)讓出CPU)
當(dāng)多個(gè)線程并發(fā)的對(duì)主存中的數(shù)據(jù)進(jìn)行操作時(shí),有且只有一個(gè)會(huì)成功,其余均失敗。
原子變量中封裝了用于對(duì)數(shù)據(jù)的原子操作,簡(jiǎn)化了代碼的編寫。
Collection并發(fā)類HashMap 與 HashTable簡(jiǎn)述
HashMap是線程不安全的,而HashTable是線程安全的,因?yàn)镠ashTable所維護(hù)的Hash表存在著獨(dú)占鎖,當(dāng)多個(gè)線程并發(fā)訪問(wèn)時(shí),只能有一個(gè)線程可進(jìn)行操作,但是對(duì)于復(fù)合操作時(shí),HashTable仍然存在線程安全問(wèn)題,不使用HashTable的主要原因還是效率低下。
// 功能:不包含obj,則添加 if (!hashTable.contains(obj)) { // 復(fù)合操作,執(zhí)行此處時(shí)線程中斷,obj被其他線程添加至容器中,此處繼續(xù)執(zhí)行將導(dǎo)致重復(fù)添加 hashTable.put(obj); } 可知上述兩個(gè)操作需要 “原子性”,為了達(dá)到效果,還不是得對(duì)代碼塊進(jìn)行同步
ConcurrentHashMap
采用鎖分段機(jī)制,分為 16 個(gè)段(并發(fā)級(jí)別),每一個(gè)段下有一張表,該表采用鏈表結(jié)構(gòu)鏈接著各個(gè)元素,每個(gè)段都使用獨(dú)立的鎖。當(dāng)多個(gè)線程并發(fā)操作的時(shí)候,根據(jù)各自的級(jí)別不同,操作不同的段,多個(gè)線程并行操作,明顯提高了效率,其次還提供了復(fù)合操作的諸多方法。注:jdk1.8由原來(lái)的數(shù)組+單向鏈表結(jié)構(gòu)轉(zhuǎn)換成數(shù)據(jù)+單向鏈表+紅黑樹結(jié)構(gòu)。
ConcurrentSkipListMap和ConcurrentSkipListSet
有序的哈希表,通過(guò)跳表實(shí)現(xiàn),不允許null作為鍵或值。ConcurrentSkipListMap詳解
CopyOnWriteArrayList 和 CopyOnWriteArraySet
對(duì)collection進(jìn)行寫入操作時(shí),將導(dǎo)致創(chuàng)建整個(gè)底層數(shù)組的副本,而源數(shù)組將保留在原地,使得復(fù)制的數(shù)組在被修改時(shí),讀取操作可以安全的執(zhí)行。當(dāng)修改完成時(shí),一個(gè)原子性的操作將把心的數(shù)組換人,使得新的讀取操作可以看到新的修改。
好處之一是當(dāng)多個(gè)迭代器同時(shí)遍歷和修改列表時(shí),不會(huì)拋出ConcurrentModificationException。
小結(jié):
當(dāng)期望許多線程訪問(wèn)一個(gè)給定 collection 時(shí),ConcurrentHashMap 通常優(yōu)于同步的 HashMap
ConcurrentSkipListMap 通常優(yōu)于同步的 TreeMap。
當(dāng)期望的讀數(shù)和遍歷遠(yuǎn)遠(yuǎn)大于列表的更新數(shù)時(shí),CopyOnWriteArrayList 優(yōu)于同步的 ArrayList。
并發(fā)迭代操作多時(shí),可選擇CopyOnWriteArrayList 和 CopyOnWriteArraySet。
高并發(fā)情況下,可選擇ConcurrentSkipListMap和ConcurrentSkipListSet
CountDownLatch閉鎖源由:
當(dāng)一個(gè)修房子的 A 線程正在執(zhí)行,需要磚頭時(shí),開啟了一個(gè)線程 B 去拉磚頭,此時(shí) A 線程需要等待 B 線程的結(jié)果后才能繼續(xù)執(zhí)行時(shí),但是線程之間都是并行操作的,為了解決這個(gè)問(wèn)題,提供了CountDownLatch。
作用:
一個(gè)同步輔助類,為了保證執(zhí)行某些操作時(shí),“所有準(zhǔn)備事項(xiàng)都已就緒”,僅當(dāng)某些操作執(zhí)行完畢后,才能執(zhí)行后續(xù)的代碼塊,否則一直等待。
CountDownLatch中存在一個(gè)鎖計(jì)數(shù)器,如果鎖計(jì)數(shù)器不為 0 的話,它會(huì)阻塞任何一個(gè)調(diào)用 await() 方法的線程。也就是說(shuō),當(dāng)一個(gè)線程調(diào)用 await() 方法時(shí),如果鎖計(jì)數(shù)器不等于 0,那么就會(huì)一直等待鎖計(jì)數(shù)器為 0 的那一刻,這樣就解決了需要等待其他線程執(zhí)行完畢才執(zhí)行的需求。
Example:
class ThreadDemo implements Runnable { private CountDownLatch latch = null; public ThreadDemo(CountDownLatch latch) { this.latch = latch; } @Override public void run() { try { System.out.println("execute over"); } finally { latch.countDown(); // 必須保證計(jì)數(shù)器減一 } } } public class TestCountDownLatch { public static void main(String[] args) { final int count = 10; final CountDownLatch latch = new CountDownLatch(count); ThreadDemo demo = new ThreadDemo(latch); for (int i = 0; i < count; ++i) { new Thread(demo).start(); } try { latch.await(); // 等待計(jì)數(shù)器為 0 System.out.println("其他線程結(jié)束,繼續(xù)往下執(zhí)行..."); } catch (InterruptedException e) { e.printStackTrace(); } } }/**output: execute over ... 其他線程結(jié)束,繼續(xù)往下執(zhí)行... */
細(xì)節(jié):
子線程完畢后,必須調(diào)用 countDown() 方法使得 鎖計(jì)數(shù)器減一,否則將會(huì)導(dǎo)致調(diào)用 await() 方法的線程持續(xù)等待,盡可能的放置在 finally 中。
鎖計(jì)數(shù)器的個(gè)數(shù)與子線程數(shù)最好相等,只要計(jì)數(shù)器等于 0,不論是否還存在子線程,await() 方法將得到響應(yīng),繼續(xù)執(zhí)行后續(xù)代碼。
Callable接口源由:
當(dāng)開啟一個(gè)線程執(zhí)行運(yùn)算時(shí),可能會(huì)需要該線程的計(jì)算結(jié)果,之前的 implements Runnable 和 extends Thread 的 run() 方法并沒有提供可以返回的功能,因此提供了 Callable接口。 Callable 的運(yùn)行結(jié)果, 需要使用 FutureTask 類來(lái)接受。
Example:
class ThreadDemo implements CallableLock同步鎖和Condition線程通信控制對(duì)象{ private Integer cycleValue; public ThreadDemo(Integer cycleValue) { this.cycleValue = cycleValue; } @Override public Integer call() throws Exception { int result = 0; for (int i=0; i task = new FutureTask<>(demo); new Thread(task).start(); Integer result = task.get(); // 等待計(jì)算結(jié)果返回, 閉鎖 System.out.println(result); } }/*output:1073741825 */
Lock:在進(jìn)行性能測(cè)試時(shí),使用Lock通常會(huì)比使用synchronized要高效許多,并且synchronized的開銷變化范圍很大,而Lock相對(duì)穩(wěn)定。只有在性能調(diào)優(yōu)時(shí)才使用Lock對(duì)象。
Condition: 替代了 Object 監(jiān)視器方法的使用,描述了可能會(huì)與鎖有關(guān)的條件標(biāo)量,相比 Object 的 notifyAll() ,Condition 的 signalAll() 更安全。Condition 實(shí)質(zhì)上被綁定到一個(gè)鎖上,使用newCondition() 方法為 Lock 實(shí)例獲取 Condition。
Lock和Condition對(duì)象只有在困難的多線程問(wèn)題中才是必須的。
synchonized與Lock的區(qū)別:
synchonized | Lock |
---|---|
隱式鎖 | 顯示鎖 |
JVM底層實(shí)現(xiàn),由JVM維護(hù) | 由程序員手動(dòng)維護(hù) |
靈活控制(也有風(fēng)險(xiǎn)) |
“虛假喚醒”:當(dāng)一個(gè)線程A在等待時(shí),被另一個(gè)線程喚醒,被喚醒的線程不一定滿足了可繼續(xù)向下執(zhí)行的條件,如果被喚醒的線程未滿足條件,而又向下執(zhí)行了,那么稱這個(gè)現(xiàn)象為 “虛假喚醒”。
// 安全的方式,保證退出等待循環(huán)前,一定能滿足條件 while (條件) { wait(); }
Example:生產(chǎn)消費(fèi)者
// 產(chǎn)品car class Car { private Lock lock = new ReentrantLock(); private Condition condition = lock.newCondition(); private boolean available = false; // false:無(wú)貨;true有貨 public void put(){ lock.lock(); try { while (available) { // 有貨等待 condition.await(); } System.out.println(Thread.currentThread().getName() + "put(): 進(jìn)貨"); available = true; condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } public void get() { lock.lock(); try { while (!available) { // 無(wú)貨等待 condition.await(); } System.out.println(Thread.currentThread().getName() + "get():出貨"); available = false; condition.signalAll(); } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); } } } // 消費(fèi)者 class Consume implements Runnable { private Car car; public Consume(Car car) { this.car = car; } @Override public void run() { for (int i=0; i 每一個(gè) 對(duì)lock()的調(diào)用都必須緊跟著一個(gè) try-finally 子句,用以保證可以在任何情況下都能釋放鎖,任務(wù)在調(diào)用 await()、signal()、signalAll()之前,必須擁有鎖。lock.lock(); try { ... // 業(yè)務(wù)代碼 } finally { lock.unlock(); }ReadWriteLock讀寫鎖源由:
上述講解的鎖都是讀寫一把鎖,不論是讀或?qū)懀际且话焰i解決,當(dāng)多線程訪問(wèn)數(shù)據(jù)時(shí),若發(fā)生了一千次操作,其中的寫操作只執(zhí)行了一次,數(shù)據(jù)的更新率非常低,那么每次進(jìn)行讀操作時(shí),都要加鎖讀取”不會(huì)更改的“數(shù)據(jù),顯然是不必要的開銷,因此出現(xiàn)了 ReadWriteLock 讀寫鎖,該對(duì)象提供讀鎖和寫鎖。
作用:
ReadWriteLock 維護(hù)了一對(duì)相關(guān)的鎖,一個(gè)用于只讀操作,另一個(gè)用于寫入操作。只要沒有 write寫入操作,那么多個(gè)線程可以同時(shí)進(jìn)行持有讀鎖。而寫入鎖是獨(dú)占的,當(dāng)執(zhí)行寫操作時(shí),其他線程不可寫,也不可讀。
性能的提升取決于讀寫操作期間讀取數(shù)據(jù)相對(duì)于修改數(shù)據(jù)的頻率,如果讀取操作遠(yuǎn)遠(yuǎn)大于寫入操作時(shí),便能增強(qiáng)并發(fā)性。
Example:
class Demo { private int value = 0; private ReadWriteLock lock = new ReentrantReadWriteLock(); public void read() { lock.readLock().lock(); try { System.out.println(Thread.currentThread().getName() + " : " + value); } finally { lock.readLock().unlock(); } } public void write(int value) { lock.writeLock().lock(); try { this.value = value; System.out.println("write(" + value + ")"); } finally { lock.writeLock().unlock(); } } } class ReadLock implements Runnable { private Demo demo = null; public ReadLock(Demo demo) { this.demo = demo; } @Override public void run() { for (int i=0; i<20; ++i) { demo.read(); try { Thread.sleep(320); } catch (InterruptedException e) { } } } } class WriteLock implements Runnable { private Demo demo = null; public WriteLock(Demo demo) { this.demo = demo; } @Override public void run() { for (int i=0; i<10; ++i) { demo.write(i); try { Thread.sleep(200); } catch (InterruptedException e) { } } } } public class TestReadWriteLock { public static void main(String[] args) { Demo demo = new Demo(); ReadLock readLock = new ReadLock(demo); WriteLock writeLock = new WriteLock(demo); for (int i=0; i<3; ++i) { new Thread(readLock, i + "--").start(); } new Thread(writeLock).start(); } }/**output: 0-- : 0 1-- : 0 2-- : 0 write(0) write(1) 1-- : 1 2-- : 1 0-- : 1 write(2) write(3) 1-- : 3 0-- : 3 ... */線程池與線程調(diào)度源來(lái):
在傳統(tǒng)操作中(如連接數(shù)據(jù)庫(kù)),當(dāng)我們需要使用一個(gè)線程的時(shí)候,就 直接創(chuàng)建一個(gè)線程,線程完畢后被垃圾收集器回收。每一次需要線程的時(shí)候,不斷的創(chuàng)建與銷毀,大大增加了資源的開銷。
作用:
線程池維護(hù)著一個(gè)線程隊(duì)列,該隊(duì)列中保存著所有等待著的線程,避免了重復(fù)的創(chuàng)建與銷毀而帶來(lái)的開銷。
體系結(jié)構(gòu):
Execuotr:負(fù)責(zé)線程的使用與調(diào)度的根接口。 |- ExecutorService:線程池的主要接口。 |- ForkJoinPool:采用分而治之技術(shù)將任務(wù)分解。 |- ThreadPoolExecutor:線程池的實(shí)現(xiàn)類。 |- ScheduledExecutorService:負(fù)責(zé)線程調(diào)度的子接口。 |- ScheduledThreadPoolExecutor:負(fù)責(zé)線程池的調(diào)度。繼承ThreadPoolExecutor并實(shí)現(xiàn)ScheduledExecutorService接口Executors 工具類API描述:
方法 描述 ExecutorService newFixedThreadPool(int nThreads) 創(chuàng)建一個(gè)可重用固定數(shù)量的無(wú)界隊(duì)列線程池。使用了有限的線程集來(lái)執(zhí)行所提交的所有任務(wù)。創(chuàng)建的時(shí)候可以一次性預(yù)先進(jìn)行代價(jià)高昂的線程分配。 ExecutorService newWorkStealingPool(int parallelism) 創(chuàng)建一個(gè)維護(hù)足夠的線程以支持給定的parallelism并行級(jí)別的線程池。 ExecutorService newSingleThreadExecutor() 創(chuàng)建一個(gè)使用單個(gè)線程運(yùn)行的無(wú)界隊(duì)列的執(zhí)行程序。 ExecutorService newCachedThreadPool() 創(chuàng)建一個(gè)根據(jù)需要?jiǎng)?chuàng)建新線程的線程池,當(dāng)有可用線程時(shí)將重新使用以前構(gòu)造的線程。 ScheduledExecutorService newSingleThreadScheduledExecutor() 創(chuàng)建一個(gè)單線程執(zhí)行器,可以調(diào)度命令在給定的延遲之后運(yùn)行,或定期執(zhí)行。 ScheduledExecutorService newScheduledThreadPool(int corePoolSize) 創(chuàng)建一個(gè)線程池,可以調(diào)度命令在給定的延遲之后運(yùn)行,或定期執(zhí)行。 ThreadFactory privilegedThreadFactory() 返回一個(gè)用于創(chuàng)建與當(dāng)前線程具有相同權(quán)限的新線程的線程工廠。 補(bǔ)充:
ExecutorService.shutdown():防止新任務(wù)被提交,并繼續(xù)運(yùn)行被調(diào)用之前所提交的所有任務(wù),待任務(wù)都完成后退出。
CachedThreadPoo在程序執(zhí)行過(guò)程中通常會(huì)創(chuàng)建與所需數(shù)量相同的線程,然后在它回收舊線程時(shí)停止創(chuàng)建新線程,是Executor的首選。僅當(dāng)這個(gè)出現(xiàn)問(wèn)題時(shí),才需切換 FixedThreadPool。
SingleThreadExecutor: 類似于線程數(shù)量為 1 的FixedThreadPool,但它提供了不會(huì)存在兩個(gè)及以上的線程被并發(fā)調(diào)用的并發(fā)。
Example:線程池
public class TestThreadPool { public static void main(String[] args) throws Exception { ExecutorService pool = Executors.newFixedThreadPool(2); for (int i = 0; i < 10; ++i) { Futurefuture = pool.submit(new Callable () { @Override public String call() throws Exception { return Thread.currentThread().getName(); } }); String threadName = future.get(); System.out.println(threadName); } pool.shutdown(); // 拒絕新任務(wù)并等待正在執(zhí)行的線程完成當(dāng)前任務(wù)后關(guān)閉。 } }/**output: pool-1-thread-1 pool-1-thread-2 pool-1-thread-1 pool-1-thread-2 ... */ Example:線程調(diào)度
public class TestThreadPool { public static void main(String[] args) throws Exception { ScheduledExecutorService pool = Executors.newScheduledThreadPool(2); for (int i = 0; i < 5; ++i) { ScheduledFuturefuture = pool.schedule(new Callable () { @Override public String call() throws Exception { return Thread.currentThread().getName() + " : " + Instant.now(); } }, 1, TimeUnit.SECONDS); // 延遲執(zhí)行單位為 1秒的任務(wù) String result = future.get(); System.out.println(result); } pool.shutdown(); } }/**output: pool-1-thread-1 : 2019-03-18T12:10:31.260Z pool-1-thread-1 : 2019-03-18T12:10:32.381Z pool-1-thread-2 : 2019-03-18T12:10:33.382Z pool-1-thread-1 : 2019-03-18T12:10:34.383Z pool-1-thread-2 : 2019-03-18T12:10:35.387Z */ 注意:若沒有執(zhí)行 shutdown()方法,則線程會(huì)一直等待而不停止。
ForkJoinPool分支/合并框架源由:
在一個(gè)線程隊(duì)列中,假如隊(duì)頭的線程由于某種原因?qū)е铝俗枞?,那么在該?duì)列中的后繼線程需要等待隊(duì)頭線程結(jié)束,只要隊(duì)頭一直阻塞,這個(gè)隊(duì)列中的所有線程都將等待。此時(shí),可能其他線程隊(duì)列都已經(jīng)完成了任務(wù)而空閑,這種情況下,就大大減少了吞吐量。
ForkJoin的“工作竊取”模式:
當(dāng)執(zhí)行一個(gè)新任務(wù)時(shí),采用分而治之的思想,將其分解成更小的任務(wù)執(zhí)行,并將分解的任務(wù)加入到線程隊(duì)列中,當(dāng)某一個(gè)線程隊(duì)列沒有任務(wù)時(shí),會(huì)隨機(jī)從其他線程隊(duì)列中“偷取”一個(gè)任務(wù),放入自己的隊(duì)列中執(zhí)行。
Example:
// 求次方: value為底,size為次方數(shù) class CountPower extends RecursiveTask{ private static final long serialVersionUID = 1L; public Long value = 0L; public int size = 0; public static final Long CRITICAL = 10L; // 閾值 public CountPower(Long value, int size) { this.value = value; this.size = size; } @Override protected Long compute() { // 當(dāng)要開方的此時(shí) 小于 閾值,則計(jì)算 (視為最小的任務(wù)單元) if(size <= CRITICAL) { Long sum = 1L; for (int i=0; i 根據(jù)分而治之的思想進(jìn)行分解,需要一個(gè)結(jié)束遞歸的條件,該條件內(nèi)的代碼就是被分解的最小單元。使用fork()在當(dāng)前任務(wù)正在運(yùn)行的池中異步執(zhí)行此任務(wù),即將該任務(wù)壓入線程隊(duì)列。調(diào)用join()`返回計(jì)算結(jié)果。RecursiveTask是有返回值的task,RecursiveAction則是沒有返回值的。
參考尚硅谷JUC視頻教程
《java編程思想》第 21 章 并發(fā)
文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/73784.html
摘要:發(fā)布的對(duì)象內(nèi)部狀態(tài)可能會(huì)破壞封裝性,使程序難以維持不變性條件。不變性線程安全性是不可變對(duì)象的固有屬性之一??勺儗?duì)象必須通過(guò)安全方式來(lái)發(fā)布,并且必須是線程安全的或者有某個(gè)鎖保護(hù)起來(lái)。 線程的優(yōu)缺點(diǎn) 線程是系統(tǒng)調(diào)度的基本單位。線程如果使用得當(dāng),可以有效地降低程序的開發(fā)和維護(hù)等成本,同時(shí)提升復(fù)雜應(yīng)用程序的性能。多線程程序可以通過(guò)提高處理器資源的利用率來(lái)提升系統(tǒng)的吞吐率。與此同時(shí),在線程的使用...
摘要:于是抽時(shí)間看了看以后各個(gè)版本的特性,做了一個(gè)總結(jié)。年和公開版本發(fā)布,取名為。此后對(duì)應(yīng)版本就是,。發(fā)布,是一個(gè)重大版本更新。在此之后,就是每六個(gè)月發(fā)布一次新版本。以上和參考資料聊了一些關(guān)于的歷史,下面我們看看各個(gè)版本有那些新特性。 【這是 ZY 第 11 篇原創(chuàng)技術(shù)文章】 某天在網(wǎng)上閑逛,突然看到有篇介紹 Java 11 新特性的文章,頓時(shí)心里一驚,畢竟我對(duì)于 Java 的版本認(rèn)識(shí)...
摘要:中很多特性或者說(shuō)知識(shí)點(diǎn)都是和面向?qū)ο缶幊谈拍钕嚓P(guān)的。在多線程中內(nèi)容有很多,只是簡(jiǎn)單說(shuō)明一下中初步使用多線程需要掌握的知識(shí)點(diǎn),以后有機(jī)會(huì)單獨(dú)再詳細(xì)介紹一些高級(jí)特性的使用場(chǎng)景。 寫這篇文章的目的是想總結(jié)一下自己這么多年來(lái)使用java的一些心得體會(huì),主要是和一些java基礎(chǔ)知識(shí)點(diǎn)相關(guān)的,所以也希望能分享給剛剛?cè)腴T的Java程序員和打算入Java開發(fā)這個(gè)行當(dāng)?shù)臏?zhǔn)新手們,希望可以給大家一些經(jīng)...
摘要:標(biāo)記,表示記錄當(dāng)前的位置。直接緩沖通過(guò)方法分配的緩沖區(qū),此緩沖區(qū)建立在物理內(nèi)存中。直接在兩個(gè)空間中開辟內(nèi)存空間,創(chuàng)建映射文件,去除了在內(nèi)核地址空間和用戶地址空間中的操作,使得直接通過(guò)物理內(nèi)存?zhèn)鬏敂?shù)據(jù)。 NIO與IO的區(qū)別 IO NIO 阻塞式 非阻塞式、選擇器selectors 面向流:?jiǎn)蜗蛄鲃?dòng),直接將數(shù)據(jù)從一方流向另一方 面向緩存:將數(shù)據(jù)放到緩存區(qū)中進(jìn)行存取,經(jīng)通道進(jìn)行...
摘要:外部存儲(chǔ)器可用于長(zhǎng)期保存大量程序和數(shù)據(jù),其成本低容量大,但速度較慢。 1_計(jì)算機(jī)概述(了解) A:什么是計(jì)算機(jī)?計(jì)算機(jī)在生活中的應(yīng)用舉例 計(jì)算機(jī)(Computer)全稱:電子計(jì)算機(jī),俗稱電腦。是一種能夠按照程序運(yùn)行,自動(dòng)、高速處理海量數(shù)據(jù)的現(xiàn)代化智能電子設(shè)備。由硬件和軟件所組成,沒有安裝任何軟件的計(jì)算機(jī)稱為裸機(jī)。常見的形式有臺(tái)式計(jì)算機(jī)、筆記本計(jì)算機(jī)、大型計(jì)算機(jī)等。 應(yīng)用舉例 ...
閱讀 1218·2021-11-17 09:33
閱讀 3629·2021-09-28 09:42
閱讀 3357·2021-09-13 10:35
閱讀 2517·2021-09-06 15:00
閱讀 2457·2021-08-27 13:12
閱讀 3621·2021-07-26 23:38
閱讀 1873·2019-08-30 15:55
閱讀 553·2019-08-30 15:53