1.什么是進程和線程
進程:程序運行資源分配的最小單位,進程內部有多個線程,會共享這個進程的資源
線程:CPU調度的最小單位,必須依賴進程而存在。
2、進程有自己的獨立地址空間,每啟動一個進程,系統就會為它分配地址空間,建立數據表來維護代碼段、堆棧段和數據段,這種操作非常昂貴。
而線程是共享進程中的數據的,使用相同的地址空間,因此CPU切換一個線程的花費遠比進程要小很多,同時創建一個線程的開銷也比進程要小很多。
3、線程之間的通信更方便,同一進程下的線程共享全局變量、靜態變量等數據,而進程之間的通信需要以通信的方式(IPC)進行。不過如何處理好同步與互斥是編寫多線程程序的難點。
4、但是多進程程序更健壯,多線程程序只要有一個線程死掉,整個進程也死掉了,而一個進程死掉並不會對另外一個進程造成影響,因為進程有自己獨立的地址空間
2. 新起線程方法
兩種方法:繼承類Thread
實現接口 Runnable
為什么要類,也要接口。因為JAVA單繼承,類只能繼承一個
接口 Callable 與Runnable 區別:Callable 有返回值
3.怎么樣才能讓Java里的線程安全停止工作呢
線程自然終止:自然執行完或拋出未處理異常
stop(),resume(),suspend()已不建議使用,stop()會導致線程不會正確釋放資源,suspend()不釋放資源容易導致死鎖。
java線程是協作式,而非搶占式
調用一個線程的interrupt() 方法中斷一個線程,並不是強行關閉這個線程,只是跟這個線程打個招呼,將線程的中斷標志位置為true,線程是否中斷,由線程本身決定。
isInterrupted() 判定當前線程是否處於中斷狀態。
static方法interrupted() 判定當前線程是否處於中斷狀態,同時中斷標志位改為false。
方法里如果拋出InterruptedException,線程的中斷標志位會被復位成false,如果確實是需要中斷線程,要求我們自己在catch語句塊里再次調用interrupt()。
所有阻塞方法都會拋出InterruptedException
4.一個線程的生命周期(線程的方法)
新建線程調用start()方法進入就緒狀態,cpu分配或join()方法獲取執行權進入運行狀態,調用sleep()或wait()進入阻塞狀態,sleep時間到或調用notify,notifyAll方法進入就緒狀態,運行時還可以調用yield方法讓步進入就緒狀態進行重新爭搶cpu
5.調用yield() 、sleep()、wait()、notify()等方法對鎖有何影響?
線程在執行yield()以后,持有的鎖是不釋放的
sleep()方法被調用以后,持有的鎖是不釋放的
調動方法之前,必須要持有鎖。調用了wait()方法以后,鎖就會被釋放,當wait方法返回的時候,線程會重新持有鎖
調動方法之前,必須要持有鎖,調用notify()方法本身不會釋放鎖的
6.守護線程是什么
和主線程共死,finally不能保證一定執行
在start之前setDaemon(true);設置守護線程,主線程運行完,子線程也結束。
7.synchronized內置鎖
對象鎖,鎖的是類的對象實例。
類鎖 ,鎖的是每個類的的Class對象,每個類的的Class對象在一個虛擬機中只有一個,所以類鎖也只有一個。
8.volatile關鍵字
適合於只有一個線程寫,多個線程讀的場景,因為它只能確保可見性。
9.ThreadLocal
ThreadLocal是一個本地線程副本變量工具類。主要用於將私有線程和該線程存放的副本對象做一個映射,各個線程之間的變量互不干擾,在高並發場景下,可以實現無狀態的調用,特別適用於各個線程依賴不同的變量值完成操作的場景。
ThreadLocal為變量在每個線程中都創建了一個副本,那么每個線程可以訪問自己內部的副本變量。
該類中方法有
public T get() { } public void set(T value) { } public void remove() { } protected T initialValue() { }
get()方法是用來獲取ThreadLocal在當前線程中保存的變量副本,set()用來設置當前線程中變量的副本,remove()用來移除當前線程中變量的副本,initialValue()是一個protected方法,一般是用來在使用時進行重寫的,它是一個延遲加載方法,ThreadLocal沒有被當前線程賦值時或當前線程剛調用remove方法后調用get方法,返回此方法值。
線程變量。可以理解為是個map,類型 Map<Thread,T>
10.CountDownLatch
作用:是一組線程等待其他的線程完成工作以后在執行,加強版join
await用來等待,countDown負責計數器的減一
11.CyclicBarrier
讓一組線程達到某個屏障,被阻塞,一直到組內最后一個線程達到屏障時,屏障開放,所有被阻塞的線程會繼續運行CyclicBarrier(int parties)
12.Semaphore
控制同時訪問某個特定資源的線程數量,用在流量控制
13.Exchange
兩個線程間的數據交換
14.CAS的原理
CAS(Compare And Swap),指令級別保證這是一個原子操作
基本思路:如果地址V上的值和期望的值A相等,就給地址V賦給新值B,如果不是,不做任何操作。
CAS可以有效的提升並發的效率,但同時也會引入ABA問題。
ABA問題可加入版本號進行標識是否有變更
從Java1.5開始JDK的atomic包里提供了一個類AtomicStampedReference來解決ABA問題。這個類的compareAndSet方法作用是首先檢查當前引用是否等於預期引用,並且當前標志是否等於預期標志,如果全部相等,則以原子方式將該引用和該標志的值設置為給定的更新值。
15.Lock接口和synchronized的比較
synchronized jvm級別的鎖,代碼簡潔,Lock:Java語言級別的鎖,獲取鎖可以被中斷,超時獲取鎖,嘗試獲取鎖,讀多寫少用讀寫鎖
16.可重入鎖ReentrantLock中所謂鎖的公平和非公平有什么區別
如果在時間上,先對鎖進行獲取的請求,一定先被滿足,這個鎖就是公平的,不滿足,就是非公平的
非公平的效率一般來講更高
節約了從掛起(排隊)到拿鎖的時間
17.AQS是什么
AbstractQueuedSynchronizer
實質上用雙向鏈表實現同步隊列
用AQS實現同步,首先線程獲取同步狀態失敗時,生成note節點加入同步隊列尾部(用CAS設置),判斷前驅是否為首節點,是的話嘗試獲取同步狀態,獲取成功將其設置為首節點,獲取不成功進入等待隊列,再去嘗試獲取同步狀態
競爭失敗的線程會打包成Node放到同步隊列
注:AQS實質上擁有一個同步隊列和多個等待隊列,具體對應關系如下圖所示:
上邊為同步隊列 雙向鏈表
下邊為condition等待隊列 單向鏈表
Await從同步隊列移動到等待隊列等待
Signal從等待隊列移動到同步隊列爭搶鎖
18.Concurrenthashmap實現原理
1.7及之前: ConcurrentHashMap是由Segment數組結構和HashEntry數組結構組成。Segment實際繼承自可重入鎖(ReentrantLock),在ConcurrentHashMap里扮演鎖的角色;HashEntry則用於存儲鍵值對數據。一個ConcurrentHashMap里包含一個Segment數組,每個Segment里包含一個HashEntry數組,我們稱之為table,每個HashEntry是一個鏈表結構的元素。
1.8
1、 取消了segment數組,直接用table保存數據,鎖的粒度更小,減少並發沖突的概率。
2、 存儲數據時采用了鏈表+紅黑樹的形式,純鏈表的形式時間復雜度為O(n),紅黑樹則為O(logn),性能提升很大。什么時候鏈表轉紅黑樹?當key值相等的元素形成的鏈表中元素個數超過8個的時候。
主要數據結構和關鍵變量
Node類存放實際的key和value值,hash ,next
sizeCtl:
負數:表示進行初始化或者擴容,-1表示正在初始化,-N,表示有N-1個線程正在進行擴容
正數:0 表示還沒有被初始化,>0的數,初始化或者是下一次進行擴容的閾值
TreeNode 用在紅黑樹,表示樹的節點, TreeBin是實際放在table數組中的,代表了這個紅黑樹的根。
在高並發下的情況下如何保證取得的元素是最新的?
答:用於存儲鍵值對數據的HashEntry,在設計上它的成員變量value等都是volatile類型的,這樣就保證別的線程對value值的修改,get方法可以馬上看到。
問ConcurrentHashMap如何在保證高並發下線程安全的同時實現了性能提升?
答:ConcurrentHashMap允許多個修改操作並發進行,其關鍵在於使用了鎖分離技術。它使用了多個鎖來控制對hash表的不同部分進行的修改。內部使用段(Segment)來表示這些不同的部分,每個段其實就是一個小的hash table,只要多個修改操作發生在不同的段上,它們就可以並發進行。