今天爲你們帶來了關於17道線程、多線程和線程池面試知識,對於這些東西我還專門整理了 一份983頁的完整PDF核心筆記,須要的小夥伴能夠聯繫我獲取~ 我不休息我還能學 ⊂(‘ω’⊂ )))Σ≡=─༄༅༄༅༄༅༄༅༄༅
核心學習筆記的獲取方式:VX:mm14525201314
java
1)繼承Thread類,重寫run()方法,在run()方法體中編寫要完成的任務 new Thread().start();面試
2)實現Runnable接口,實現run()方法 new Thread(new MyRunnable()).start();編程
3)實現Callable接口MyCallable類,實現call()方法,使用FutureTask類來包裝Callable對象,使用FutureTask對象做爲Thread對象的target建立並啓動線程;調用FutureTask對象的get()方法來得到子線程執行結束後的返回值。緩存
FutureTask<Integer> ft = new FutureTask<Integer>(new MyCallable()); new Thread(ft).start();
run()方法只是線程的主體方法,和普通方法同樣,不會建立新的線程。只有調用start()方法,纔會啓動一個新的線程,新線程纔會調用run()方法,線程纔會開始執行。安全
建立Semaphore變量,Semaphore semaphore = new Semaphore(5, true); 當方法進入時,請求一個信號,若是信號被用完則等待,方法運行完,釋放一個信號,釋放的信號新的線程就可使用。多線程
wait()方法屬於Object類,調用該方法時,線程會放棄對象鎖,只有該對象調用notify()方法後本線程才進入對象鎖定池準備獲取對象鎖進入運行狀態。併發
sleep()方法屬於Thread類,sleep()致使程序暫停執行指定的時間,讓出CPU,但它的監控狀態依然保存着,當指定時間到了又會回到運行狀態,sleep()方法中線程不會釋放對象鎖。函數
notify: 喚醒在此對象監視器上等待的單個線程性能
notifyAll(): 通知全部等待該競爭資源的線程學習
wait: 釋放obj的鎖,致使當前的線程等待,直接其餘線程調用此對象的notify()或notifyAll()方法
當要調用wait()或notify()/notifyAll()方法時,必定要對競爭資源進行加鎖,通常放到synchronized(obj)代碼中。
當調用obj.notify/notifyAll後,調用線程依舊持有obj鎖,所以等待線程雖被喚醒,但仍沒法得到obj鎖,直到調用線程退出synchronized塊,釋放obj鎖後,其餘等待線程纔有機會得到鎖繼續執行。
通常線程阻塞
1)線程執行了Thread.sleep(int millsecond)方法,放棄CPU,睡眠一段時間,一段時間事後恢復執行;
2)線程執行一段同步代碼,但沒法得到相關的同步鎖,只能進入阻塞狀態,等到獲取到同步鎖,才能恢復執行;
3)線程執行了一個對象的wait()方法,直接進入阻塞態,等待其餘線程執行notify()/notifyAll()操做;
4)線程執行某些IO操做,由於等待相關資源而進入了阻塞態,如System.in,但沒有收到鍵盤的輸入,則進入阻塞態。
5)線程禮讓,Thread.yield()方法,暫停當前正在執行的線程對象,把執行機會讓給相同或更高優先級的線程,但並不會使線程進入阻塞態,線程仍處於可執行態,隨時可能再次分得CPU時間。
線程自閉,join()方法,在當前線程調用另外一個線程的join()方法,則當前線程進入阻塞態,直到另外一個線程運行結束,當前線程再由阻塞轉爲就緒態。
6)線程執行suspend()使線程進入阻塞態,必須resume()方法被調用,才能使線程從新進入可執行狀態
1 ) 使用標誌位
2)使用stop()方法,但該方法就像關掉電腦電源同樣,可能會發生預料不到的問題
3)使用中斷interrupt()
public class Thread { // 中斷當前線程 public void interrupt(); // 判斷當前線程是否被中斷 public boolen isInterrupt(); // 清除當前線程的中斷狀態,並返回以前的值 public static boolen interrupted(); }
但調用interrupt()方法只是傳遞中斷請求消息,並不表明要立馬中止目標線程。
之因此須要同步,由於在多線程併發控制,當多個線程同時操做一個可共享的資源時,若是沒有采起同步機制,將會致使數據不許確,所以須要加入同步鎖,確保在該線程沒有完成操做前被其餘線程調用,從而保證該變量的惟一一性和準確性。
1)synchronized修飾同步代碼塊或方法
因爲java的每一個對象都有一個內置鎖,用此關鍵字修飾方法時,內置鎖會保護整個方法。在調用該方法前,需得到內置鎖,不然就處於陰塞狀態。
2)volatile修飾變量
保證變量在線程間的可見性,每次線程要訪問volatile修飾的變量時都從內存中讀取,而不緩存中,這樣每一個線程訪問到的變量都是同樣的。且使用內存屏障。
3)ReentrantLock重入鎖,它經常使用的方法有ReentrantLock():建立一個ReentrantLock實例
lock()得到鎖 unlock()釋放鎖
4)使用局部變量ThreadLocal實現線程同步,每一個線程都會保存一份該變量的副本,副本之間相互獨立,這樣每一個線程均可以隨意修改本身的副本,而不影響其餘線程。
經常使用方法ThreadLocal()建立一個線程本地變量;get()返回此線程局部的當前線程副本變量;initialValue()返回此線程局部變量的當前線程的初始值;set(T value)將此線程變量的當前線程副本中的值設置爲value
5) 使用原子變量,如AtomicInteger,經常使用方法AtomicInteger(int value)建立個有給定初始值的AtomicInteger整數;addAndGet(int data)以原子方式將給定值與當前值相加
6)使用阻塞隊列實現線程同步LinkedBlockingQueue<E>
線程安全性體如今三方法:
1)原子性:提供互斥訪問,同一時刻只能有一個線和至數據進行操做。
JDK中提供了不少atomic類,如AtomicIntegerAtomicBooleanAtomicLong,它們是經過CAS完成原子性。JDK提供鎖分爲兩種:synchronized依賴JVM實現鎖,該關鍵字做用對象的做用範圍內同一時刻只能有一個線程進行操做。另外一種是LOCK,是JDK提供的
代碼層面的鎖,依賴CPU指令,表明性是ReentrantLock。
2)可見性:一個線程對主內存的修改及時被其餘線程看到。
JVM提供了synchronized和volatile,volatile的可見性是經過內存屏障和禁止重排序實現的,volatile會在寫操做時,在寫操做後加一條store屏障指令,將本地內存中的共享變量值刷新到主內存;會在讀操做時,在讀操做前加一條load指令,從內存中讀取共享變量。
3)有序性:指令沒有被編譯器重排序。
可經過volatile、synchronized、Lock保證有序性。
我認爲能夠實現,好比兩個進程都讀取日曆進程數據是沒有問題,但同時寫,應該會有衝突。
可使用共享內存實現進程間數據共享。
1)建立階段(Created):爲對象分配存儲空間,開始構造對象,從超類到子類對static成員初始化;超類成員變量按順序初始化,遞歸調用超類的構造方法,子類成員變量按順序初始化,子類構造方法調用。
2)應用階段(In Use):對象至少被一個強引用持有着。
3)不可見階段(Invisible):程序運行已超出對象做用域
4)不可達階段(Unreachable):該對象再也不被強引用所持有
5)收集階段(Collected):假設該對象重寫了finalize()方法且未執行過,會去執行該方法。
6)終結階段(Finalized):對象運行完finalize()方法仍處於不可達狀態,等待垃圾回收器對該對象空間進行回收。
7)對象空間從新分配階段(De-allocated):垃圾回收器對該對象所佔用的內存空間進行回收或再分配,該對象完全消失。
static synchronized控制的是類的全部實例訪問,無論new了多少對象,只有一份,因此對該類的全部對象都加了鎖。限制多線程中該類的全部實例同時訪問JVM中該類對應的代碼。
若是synchronized修飾的是靜態方法,鎖的是當前類的class對象,進入同步代碼前要得到當前類對象的鎖;
普通方法,鎖的是當前實例對象,進入同步代碼前要得到的是當前實例的鎖;
同步代碼塊,鎖的是括號裏面的對象,對給定的對象加鎖,進入同步代碼塊庫前要得到給定對象鎖;
若是兩個線程訪問同一個對象的synchronized方法,會出現競爭,若是是不一樣對象,則不會相互影響。
有volatile變量修飾的共享變量進行寫操做的時候會多一條彙編代碼,lock addl $0x0,lock前綴的指令在多核處理器下會將當前處理器緩存行的數據會寫回到系統內存,這個寫回內存的操做會引發在其餘CPU裏緩存了該內存地址的數據無效。同時lock前綴也至關於一個內存屏障,對內存操做順序進行了限制。
synchronized經過對象的對象頭(markword)來實現鎖機制,java每一個對象都有對象頭,均可覺得synchronized實現提供基礎,均可以做爲鎖對象,在字節碼層面synchronized塊是經過插入monitorenter monitorexit完成同步的。持有monitor對象,經過進入、退出這個Monitor對象來實現鎖機制。
NIO( New Input/ Output) 引入了一種基於通道和緩衝區的 I/O 方式,它可使用 Native 函數庫直接分配堆外內存,而後經過一個存儲在 Java 堆的 DirectByteBuffer 對象做爲這塊內存的引用進行操做,避免了在 Java 堆和 Native 堆中來回複製數據。
NIO 是一種同步非阻塞的 IO 模型。同步是指線程不斷輪詢 IO 事件是否就緒,非阻塞是指線程在等待 IO 的時候,能夠同時作其餘任務。
同步的核心就是 Selector,Selector 代替了線程自己輪詢 IO 事件,避免了阻塞同時減小了沒必要要的線程消耗;非阻塞的核心就是通道和緩衝區,當 IO 事件就緒時,能夠經過寫道緩衝區,保證 IO 的成功,而無需線程阻塞式地等待。
1)volatile:解決變量在多個線程間的可見性,但不能保證原子性,只能用於修飾變量,不會發生阻塞。volatile能屏蔽編譯指令重排,不會把其後面的指令排到內存屏障以前的位置,也不會把前面的指令排到內存屏障的後面。多用於並行計算的單例模式。volatile規定CPU每次都必須從內存讀取數據,不能從CPU緩存中讀取,保證了多線程在多CPU計算中永遠拿到的都是最新的值。
2)synchronized:互斥鎖,操做互斥,併發線程過來,串行得到鎖,串行執行代碼。解決的是多個線程間訪問共享資源的同步性,可保證原子性,也可間接保證可見性,由於它會將私有內存和公有內存中的數據作同步。可用來修飾方法、代碼塊。會出現阻塞。synchronized發生異常時,會自動釋放線程佔有的鎖,所以不會致使死鎖現象發生。非公平鎖,每次都是相互爭搶資源。
3)lock是一個接口,而synchronized是java中的關鍵字,synchronized是內置語言的實現。lock可讓等待鎖的線程響應中斷。在發生異常時,若是沒有主動經過unLock()去釋放鎖,則可能形成死鎖現象,所以使用Lock時須要在finally塊中釋放鎖。
4)ReentrantLock可重入鎖,鎖的分配機制是基於線程的分配,而不是基於方法調用的分配。ReentrantLock有tryLock方法,若是鎖被其餘線程持有,返回false,可避免造成死鎖。對代碼加鎖的顆粒會更小,更節省資源,提升代碼性能。ReentrantLock可實現公平鎖和非公平鎖,公平鎖就是先來的先獲取資源。ReentrantReadWriteLock用於讀多寫少的場合,且讀不須要互斥場景。
5)併發編程有關知識點(這個是通常Android開發用的少的,因此建議多去看看):
平時Android開發中對併發編程能夠作得比較少,Thread這個類常常會用到,可是咱們想提高本身的話,必定不能停留在表面,,咱們也應該去了解一下java的關於線程相關的源碼級別的東西。