本文快速回顧了常考的的知識點,用做面試複習,事半功倍。java
全複習手冊文章導航git
點擊公衆號下方技術推文——面試衝刺程序員
已發佈知識點複習手冊github
Java基礎知識點面試手冊(上)面試
Java基礎知識點面試手冊(下)算法
Java容器(List、Set、Map)知識點快速複習手冊(上)數據庫
Java容器(List、Set、Map)知識點快速複習手冊(中)segmentfault
Java容器(List、Set、Map)知識點快速複習手冊(下)數組
Redis基礎知識點快速複習手冊(上)安全
Redis基礎知識點快速複習手冊(下)
雙非碩士的春招秋招經驗總結——對校招,複習以及面試心態的理解
本文內容參考自CyC2018的Github倉庫:CS-Notes
https://github.com/CyC2018/CS-Notes/
有刪減,修改,補充額外增長內容
本做品採用知識共享署名-非商業性使用 4.0 國際許可協議進行許可。
建立後還沒有啓動。
可能正在運行,也可能正在等待 CPU 時間片。
包含了操做系統線程狀態中的 Running 和 Ready。
等待獲取一個排它鎖,若是其線程釋放了鎖就會結束此狀態。
等待其它線程顯式地喚醒,不然不會被分配 CPU 時間片。
無需等待其它線程顯式地喚醒,在必定時間以後會被系統自動喚醒。
調用 Thread.sleep() 方法使線程進入限期等待狀態時,經常用「使一個線程睡眠」進行描述。
調用 Object.wait() 方法使線程進入限期等待或者無限期等待時,經常用「掛起一個線程」進行描述。
睡眠和掛起是用來描述行爲,而阻塞和等待用來描述狀態。
阻塞和等待的區別在於,阻塞是被動的,它是在等待獲取一個排它鎖;
而等待是主動的,經過調用 Thread.sleep() 和 Object.wait() 等方法進入。
能夠是線程結束任務以後本身結束,或者產生了異常而結束。
有三種使用線程的方法:
實現 Runnable 和 Callable 接口的類只能當作一個能夠在線程中運行的任務,不是真正意義上的線程,所以最後還須要經過 Thread 來調用。能夠說任務是經過線程驅動從而執行的。
須要實現 run() 方法。
經過 Thread 調用 start() 方法來啓動線程。
1public class MyRunnable implements Runnable { 2 public void run() { 3 // ... 4 } 5} 1public static void main(String[] args) { 2 MyRunnable instance = new MyRunnable(); 3 Thread thread = new Thread(instance); 4 thread.start(); 5}
Callable就是Runnable的擴展。
與 Runnable 相比,Callable 能夠有返回值,返回值經過 FutureTask 進行封裝。
1public class MyCallable implements Callable<Integer> { 2 public Integer call() { 3 return 123; 4 } 5} 1public static void main(String[] args) throws ExecutionException, InterruptedException { 2 MyCallable mc = new MyCallable(); 3 FutureTask<Integer> ft = new FutureTask<>(mc); 4 Thread thread = new Thread(ft); 5 thread.start(); 6 System.out.println(ft.get()); 7}
一樣也是須要實現 run() 方法,而且最後也是調用 start() 方法來啓動線程。
1public class MyThread extends Thread { 2 public void run() { 3 // ... 4 } 5} 1public static void main(String[] args) { 2 MyThread mt = new MyThread(); 3 mt.start(); 4}
嚴格說不能算方法,只能算實現方式:
實現接口會更好一些,由於:
詳細解釋:https://blog.csdn.net/lai_li/article/details/53070141?locationNum=13&fps=1
start方法:
經過該方法啓動線程的同時也建立了一個線程,真正實現了多線程。無需等待run()方法中的代碼執行完畢,就能夠接着執行下面的代碼。
run方法:
1package cn.thread.test; 2 3/* 4 * 設計4個線程,其中兩個線程每次對j增長1,另外兩個線程對j每次減小1。寫出程序。 5 */ 6public class ThreadTest1 { 7 8 private int j; 9 10 public static void main(String[] args) { 11 ThreadTest1 tt = new ThreadTest1(); 12 Inc inc = tt.new Inc(); 13 Dec dec = tt.new Dec(); 14 15 16 Thread t1 = new Thread(inc); 17 Thread t2 = new Thread(dec); 18 Thread t3 = new Thread(inc); 19 Thread t4 = new Thread(dec); 20 t1.start(); 21 t2.start(); 22 t3.start(); 23 t4.start(); 24 25 } 26 27 private synchronized void inc() { 28 j++; 29 System.out.println(Thread.currentThread().getName()+"inc:"+j); 30 } 31 32 private synchronized void dec() { 33 j--; 34 System.out.println(Thread.currentThread().getName()+"dec:"+j); 35 } 36 37 class Inc implements Runnable { 38 @Override 39 public void run() { 40 for (int i = 0; i < 100; i++) { 41 inc(); 42 } 43 } 44 } 45 46 class Dec extends Thread { 47 @Override 48 public void run() { 49 for (int i = 0; i < 100; i++) { 50 dec(); 51 } 52 } 53 } 54}
http://www.javashuo.com/article/p-gmlygbhe-mw.html
Executor 管理多個異步任務的執行,而無需程序員顯式地管理線程的生命週期。異步是指多個任務的執行互不干擾,不須要進行同步操做。
當前線程池大小 :表示線程池中實際工做者線程的數量;
最大線程池大小 (maxinumPoolSize):表示線程池中容許存在的工做者線程的數量上限;
若是運行的線程少於 corePoolSize,則 Executor 始終首選添加新的線程,而不進行排隊;
若是運行的線程等於或者多於 corePoolSize,則 Executor 始終首選將請求加入隊列,而不是添加新線程;
若是沒法將請求加入隊列,即隊列已經滿了,則建立新的線程,除非建立此線程超出 maxinumPoolSize, 在這種狀況下,任務將被拒絕。
實現了Executor接口,是用的最多的線程池,下面是已經默認實現的三種:
很是有彈性的線程池,對於新的任務,若是此時線程池裏沒有空閒線程,線程池會絕不猶豫的建立一條新的線程去處理這個任務。
1public static void main(String[] args) { 2 ExecutorService executorService = Executors.newCachedThreadPool(); 3 for (int i = 0; i < 5; i++) { 4 executorService.execute(new MyRunnable()); 5 } 6 executorService.shutdown(); 7}
一個固定線程數的線程池,它將返回一個corePoolSize和maximumPoolSize相等的線程池。
至關於提供了延遲和週期執行功能的ThreadPoolExecutor類
守護線程是程序運行時在後臺提供服務的線程,不屬於程序中不可或缺的部分。
當全部非守護線程結束時,程序也就終止,同時會殺死全部守護線程。
main() 屬於非守護線程,垃圾回收是守護線程。
使用 setDaemon() 方法將一個線程設置爲守護線程。
1public static void main(String[] args) { 2 Thread thread = new Thread(new MyRunnable()); 3 thread.setDaemon(true); 4}
Thread.sleep(millisec) 方法會休眠當前正在執行的線程,millisec 單位爲毫秒。
sleep() 可能會拋出 InterruptedException,由於異常不能跨線程傳播回 main() 中,所以必須在本地進行處理。線程中拋出的其它異常也一樣須要在本地進行處理。
1public void run() { 2 try { 3 Thread.sleep(3000); 4 } catch (InterruptedException e) { 5 e.printStackTrace(); 6 } 7}
對靜態方法 Thread.yield() 的調用聲明瞭當前線程已經完成了生命週期中最重要的部分,能夠切換給其它線程來執行。該方法只是對線程調度器的一個建議,並且也只是建議具備相同優先級的其它線程能夠運行。
1public void run() { 2 Thread.yield(); 3}
一個線程執行完畢以後會自動結束,若是在運行過程當中發生異常也會提早結束。
如今已經沒有強制線程終止的方法了!。
Stop方法太暴力了,不安全,因此被設置過期了。
http://www.javashuo.com/article/p-hwobvksy-nc.html
要注意的是:interrupt不會真正中止一個線程,它僅僅是給這個線程發了一個信號告訴它,它應該要結束了(明白這一點很是重要!)
調用interrupt()並非要真正終止掉當前線程,僅僅是設置了一箇中斷標誌。這個中斷標誌能夠給咱們用來判斷何時該幹什麼活!何時中斷由咱們本身來決定,這樣就能夠安全地終止線程了!
經過調用一個線程的 interrupt() 來中斷該線程,能夠中斷處於:
那麼就會拋出 InterruptedException,從而提早結束該線程。
可是不能中斷 I/O 阻塞和 synchronized 鎖阻塞。
對於如下代碼,在 main() 中啓動一個線程以後再中斷它,因爲線程中調用了 Thread.sleep() 方法,所以會拋出一個 InterruptedException,從而提早結束線程,不執行以後的語句。
1public class InterruptExample { 2 3 private static class MyThread1 extends Thread { 4 @Override 5 public void run() { 6 try { 7 Thread.sleep(2000); 8 System.out.println("Thread run"); 9 } catch (InterruptedException e) { 10 e.printStackTrace(); 11 } 12 } 13 } 14} 1public static void main(String[] args) throws InterruptedException { 2 Thread thread1 = new MyThread1(); 3 thread1.start(); 4 thread1.interrupt(); 5 System.out.println("Main run"); 6} 1Main run 2java.lang.InterruptedException: sleep interrupted 3 at java.lang.Thread.sleep(Native Method) 4 at InterruptExample.lambda$main$0(InterruptExample.java:5) 5 at InterruptExample$$Lambda$1/713338599.run(Unknown Source) 6 at java.lang.Thread.run(Thread.java:745)
interrupt線程中斷還有另外兩個方法(檢查該線程是否被中斷):
若是一個線程的 run() 方法執行一個無限循環(不屬於阻塞、限期等待、非限期等待),例如while(True),而且沒有執行 sleep() 等會拋出 InterruptedException 的操做,那麼調用線程的 interrupt() 方法就沒法使線程提早結束。
然而,
可是調用 interrupt() 方法會設置線程的中斷標記,此時調用 interrupted() 方法會返回 true。所以能夠在循環體中使用 interrupted() 方法來判斷線程是否處於中斷狀態,從而提早結束線程。
1Thread t1 = new Thread( new Runnable(){ 2 public void run(){ 3 // 若未發生中斷,就正常執行任務 4 while(!Thread.currentThread.isInterrupted()){ 5 // 正常任務代碼…… 6 } 7 // 中斷的處理代碼…… 8 doSomething(); 9 } 10} ).start();
如下使用 Lambda 建立線程,至關於建立了一個匿名內部線程。
1public static void main(String[] args) { 2 ExecutorService executorService = Executors.newCachedThreadPool(); 3 executorService.execute(() -> { 4 try { 5 Thread.sleep(2000); 6 System.out.println("Thread run"); 7 } catch (InterruptedException e) { 8 e.printStackTrace(); 9 } 10 }); 11 executorService.shutdownNow(); 12 System.out.println("Main run"); 13} 1Main run 2java.lang.InterruptedException: sleep interrupted 3 at java.lang.Thread.sleep(Native Method) 4 at ExecutorInterruptExample.lambda$main$0(ExecutorInterruptExample.java:9) 5 at ExecutorInterruptExample$$Lambda$1/1160460865.run(Unknown Source) 6 at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142) 7 at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617) 8 at java.lang.Thread.run(Thread.java:745)
若是隻想中斷 Executor 中的一個線程,能夠經過使用 submit() 方法來提交一個線程,它會返回一個 Future 對象,經過調用該對象的 cancel(true) 方法就能夠中斷線程。
1Future<?> future = executorService.submit(() -> { 2 // .. 3}); 4future.cancel(true);
https://blog.csdn.net/u012545728/article/details/80843595
所謂不可重入鎖,即若當前線程執行某個方法已經獲取了該鎖,那麼在方法中嘗試再次獲取鎖時,就會獲取不到被阻塞。
1public class Count{ 2 Lock lock = new Lock(); 3 public void print(){ 4 lock.lock(); 5 doAdd(); 6 lock.unlock(); 7 } 8 public void doAdd(){ 9 lock.lock(); 10 //do something 11 lock.unlock(); 12 } 13}
所謂可重入,意味着線程能夠進入它已經擁有的鎖的同步代碼塊兒
咱們設計兩個線程調用print()方法,第一個線程調用print()方法獲取鎖,進入lock()方法,因爲初始lockedBy是null,因此不會進入while而掛起當前線程,而是是增量lockedCount並記錄lockBy爲第一個線程。接着第一個線程進入doAdd()方法,因爲同一進程,因此不會進入while而掛起,接着增量lockedCount,當第二個線程嘗試lock,因爲isLocked=true,因此他不會獲取該鎖,直到第一個線程調用兩次unlock()將lockCount遞減爲0,纔將標記爲isLocked設置爲false。
可重入鎖的概念和設計思想大致如此,Java中的可重入鎖ReentrantLock設計思路也是這樣
synchronized和ReentrantLock都是可重入鎖
1public void func () { 2 synchronized (this) { 3 // ... 4 } 5}
它只做用於同一個對象,若是調用兩個不一樣對象上的同步代碼塊,就不會進行同步。
1public synchronized void func () { 2 // ... 3}
它和同步代碼塊同樣,只做用於同一個對象。
1public void func() { 2 synchronized (SynchronizedExample.class) { 3 // ... 4 } 5}
做用於整個類,也就是說兩個線程調用同一個類的不一樣對象上的這種同步語句,也須要進行同步。
1public synchronized static void fun() { 2 // ... 3}
做用於整個類。
當方法(代碼塊)執行完畢後會自動釋放鎖,不須要作任何的操做。
有ReentrantLock和ReentrantReadWriteLock,後者分爲讀鎖和寫鎖,讀鎖容許併發訪問共享資源。
1public class LockExample { 2 3 private Lock lock = new ReentrantLock(); 4 5 public void func() { 6 lock.lock(); 7 try { 8 for (int i = 0; i < 10; i++) { 9 System.out.print(i + " "); 10 } 11 } finally { 12 lock.unlock(); // 確保釋放鎖,從而避免發生死鎖。 13 } 14 } 15} 1public static void main(String[] args) { 2 LockExample lockExample = new LockExample(); 3 ExecutorService executorService = Executors.newCachedThreadPool(); 4 executorService.execute(() -> lockExample.func()); 5 executorService.execute(() -> lockExample.func()); 6} 10 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9
ReentrantLock 是 java.util.concurrent(J.U.C)包中的鎖,相比於 synchronized,它多瞭如下高級功能:
當持有鎖的線程長期不釋放鎖的時候,正在等待的線程能夠選擇放棄等待,改成處理其餘事情,可中斷特性對處理執行時間很是長的同步塊頗有幫助。
公平鎖是指多個線程在等待同一個鎖時,必須按照申請鎖的時間順序來依次得到鎖;而非公平鎖則不保證這一點,在鎖被釋放時,任何一個等待鎖的線程都有機會得到鎖。
ReentrantLock 默認狀況下也是非公平的,但能夠經過帶布爾值的構造函數要求使用公平鎖。
咱們知道synchronized內置鎖和ReentrantLock都是互斥鎖(一次只能有一個線程進入到臨界區(被鎖定的區域)
ReentrantReadWriteLock優勢:
synchronized 是 JVM 實現的,而 ReentrantLock 是 JDK 實現的。
新版本 Java 對 synchronized 進行了不少優化,例如自旋鎖等,synchronized 與 ReentrantLock 大體相同。
ReentrantLock 可中斷,而 synchronized 不行。
一個 ReentrantLock 能夠同時綁定多個 Condition 對象。
除非須要使用 ReentrantLock 的高級功能,不然優先使用 synchronized。
當多個線程能夠一塊兒工做去解決某個問題時,若是某些部分必須在其它部分以前完成,那麼就須要對線程進行協調。
在線程中調用另外一個線程的 join() 方法,會將當前線程掛起,而不是忙等待, 直到目標線程結束。
對於如下代碼,雖然 b 線程先啓動,可是由於在 b 線程中調用了 a 線程的 join() 方法,所以 b 線程會等待 a 線程結束才繼續執行,所以最後可以保證 a 線程的輸出先與 b 線程的輸出。
調用 wait() 使得線程等待某個條件知足,線程在等待時會被掛起,當其餘線程的運行使得這個條件知足時,其它線程會調用 notify() 或者 notifyAll() 來喚醒掛起的線程。
它們都屬於 Object 的一部分,而不屬於 Thread。
只能用在同步方法或者同步控制塊中使用,不然會在運行時拋出 IllegalMonitorStateExeception。
使用 wait() 掛起期間,線程會釋放鎖。這是由於,若是沒有釋放鎖,那麼其它線程就沒法進入對象的同步方法或者同步控制塊中,那麼就沒法執行 notify() 或者 notifyAll() 來喚醒掛起的線程,形成死鎖。
wait() 和 sleep() 的區別
java.util.concurrent 類庫中提供了 Condition 類來實現線程之間的協調,能夠在 Condition 上調用 await() 方法使線程等待,其它線程調用 signal() 或 signalAll() 方法喚醒等待的線程。
相比於 wait() 這種等待方式,await() 能夠指定等待的條件,所以更加靈活。
使用 Lock 來獲取一個 Condition 對象。
1public class AwaitSignalExample { 2 private Lock lock = new ReentrantLock(); 3 private Condition condition = lock.newCondition(); 4 5 public void before() { 6 lock.lock(); 7 try { 8 System.out.println("before"); 9 condition.signalAll(); 10 } finally { 11 lock.unlock(); 12 } 13 } 14 15 public void after() { 16 lock.lock(); 17 try { 18 condition.await(); 19 System.out.println("after"); 20 } catch (InterruptedException e) { 21 e.printStackTrace(); 22 } finally { 23 lock.unlock(); 24 } 25 } 26} 1public static void main(String[] args) { 2 ExecutorService executorService = Executors.newCachedThreadPool(); 3 AwaitSignalExample example = new AwaitSignalExample(); 4 executorService.execute(() -> example.after()); 5 executorService.execute(() -> example.before()); 6} 1before 2after
從總體來看,concurrent包的實現示意圖以下:
在這裏插入圖片描述
http://www.javashuo.com/article/p-gvvwqcdj-nc.html
java.util.concurrent(J.U.C)大大提升了併發性能,AQS 被認爲是 J.U.C 的核心。
AbstractQueuedSynchronizer簡稱爲AQS:AQS定義了一套多線程訪問共享資源的同步器框架,許多同步類實現都依賴於它,咱們Lock之類的兩個常見的鎖都是基於它來實現的。
維護了一個計數器 cnt,每次調用 countDown() 方法會讓計數器的值減 1,減到 0 的時候,那些由於調用 await() 方法而在等待的線程就會被喚醒。
使用說明:
1public class CountdownLatchExample { 2 public static void main(String[] args) throws InterruptedException { 3 final int totalThread = 10; 4 CountDownLatch countDownLatch = new CountDownLatch(totalThread); 5 ExecutorService executorService = Executors.newCachedThreadPool(); 6 for (int i = 0; i < totalThread; i++) { 7 executorService.execute(() -> { 8 System.out.print("run.."); 9 countDownLatch.countDown(); 10 }); 11 } 12 countDownLatch.await(); 13 System.out.println("end"); 14 executorService.shutdown(); 15 } 16} 17 18run..run..run..run..run..run..run..run..run..run..end
CyclicBarrier 的字面意思是可循環使用(Cyclic)的屏障(Barrier)。它要作的事情是,讓一組線程到達一個屏障(也能夠叫同步點)時被阻塞,直到最後一個線程到達屏障時,屏障纔會開門,全部被屏障攔截的線程纔會繼續幹活。
和 CountdownLatch類似,都是經過維護計數器來實現的。可是它的計數器是遞增的,每次執行 await() 方法以後,計數器會加 1,直到計數器的值和設置的值相等,等待的全部線程纔會繼續執行。
CyclicBarrier能夠被重用(對比於CountDownLatch是不能重用的),CyclicBarrier 的計數器經過調用 reset() 方法能夠循環使用,因此它才叫作循環屏障。
在這裏插入圖片描述
1public CyclicBarrier(int parties, Runnable barrierAction) { 2 if (parties <= 0) throw new IllegalArgumentException(); 3 this.parties = parties; 4 this.count = parties; 5 this.barrierCommand = barrierAction; 6} 7 8public CyclicBarrier(int parties) { 9 this(parties, null); 10} 1public class CyclicBarrierExample { 2 public static void main(String[] args) { 3 final int totalThread = 10; 4 CyclicBarrier cyclicBarrier = new CyclicBarrier(totalThread); 5 ExecutorService executorService = Executors.newCachedThreadPool(); 6 for (int i = 0; i < totalThread; i++) { 7 executorService.execute(() -> { 8 System.out.print("before.."); 9 try { 10 cyclicBarrier.await(); 11 } catch (InterruptedException | BrokenBarrierException e) { 12 e.printStackTrace(); 13 } 14 System.out.print("after.."); 15 }); 16 } 17 executorService.shutdown(); 18 } 19} 20before..before..before..before..before..before..before..before..before..before..after..after..after..after..after..after..after..after..after..after..
Semaphore 就是操做系統中的信號量,能夠控制對互斥資源的訪問線程數。
1public class SemaphoreExample { 2 public static void main(String[] args) { 3 final int clientCount = 3; 4 final int totalRequestCount = 10; 5 Semaphore semaphore = new Semaphore(clientCount); 6 ExecutorService executorService = Executors.newCachedThreadPool(); 7 for (int i = 0; i < totalRequestCount; i++) { 8 executorService.execute(()->{ 9 try { 10 semaphore.acquire(); 11 System.out.print(semaphore.availablePermits() + " "); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } finally { 15 semaphore.release(); 16 } 17 }); 18 } 19 executorService.shutdown(); 20 } 21}
在介紹 Callable 時咱們知道它能夠有返回值,返回值經過 Future進行封裝。
FutureTask 實現了 RunnableFuture 接口,該接口繼承自 Runnable 和 Future接口,這使得 FutureTask 既能夠當作一個任務執行,也能夠有返回值。
1public class FutureTask<V> implements RunnableFuture<V> 2 1public interface RunnableFuture<V> extends Runnable, Future<V> 2
當一個計算任務須要執行很長時間,那麼就能夠用 FutureTask 來封裝這個任務,用一個線程去執行該任務,而後其它線程繼續執行其它任務。當須要該任務的計算結果時,再經過 FutureTask 的 get() 方法獲取。
1public class FutureTaskExample { 2 public static void main(String[] args) throws ExecutionException, InterruptedException { 3 FutureTask<Integer> futureTask = new FutureTask<Integer>(new Callable<Integer>() { 4 @Override 5 public Integer call() throws Exception { 6 int result = 0; 7 for (int i = 0; i < 100; i++) { 8 Thread.sleep(10); 9 result += i; 10 } 11 return result; 12 } 13 }); 14 15 Thread computeThread = new Thread(futureTask); 16 computeThread.start(); 17 18 Thread otherThread = new Thread(() -> { 19 System.out.println("other task is running..."); 20 try { 21 Thread.sleep(1000); 22 } catch (InterruptedException e) { 23 e.printStackTrace(); 24 } 25 }); 26 otherThread.start(); 27 System.out.println(futureTask.get()); 28 } 29}
1other task is running... 24950
java.util.concurrent.BlockingQueue 接口有如下阻塞隊列的實現:
使用 BlockingQueue 實現生產者消費者問題
1public class ProducerConsumer { 2 3 private static BlockingQueue<String> queue = new ArrayBlockingQueue<>(5); 4 5 private static class Producer extends Thread { 6 @Override 7 public void run() { 8 try { 9 queue.put("product"); 10 } catch (InterruptedException e) { 11 e.printStackTrace(); 12 } 13 System.out.print("produce.."); 14 } 15 } 16 17 private static class Consumer extends Thread { 18 19 @Override 20 public void run() { 21 try { 22 String product = queue.take(); 23 } catch (InterruptedException e) { 24 e.printStackTrace(); 25 } 26 System.out.print("consume.."); 27 } 28 } 29} 1public static void main(String[] args) { 2 for (int i = 0; i < 2; i++) { 3 Producer producer = new Producer(); 4 producer.start(); 5 } 6 for (int i = 0; i < 5; i++) { 7 Consumer consumer = new Consumer(); 8 consumer.start(); 9 } 10 for (int i = 0; i < 3; i++) { 11 Producer producer = new Producer(); 12 producer.start(); 13 } 14} 1produce..produce..consume..consume..produce..consume..produce..consume..produce..consume..
都是線程安全的,否則叫什麼併發類呢
ArrayBlockingQueue, LinkedBlockingQueue 繼承自 BlockingQueue, 他們的特色就是 Blocking, Blocking 特有的方法就是 take() 和 put(), 這兩個方法是阻塞方法, 每當隊列容量滿的時候, put() 方法就會進入wait, 直到隊列空出來, 而每當隊列爲空時, take() 就會進入等待, 直到隊列有元素能夠 take()
ArrayBlockingQueue, LinkedBlockingQueue 區別在於:
鏈表和數組性質決定的
ConcurrentLinkedQueue 經過 CAS 操做實現了無鎖的 poll() 和 offer(),
他的容量是動態的,
因爲無鎖, 因此在 poll() 或者 offer() 的時候 head 與 tail 可能會改變,因此它會持續的判斷 head 與 tail 是否改變來保證操做正確性, 若是改變, 則會從新選擇 head 與 tail.
除了ScheduledThreadPoolExecutor和ThreadPoolExecutor類線程池之外,還有一個是JDK1.7新增的線程池:ForkJoinPool線程池
主要用於並行計算中,和 MapReduce 原理相似,都是把大的計算任務拆分紅多個小任務並行計算。
1public class ForkJoinExample extends RecursiveTask<Integer> { 2 private final int threhold = 5; 3 private int first; 4 private int last; 5 6 public ForkJoinExample(int first, int last) { 7 this.first = first; 8 this.last = last; 9 } 10 11 @Override 12 protected Integer compute() { 13 int result = 0; 14 if (last - first <= threhold) { 15 // 任務足夠小則直接計算 16 for (int i = first; i <= last; i++) { 17 result += i; 18 } 19 } else { 20 // 拆分紅小任務 21 int middle = first + (last - first) / 2; 22 ForkJoinExample leftTask = new ForkJoinExample(first, middle); 23 ForkJoinExample rightTask = new ForkJoinExample(middle + 1, last); 24 leftTask.fork(); 25 rightTask.fork(); 26 result = leftTask.join() + rightTask.join(); 27 } 28 return result; 29 } 30} 1public static void main(String[] args) throws ExecutionException, InterruptedException { 2 ForkJoinExample example = new ForkJoinExample(1, 10000); 3 ForkJoinPool forkJoinPool = new ForkJoinPool(); 4 Future result = forkJoinPool.submit(example); 5 System.out.println(result.get()); 6}
ForkJoin 使用 ForkJoinPool 來啓動,它是一個特殊的線程池,線程數量取決於 CPU 核數。
1public class ForkJoinPool extends AbstractExecutorService 2
ForkJoinPool 實現了工做竊取算法來提升 CPU 的利用率。每一個線程都維護了一個雙端隊列,用來存儲須要執行的任務。工做竊取算法容許空閒的線程從其它線程的雙端隊列中竊取一個任務來執行。竊取的任務必須是最晚的任務,避免和隊列所屬線程發生競爭。例以下圖中,Thread2 從 Thread1 的隊列中拿出最晚的 Task1 任務,Thread1 會拿出 Task2 來執行,這樣就避免發生競爭。可是若是隊列中只有一個任務時仍是會發生競爭。
在這裏插入圖片描述
我是蠻三刀把刀,目前爲後臺開發工程師。主要關注後臺開發,網絡安全,Python爬蟲等技術。
來微信和我聊聊:yangzd1102
Github:https://github.com/qqxx6661
擁有專欄:Leetcode題解(Java/Python)、Python爬蟲開發
https://www.zhihu.com/people/yang-zhen-dong-1/
擁有專欄:碼農面試助攻手冊
https://juejin.im/user/5b48015ce51d45191462ba55
https://www.jianshu.com/u/b5f225ca2376
我的公衆號:Rude3Knife
若是文章對你有幫助,不妨收藏起來並轉發給您的朋友們~