1、多線程在CPU切換過程中,由于需要保存線程之前狀態和加載新線程狀態,成為上下文切換,上下文切換會造成消耗系統內存。所以,可合理控制線程數量。 如何控制:
(1)使用ps -ef|grep appname,查找appname的pid;如1111
(2)使用jstack 1111 > /home/ibethfy/dump1,將dump信息追加到dump1
(3)使用grep java.lang.Thread.State /home/ibethfy/dump1 | awk '{print $2$3$4$5}' | sort | uniq -c,統計查找線程狀態個數。分析是否WAITING的線程過多。
(4)查看這些WAITING的線程時怎么產生的,如某個服務創建線程池中線程過多,分析后對應修改。
(5)重啟后重新統計。
2、避免死鎖幾種常用方式
(1)避免一個線程同時獲取多個鎖。
(2)避免一個線程在鎖內同時占用多個資源,盡量保證每個鎖只占用一個資源。
(3)嘗試使用定時鎖,lock.tryLock(timeout)來替代內部鎖機制。
(4)對于數據庫鎖,加鎖和解鎖必須在同一個連接內,不然會造成解鎖失敗的情況。
3、volatile保證共享資源對其他線程的可見性。volatile修飾共享變量時,共享變量進行寫操作時,會在匯編中多一個lock前綴的指令,該指定表示要將該變量從當前處理器的緩存行數據寫到系統內存,寫會內存操作會使其他CPU緩存了該內存地址的數據失效。其他CPU去獲取該數據時,會從系統內存重新獲取。
4、synchronized,JVM通過進入和退出Monitor對象來實現代碼塊和方法同步。代碼塊同步使用的是monitorenter和monitorexit來實現。在編譯時,會將這兩個指令分別插入到代碼塊開始和結束(包括正常和異常結束),在執行這兩句指令時,會去進去和退出monitor對象。
5、鎖在哪?Java的鎖,是存在對象頭的Mark Word中。存儲一個鎖地址,指向C++里面的一個ObjectMonitor對象,該對象存儲了當前占用鎖的線程,等待的線程,該線程重入的次數,鎖狀態等字段。
6、JMM,Java內存模型,用于控制Java線程之間的通信,和計算機多線程模型類似,也是為每個線程分配了類似的內存緩存,變量存在主內存中,要保證線程之間的通信,必須要保證線程A將數據從本地緩存刷新到主內存,線程B識別到數據更新,從主內存中取得最新數據。JMM主要就是控制主內存和線程本地內存之間的交互等。
7、JMM模型中,主要是解決有序性(順序一致性),可見性,原子性。
(1)由于多線程中,每個線程有自己的本地緩存,且在執行代碼后,不知道何時回把緩存刷新到主內存,所以,線程的操作對其他線程是不可見的。這時不同線程獲取共享變量就會出現不一致的問題。
(2)指令的重排序:重排序是指編譯器和處理器為了優化程序性能而對指令序列進行重新排序的一種手段。數據依賴性:某一行代碼依賴上一行代碼,這樣不會進行這兩條代碼的重排序,多線程系統不會考慮該問題。順序一致性:指令重排序后,最終執行結果要和順序執行結果一致。
(3)多線程之間進行了正確的數據同步,JMM保證多個線程的順序一致性。
(4)順序一致性模型:一個程序的操作必須按照程序順序執行。不管程序是否同步,所有線程都能按照單一的操作執行順序。即多個線程的操作可以并行執行,但對于單個線程來看,他的指令時順序執行的,且每條指令都對其他線程可見。
(5)JMM不保證順序一致性,即未同步的線程,執行順序是無序的,且可能對其他內存不可見。這個時候,就需要同步來實現順序一致性模型。synchronized修飾的代碼塊,在獲得鎖后才能執行,從而保證了多個線程的整體順序執行,但同步塊局部不依賴的指令,也可進行重排序(盡量給編譯器或者優化器優化提供入口)。為什么JMM不保證未同步的線程的順序一致性呢?主要是要保證會禁止大量的編譯器等優化,嚴重影響性能,所以就提供同步方法來實現。
(6)JMM內存可見性的保證:單線程不會出現內存可見性問題,編譯器,runtime,處理器會共同保證期執行結果和順序一致性模型結果一致;多線程,正確同步的多線程將具有順序一致性,JMM通過限制編譯器和處理器的重排序來提供內存可見性的保證。未同步或未正確同步的多線程,JMM為他們提供了最小安全性的保證,線程讀取到的值,要么是之前線程寫入的值,要么是初始值。
(7)volatile,當寫volatile時,其他讀寫指令在其之后執行,保證了有序性;當寫時,會把新的緩存中的值寫入到主內存;另一線程讀時,會將本地緩存地址置為無效,從主內存中獲取該值。
(8)synchronized,當某線程獲得鎖時,其余線程等待,保證了有序性。當某線程釋放鎖時,會將本地緩存寫入主內存,當另一線程獲取鎖時,會將本地緩存置為無效,從主內存獲取數據。所以保證了多線程的順序一致性和可見性。
(9)happens-before規則。
8、wait和notify:WaitThread首先獲取了對象的鎖,然后調用鎖對象的wait()方法,從而放棄了鎖并進入了對象的等待隊列WaitQueue中,進入等待狀態。由于WaitThread釋放了對象的鎖,NotifyThread隨后獲取了對象的鎖,并調用鎖對象的notify()方法,將WaitThread從WaitQueue移到SynchronizedQueue中,此時WaitThread的狀態變為阻塞狀態。NotifyThread釋放了鎖之后,WaitThread再次獲取到鎖并從wait()方法返回繼續執行。金典案例如下: synchronized(對象)?{ ???????
while(條件不滿足)?{ ?????????????
?對象.wait(); ?????
??} ?????
? ?對應的處理邏輯
}
synchronized(對象)?{
???????改變條件 ???????
對象.notifyAll();
}
9、ThreadLocal:ThreadLocal,即線程變量,是一個以ThreadLocal線程對象為鍵、任意對象為值的存儲結構。這個結構被附帶在線程上,也就是說一個線程可以根據一個ThreadLocal對象查詢到綁定在這個線程上的一個值。
10、CountDownLatch:
(1)、初始化CountDownLatch cdl? = new CountDownLatch(5);
(2)、同一線程或多個線程調用cdl.countDown(),使計數器減一,通過入參傳入cdl;等待線程調用cdl.await(),阻塞等待cdl計數器等于0后,執行。
11、CyclicBarrier:
(1)、初始化CyclicBarrier cb = new CyclicBarrier();
(2)、其余線程調用cb.await(),告訴屏障我已到達,然后等待指定數量的線程都已到達后,然后統一往后執行。
(3)、構造函數可傳入另一線程,表示到達屏障的數量滿足后,優先執行該方法。
12、Semaphore:Semaphore(10)表示允許10個線程獲取許可證,也就是最大并發數是10。Semaphore的用法也很簡單,首先線程使用Semaphore的acquire()方法獲取一個許可證,使用完之后調用release()方法歸還許可證。還可以用tryAcquire()方法嘗試獲取許可證。
13、Exchanger:用于線程間數據交換。
14、線程池技術:好處
(1)降低系統消耗,重復利用線程
(2)提高響應速度,不用重新創建線程
(3)提高可管理性,可以實現對線程的管理。
線程池流程:
(1)新來一個任務,如果沒有超過核心線程數,則創建一個核心線程,執行任務,不管是否有閑置的核心線程,也會創建新的核心線程執行任務。
(2)如果核心線程都處于運行狀態,且數量已達上限,則判斷工作隊列是否已滿,如果未滿,則將任務加入工作隊列,排隊等待核心線程空閑后執行。
(3)如果核心線程都在運行任務和工作隊列都滿了,則創建新的額外線程來執行任務。(額外線程由最大線程決定,且無用后會被回收)
(4)額外線程也滿了且都在執行的話,就采用拒絕策略拒絕請求。
線程池創建參數:
(1)corePoolSize,核心線程數量。
(2)runnableTaskQueue:任務隊列,有ArrayBlockingQueue,基于數組的有界阻塞隊列,FIFO;LinkedBlockingQueue,基于鏈表實現的阻塞隊列,FIFO,吞吐量通常高于ArrayBlockingQueue,靜態工廠方法Executors.newFixedThreadPool()使用了這個隊列;SynchronousQueue:一個不存儲元素的阻塞隊列。每個插入操作必須等到另一個線程調用移除操作,否則插入操作一直處于阻塞狀態,吞吐量通常要高于Linked-BlockingQueue,靜態工廠方法Executors.newCachedThreadPool使用了這個隊列;
(3)maximumPoolSize:線程池最大線程數,線程池允許創建的最大線程數。如果隊列滿了,并且已創建的線程數小于最大線程數,則線程池會再創建新的線程執行任務。值得注意的是,如果使用了無界的任務隊列這個參數就沒什么效果。
(4)ThreadFactory:用于設置創建線程的工廠;
(5)RejectedExecutionHandler(飽和策略):當隊列和線程池都滿了,說明線程池處于飽和狀態,那么必須采取一種策略處理提交的新任務。這個策略默認情況下是AbortPolicy,表示無法處理新任務時拋出異常。以下四種策略: ·AbortPolicy:直接拋出異常。 ·CallerRunsPolicy:只用調用者所在線程來運行任務。 ·DiscardOldestPolicy:丟棄隊列里最近的一個任務,并執行當前任務。 ·DiscardPolicy:不處理,丟棄掉
(6)keepAliveTime(線程活動保持時間):線程池的工作線程空閑后(非核心線程會回收,allowCoreThreadTimeOut(true),設置這個屬性,核心線程在超時未用后,也會被回收),保持存活的時間。所以,如果任務很多,并且每個任務執行的時間比較短,可以調大時間,提高線程的利用率。
(7)TimeUnit(線程活動保持時間的單位);