1 普通線程和 守護線程的區別。java
守護線程會跟隨主線程的結束而結束,普通線程不會。web
2 線程的 stop 和 interrupted 的區別。 stop 會中止線程,可是不會釋放鎖之類的資源? interrupt 會讓線程拋出異常。數據庫
測試:stop 和 interrupt 關於鎖釋放的問題。緩存
3 關於線程 和 Thread 的 關係。 其實 實現線程的 方法只有一種,就是 new Thread 的對象 。 Thread 裏面 的run 方法 默認執行方式會去 執行 Runnable target 的 run方法。 這也就是 咱們說的 實現Runnaable 方法。當時這時候其實五門也建立了一個Thread 對象。安全
Lambda 表達式也是被翻譯了一個 Runnabel 的 子類對象。服務器
通常認爲的實現線程的方法多線程
1 繼承Thread 類併發
2 實現Runnable 接口框架
3 匿名內部類直接new一個 Thread 或者 直接new 一個Runnable。tcp
4 使用Runnable 帶返回值的封裝, FutureTask 和 Callable
5使用定時 使用 Timer 和 TimerTask
6 線程池
7 lambda 表達式
@Override public void run() { if (target != null) { target.run(); } }
4 若是一個線程參數裏面傳入了 Runnable ,而後又重寫了 run 方法。這時候生效的是重寫的run方法,由於 子類已經重寫了 run方法,父類的 run方法不會別執行(多態)。
5 FutureTask 封裝了 Runnable 和 Future 。讓線程的執行能夠拿到 一個返回值,其實它是 吧 Callable 的返回值 放在 一個成員上面,而後經過get 接口去拿,而且這個get 接口會等待 run 線程結束。
run 方法 執行的 Callable 的 call 方法,而後把 call 的返回值放在 成員變量上面,而後等待 Future 的 get 方法來取 這個返回值,而且這個 get 方法會等待線程執行完畢取到返回值。
6 Timer 和 t.schedule(task, time); 能夠實現延時任務。 而且它會另外啓動一個線程。
7 單利的寫法
餓漢式:(沒有線程安全問題)
/** * 餓漢式單利寫法(線程安全) * @author ZHANGYUKUN * */ public class Single { private static Single single = new Single(); private Single (){} public static Single getInstance(){ return single; } }
懶漢式:(有線程安全問題)
/** * 懶漢式單利寫法(有線程安全問題) * @author ZHANGYUKUN * */ public class Single2 { private static Single2 single; private Single2 (){} public static Single2 getInstance(){ if( single == null ){ single = new Single2(); } return single; } }
懶漢式,雙if 寫法(基本不會出線程安全,可是會出指令重排序)
/** * 懶漢式單利雙id寫法(基本無線程安全問題,可是有指令重排序問題) * @author ZHANGYUKUN * */ public class Single3 { private static Single3 single; private Single3 (){} public static Single3 getInstance(){ if( single == null ){ synchronized (Single3.class) { if( single == null ){ single = new Single3();//這裏的 new 和 賦值 可能被重排序 } } } return single; } }
解釋:new 一個對象,而後賦值分紅3 部。
1 分配內存空間
2 初始這個空間
3 吧這個對象引用指向這個空間
指令重排序若是 讓3 ,2 顛倒,那麼這時候 這個 空間還沒初始化,可是已經不爲null了。另外一個線程 獲取單利對象就會 獲取到這個獲取到這個沒有初始化的對象( 在 3 執行了,2 還沒執行的 這一小段時間 會出這個問題 )。
懶漢式最終版:
/** * 懶漢式單利雙id寫法(無線程安全問題) * @author ZHANGYUKUN * */ public class Single4 { private static volatile Single4 single; private Single4 (){} public static Single4 getInstance(){ if( single == null ){ synchronized (Single4.class) { if( single == null ){ single = new Single4();//volatile 修飾 禁止重排序 } } } return single; } }
8 自旋 是消耗資源的 wait 是不消耗資源的。
9 重入鎖 。在同一線程中,容許屢次進入這個鎖,不須要再次獲搶鎖,而只是在計數器上+1。
synchronized 是重入鎖。
synchronized 是在 對象頭裏面寫了一個線程id。標誌這個鎖被這個線程拿到,若是 一個線程調用了這個線程的兩個方法 a,和 b ,他們 都須要獲取這個對象改的鎖,而且 a 裏面調用了 b,由於是重入鎖 因此不會 死鎖。
10 synchronized 在等待鎖的過程當中會自旋。
11 Thread.activeCount() 能夠看到還有幾個線程活着。
12 volatile 兩個做用
1 禁止指令重排序
2 多線程 可見性
多線程環境中,一個變量被修改是發生在cpu 緩存中的。通常狀況下這個修改操做不必定會裏面刷新到,計算機內存中( 對象的堆內存 )。
若是是valatile 修飾的變量的改動會馬上 更新到內存中,而且,會是別的cpu 緩存中的 這個變量的 值失效。從而保證多線程可見性。
volatile 的缺點:
1 禁用重排序,會下降效率
2 volatitle 會使 cpu 緩存失效,下降性能。
13 synchornized 級能保證多線程可見性,有能保證 原子性,由於它是同步的。
volatile 只能保證保證 可見性,不能保證 原子性,若是是 非原子的操做並不能保證線程安全。
可是 synchornized 效率比 volatile 低。
14 java java.util.concurrent.atomic 包 下面有支持原子操做的輔助類。
15 lock 和 synchronize 的 比較
lock 須要顯示的聲明和釋放,比較麻煩。可是 比synchronize 靈活,而且能實現公平鎖性。
synchronize 使用簡單。
lock 的靈活體如今
1 非阻塞的獲取鎖
2 能中斷獲取鎖
3 給獲取鎖設定最大等待時間
16 ReentrantReadWriteLock 鎖的降級, 在寫鎖還沒釋放的時候,就獲取讀鎖,這樣能夠保證,寫的的那份數據後讀到的就是就是最新的,而且不阻塞別的讀線程,會被被別人修改。
鎖的升級,和 鎖的降級正好相反,ReentrantReadWriteLock 不支持。它是爲了保證 我讀到的是最新的,而且用這個讀到的數據直接作修改。不會出現這個 讀鎖釋放,到寫鎖獲取這個 時間差 數據被修改。
備註: 讀鎖 是共享的,寫鎖的排他的,正常來講 ,加了 寫鎖,讀鎖就加不上,可是 鎖升級和降級是對 當前線程 讀寫鎖 重入的一種特殊處理。
17 公平鎖
獲取鎖的過程不是經過搶鎖,而是經過排隊的鎖叫作公平鎖。
18 讀鎖和寫鎖的目的
讀鎖:禁止別的線程寫數據,可是別的線程能夠讀數據。這樣的目的是我 讀到的數據在鎖的週期內是最新的,而且不會被改變(不讓寫)。
寫鎖: 禁止一切鎖, 在寫鎖的範圍內,數據必定是最新的,不會被別人改變(不讓寫),而且別人不會讀到,修改數據時中間狀態的數據(不讓讀)。
19 synchronize 在1.6 之前是個重量級鎖
1.6 之後變得輕量了,
20 鎖的 關係
偏向鎖:在執行過程當中,假如該鎖沒有被其餘線程所獲取,沒有其餘線程來競爭該鎖,那麼持有偏向鎖的線程將永遠不須要進行同步操做也就是說:在此線程以後的執行過程當中,
若是再次進入或者退出同一段同步塊代碼,並再也不須要去進行加鎖或者解鎖操做
重入鎖:若是當前線程已經獲取了這個鎖,那麼再次獲取這個鎖的時候,只是數量加+。並不會由於再去搶鎖。
輕量級鎖: 通常指的自旋鎖,和 自適應自旋鎖 ,在 偏向鎖,沒有偏向本身的時候,偏向鎖 膨脹成爲 輕量級鎖。自旋鎖通常須要指定自旋次數,自適應自旋鎖會自動選擇自旋次數,剛纔回去過鎖的,線程自旋可能多一些,獲取鎖機率 小的線程可能自旋次數少一些,自旋鎖消耗CPU資源。
重量級鎖:輕量級鎖膨脹以後,就升級爲重量級鎖了。重量級鎖是依賴對象內部的monitor鎖來實現的,而monitor又依賴操做系統的MutexLock(互斥鎖)來實現的,因此重量級鎖也被成爲互斥鎖.重量級鎖 阻塞的時候是 wait ,不消耗 cpu 資源,可是阻塞或者喚醒一個線程時,都須要操做系統來幫忙,這就須要從用戶態轉換到內核態,而轉換狀態是須要消耗不少時間的,有可能比用戶執行代碼的時間還要長。
公平鎖和非公平鎖: 獲取鎖的過程是按照順序來的就是公平鎖,搶 鎖的就是非公平鎖。
21 synchronized 是 重量級鎖嗎?
不是,synchronized 活根據 鎖的 場景自動切換 鎖的類型, 同線程銅鎖方法切換偏向鎖-->偏向鎖被別人搶了變成自旋輕量級---> 自旋10次,自動升級成重量級鎖。
22 線程安全的方法
1 synchronized
2 volatile
3 Aotmic 包下面的類
4 Lock 接口的 的實現類。
23 TimeUnit.SECONDS.sleep(1); 能夠是 讓當前線程睡一會 。
public void sleep(long timeout) throws InterruptedException { if (timeout > 0) { long ms = toMillis(timeout); int ns = excessNanos(timeout, ms); Thread.sleep(ms, ns); } }
24 線程等鎖阻塞(block),和 等待(wait) 有什麼區別?
等鎖 block 是被動的等待,在麼搶到鎖的狀況下,被動的wait,不會一直 消耗cpu 資源。而且會在 下一次鎖釋放的時候自動的去搶鎖。
wait 是主動的 等待,必需要拿到監視器 才能 主動的 wait。wait 會主動釋放 鎖,wait 也是不會一直消耗cpu 資源的。 wait 的線程不會由於別的線程釋放鎖,而被喚醒。 須要主動被被 notify 才能 被喚醒。 wait 狀態被喚醒之後,若是搶到鎖進入 運行狀態,若是搶不到 進入 block 狀態。
25 線程狀態 圖
26 生產者消費者問題中 ,synchronized 的 方法讓生產者消費者是用的同一把鎖,也就是說生產的同時 無法消費,消費的同時無法生產? 怎麼解決?
27 ReentrantLock 的 的 wait 和 notify 都是經過 Condition 來實現的, condition 至關於一個組,在這個組上await 的線程被加入這個組,下次叫醒的時候 ,也就叫醒的這個組。
ReentrantLock lock = new ReentrantLock(); lock.lock(); Condition condition = lock.newCondition(); condition.await(); condition.signal(); condition.signalAll(); lock.unlock();
28 終結 java的 容器
1 有哪些支持併發
2 有哪些 List
3 有哪些map
4 有有哪些set
5 有哪些Hash
6 有哪些tree
29 總結 樹 的 類型
1 二叉樹
2 紅黑樹
3 b-tree
4 b+tree
30 線程 join 是,讓當前線程wait ,直到 join的 線程執行完成。
線程執行完成會叫醒全部監事這個線程對象的線程。
主線程 join 一個 子 線程 --> 主線程 獲取了 子線程對象的監視器 -->主線線程 wait --》 子線程執行完畢--->子線程觸發 叫醒全部在在這個join線程對象上 wait 的線程。
31 ThreadLocal 線程局部變量。
ThreadLocal 不真實的存儲數據,在向 ThreadLocal 裏面放東西的時候,它會獲取 當前線程的 一個 成員 變量 map,而後 用 ThreadLocal 做爲這個 map 的 key ,用你 想存到這個 ThreadLocal 的 值做爲 值。 這樣你 不一樣的線程 範圍這個 ThreadLocal 對應的數據的時候,其實讀的是本身 map成員變量裏面 的值。
可是 ThreadLocalMap 並非 map 的子類,可是同時 map 相似的結構。
32 線程通訊類 CountDownLatch( 數量降低鎖 ) ,指定一個數量,這個數量 每次減一,當這個數量變成0 的時候,await 的線程 被喚醒。
只要當 await 的 線程阻塞,沒countdown 一次, state -1 ,直到 state 爲0 ,await 的線程被喚醒 。
33 線程通訊類 CyclicBarrier (循環屏障) ,全部線程走到 屏障點,都會阻塞,直到左後一個線程走到屏障點,而後所有前程一塊兒唄喚醒,一塊兒向前走。
34 線程通訊類 Semaphore(信號量)Semaphore 的做用在與限流 , 指定一個 容許 的令牌數,而後 代碼 執行的時候 獲取一個令牌才能執行,執行完了釋放令牌,保證同一時間 只有 令牌數量個線程在執行。超出的線程被拒絕雜外面
35 線程通訊類 Exchanger (交換者), 兩個線程執行,其中一個線程執行到指定位置 阻塞,而後等待對方也執行到 某個位置 。而後交換數據之後,繼續向下執行。
36 forkjoin 框架,相似於 MapReduce ,先分開計算,而後匯衆 一個結果, 目的是充分的裏用 多核 cpu 資源。
37 關於支持線程安全的容器。
常見的 有Vector, Hashtable,當是 Vector ,Hashtable 處理線安全的方式是 同步方法。 效率並不高。
除了 Vector 意外 還有 Collections.sync開頭的方法,能夠 傳入一個容器,而後 返回 一個 線程 安全的 代理類。 可是這些容器 讀寫 都是 同步的,讀的時候不能寫,寫的時候不能讀。在大量的讀的狀況下,可能效率比較低。
上面這些 線程安全的容器,都是經過 同步 來處理 線程安全的的,上面你的 Vector, Hashtable 還有 Collections.sync____ 方法的代理容器都叫作 同步容器。 同步容器都 幾乎都出現 在 jdk 1.2
38 和同步容器對應 的 併發容器。 幾乎都出如今 jdk 1.5
ConcurrentHashMap 1經過控制鎖的粒度,之前是 鎖 整個容器,如今把一個容器分紅多分 ,每次只鎖定一小段容器
ConcurrentLinkedQueue 使用的樂觀鎖,經過cas 修改數據,若是數據被人修改了就從作。沒有被修改就萬事大吉。 因此 去size的時候是去算的,而不是存起來的一個 數字。
CopyOnWriteArrayList ,CopyOnWriteArraySet 這類容器是在寫的時候複製 一個新的副本,寫完之後在吧 內部容器指向新的 副本。 由於是複製,讀的那個數字 老是不加鎖(相似 innodb 的非鎖定讀 ),因此讀的效率很是高。 寫的時候 效率比較低,適合 寫少讀多。
39 關閉 併發的時候爲何 加鎖問題?
1 讀寫併發 一個線程在寫數據,加了一個,可是這時候 size 還沒增長,另外一個讀線程在讀數據, 讀到元素有 11 個 ,可是 size 是10 ,明顯數據不一致了。相似數據庫的髒讀(讀到別人沒作完,或者說沒提交的數據 )。 因此 讀寫要 互斥。
2 寫寫 併發。 若是沒有鎖 ,原來容器是 10 個,如今兩個線程都在加 數據,變成12 個, 可是 他們會作的事情是吧 11 設置爲 長度, 而且 他們寫入的位置多是同樣的 ,都是11 這個位置。這樣有一個就被 沖掉了(有點像數據庫的丟失更新 )。 因此寫寫 要互斥。
3 讀讀 ,都是讀 ,不會出 併發問題。
40 web 服務器,是經過 tcp 協議實現的。 只是 在堆請求和響應的格式有要求。用 SocketServer 能夠實現一個簡單的 web 服務器。
41 jdk 8 出的 LongAdder 和DoubleAdder 的 原理,相對於 AtomicLong 來講, 好比一個 值是10 ,之前在這個 10 上面 作併發操做,如今把10 分紅幾份,好比 5,3,2,0,0 多線程環境下競爭 的就不是 一把鎖,而是多吧鎖。這樣 幾個值加起來就是 最終的的值。
上面的 5 份 叫作 cell , 總值叫作base。
42 java 8 的 StampedLock ,對 ReentranReadWriterLock 的加強。
StampedLock 的 高效的 緣由, ReentranReadWriterLock 讀寫是互斥的,StampedLock 能夠讀寫並行。從而提升效率,原理在讀的時候,若是被別的線程修改了數據,那麼在讀一次,取到最新的值。
StampedLock 和之前 ReentranReadWriterLock 相似 的 悲觀的鎖,讀寫互斥,也有 樂觀鎖的支持,讀寫,並行。
43 java 僞共享
僞共享的非標準定義爲:緩存系統中是以緩存行(cache line)爲單位存儲的,當多線程修改互相獨立的變量時,若是這些變量共享同一個緩存行,就會無心中影響彼此的性能,這就是僞共享。