來自小馬哥公開課java
歸根到底new Thread()
並調用start()
是java建立並運行線程的惟一方式. Runnable
,Callable
以及繼承 Thread
, 還有lamda表達式等相似代碼其實都是線程的使用方式.數組
相比較而言, 建立進程的Java方式是使用Runtime#exec(String command)
方法實現.安全
Java正在執行的線程只有在執行完畢, 或者拋出異常後才能中止. 也就是基本能夠認爲: 線程一旦開始執行就沒法從外部控制其銷燬.多線程
Thread#stop()
方法用來中止線程, 但此方法過於暴力, 可能會致使資源不會正確釋放, 會致使死鎖, 此方法已經標記爲過時.jvm
while(true)
循環類線程可使用interrupt()
方法經過拋出InterruptedException
來結束線程, 但要注意只有線程阻塞狀況下才會拋出此異常, 所以, 對於非阻塞狀態的線程, 是使用isInterrupted()
方法來判斷中斷標誌來結束控制循環.ide
while(!isInterrupted()){ try{ Thread.sleep(5000); }catch(InterruptedException e){ e.pringStackTrace(); break; } }
使用狀態標誌變量中止方式以下:工具
class TaskThread implements Runnable{ private volatile boolean stoped = fasle; // 標識字段, 注意線程安全問題, 使用volatile保證可見性 @Override public void run(){ if(!stopped){ // todo } } public void setStoped(boolean stop){ stoped=stop; } }
join()
join()
方法會阻塞調用它的線程. 方法的關鍵代碼以下:性能
public final synchronized void join(long millis){ while (isAlive()) { wait(millis); } }
可見其最終是使用Object#wait(long millis)
來實現, 也就是說, 咱們本身也可使用Object#wait()
來控制線程的執行順序.ui
ExecutorService singlePool = Executors.newSingleThreadExecutor() singlePool.submit(t1); singlePool.submit(t2); singlePool.shutdown();
CountdownLatch
t1.start() while(t1.isAlive()){ //Thread.sleep(5); } t2.start();
Thread.sleep(long millis)
Java只能捕獲unchecked異常. 線程異常時線程即終止.線程
可使用下面方式捕獲線程拋出的異常, 這樣作的話線程的異常堆棧將不會輸出到System.error標準輸出流.
對於堆棧過多的場景來自定義錯誤handler處理, 能夠防止錯誤堆棧致使內存耗盡. Spring boot 中的SpringApplication有相關的接口.
Thread.setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh);
除了上面的方法, 還可使用以下方法捕獲異常.
ThreadPoolExecutor#afterExecute(Runnable r, Throwable t)
NEW 新建線程 new
RUNABLE 可運行, start()
BLOCKED 阻塞, 獲取鎖
WAITING 等待
TIME_WAITING 超時等待
TERMINATED 終結
jstack pid
能夠顯示出全部線程.ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long[] ThreadIds = threadMXBean.getAllThreadIds(); for(long threadId : ThreadIds){ ThreadInfo threadInfo =ThreadMXBean.getThreadInfo(threadId); }
synchronized 在方法和代碼塊上的區別,能夠查看字節碼文件. 關注monitorenter和monitorexit指令, 方法的話關注其flag的標誌位.
兩者都可重入. ReentrantLock 多了三個功能, 可中斷, 公平鎖, 綁定多個Condition. ReentrantLock 性能可能更高一點, 也有更多的控制, 好比嘗試得到鎖, 釋放等, 同時也能夠綁定多個條件.
final boolean nonfairTryAcquire(int acquires) { final Thread current = Thread.currentThread(); int c = getState(); // 當前的state if (c == 0) { //首次獲取到鎖 if (compareAndSetState(0, acquires)) { //CAS並設置狀態 setExclusiveOwnerThread(current); //線程暫存 return true; } } else if (current == getExclusiveOwnerThread()) { //斷定是否存儲當前線程, 是的話就是重入 int nextc = c + acquires; //狀態新增 if (nextc < 0) // overflow throw new Error("Maximum lock count exceeded"); setState(nextc); //狀態回寫 return true; } return false; }
wait()
和 notity()
notifyAll()
在 Object 中定義的緣由?一個很明顯的緣由是JAVA提供的鎖是對象級的而不是線程級的,每一個對象都有鎖,經過線程得到。鎖是線程爭用的對象, 與線程不該該過分鏈接. Object上也有一些頭信息, 爲鎖的管理提供方便.
wait()
和 notity()
爲何必須在 synchronized 中執行?很簡單, 多線程環境下的鎖纔有意義, synchronized 代表有資源爭用, 即鎖的爭奪, 這是鎖存在的前提.
關於這個問題, 其實是一個depands-on的答案, 有時候子線程會繼續執行一段時間, 有時候則在主線程退出後當即退出, 通常認爲主線程退出後臺線程也會退出, 但並不能認爲子線程必定不會執行.
方法簽名: Runtime#addShutdownHook(Thread thread)
這個鉤子方法能夠在線程退出時回調完成一些工做, 好比資源回收,還有一些對象的銷燬工做.
ThreadGroup
ThreadGroup threadGroup = Thread.currentThread().getThreadGroup(); int count = threadGroup.activeCount(); Thread[] threads = new Thread[count]; threadGroup.enumerate(threads,true); //複製到這個數組 for (Thread t : threads) { System.out.println(t.getState()); }