四、多線程/并發
1.如何創建線程?如何保證線程安全?
創建線程有三種方法:
-
MyThread
繼承Thread
, 然后new MyThread().start()
-
MyThread
實現Runnable
,然后new Thread(new MyThread()).start()
-
MyThread
實現Callable
,然后new Thread(new MyThread()).start()
保證線程安全 - 同步代碼塊,
synchronized(obj){ ... }
- 同步方法,
public synchronized void draw(int money){ ... }
- 同步鎖,
Lock
,如ReadWriteLock
,ReentrantLock
2.如何實現一個線程安全的數據結構
- 使用鎖,同步代碼塊,同步方法
- 使用
Collections.synchronizedMap()
等內置方法 - 使用標志位,進入方法了就置true,出去了就置false,這樣子保證一個方法中只有一個在執行(有爭議)
3.如何避免死鎖
- 代碼邏輯清晰
- suspend方法容易死鎖,不要使用
4.Volatile關鍵字的作用?
5.HashMap在多線程環境下使用需要注意什么?為什么?
HashMap是線程的容器,在多線程環境中如果和多個線程都有交互,那么應該將其包裝成線程安全的容器。Collections.synchronizedMap()
6.Java程序中啟動一個線程是用run()還是start()?
new Thread().start()
@Override
public void run(){...}
7.什么是守護線程?有什么用?
Daemon的作用是為其他線程的運行提供服務,比如說GC線程。其實User Thread線程和Daemon Thread守護線程本質上來說去沒啥區別的,唯一的區別之處就在虛擬機的離開:如果User Thread全部撤離,那么Daemon Thread也就沒啥線程好服務的了,所以虛擬機也就退出了。
8.什么是死鎖?如何避免
當兩個線程相互等待對方釋放同步監視器時就會發生死鎖。
- 代碼邏輯清晰
- suspend方法容易死鎖,不要使用
9.線程和進程的差別是什么?
操作系統可以同時執行多個任務,每個任務就是進程;進程可以同時多個任務,每個任務就是線程。一個程序運行后至少有一個進程,一個進程里可以包含多個線程,但至少包含一個線程。
10.Java里面的Threadlocal是怎樣實現的?
每個線程中都保留一個變量值的副本,使得每個線程都可以獨立改變自己的副本,而不會和其他線程的副本沖突。
11.ConcurrentHashMap的實現原理是?
分段加鎖,比起簡單粗暴的Hashtable
效率高很多
12.sleep和wait區別
wait等待別人notify。
sleep自己到時間或者被中斷了就繼續。
13.notify和notifyAll區別
notify喚醒一個在此同步監視器上等待的線程,選擇是任意的
notifyAll喚醒所有在此同步監視器上等待的線程。
14.volatile關鍵字的作用
簡單說來就是,讓其他高速緩存直接刷到內存中去。
參考這篇文章
15.ThreadLocal的作用與實現
每個線程中都保留一個變量值的副本,使得每個線程都可以獨立改變自己的副本,而不會和其他線程的副本沖突。
16.兩個線程如何串行執行
在A線程的run方法中啟動B線程。
在A線程中join B線程
17.上下文切換是什么含義
上下文切換是存儲和恢復CPU狀態的過程,它使得線程執行能夠從中斷點恢復執行。上下文切換是多任務操作系統和多線程環境的基本特征。
18.可以運行時kill掉一個線程嗎?
JPS能不能在進程里找到這個線程,然后kill了,不建議強制kill一個線程,最好是讓其自然死亡,或者Interrupt它
19.什么是條件鎖、讀寫鎖、自旋鎖、可重入鎖?
從Java5開始,Java提供了一種功能更強大的線程同步機制,通過顯示定義同步鎖對象來實現同步,在這種機制下,同步鎖由Lock對象充當。
Lock
和ReadWriteLock
是兩個接口,ReentrantLock
(可重入鎖)是Lock
的實現類,ReentrantReadWriteLock
是ReadWriteLock
的實現類。
ReentrantLock
鎖具有可重入性,也就是說,一個線程可以對已被加鎖的ReentrantLock
鎖再次加鎖,內部會維持一個計數器來追蹤lock()
方法的潛逃調用。
20.線程池ThreadPoolExecutor的實現原理?
一個線程從被提交(submit)到執行共經歷以下流程:
- 線程池判斷核心線程池里是的線程是否都在執行任務,如果不是,則創建一個新的工作線程來執行任務。如果核心線程池里的線程都在執行任務,則進入下一個流程
- 線程池判斷工作隊列是否已滿。如果工作隊列沒有滿,則將新提交的任務儲存在這個工作隊列里。如果工作隊列滿了,則進入下一個流程。
- 線程池判斷其內部線程是否都處于工作狀態。如果沒有,則創建一個新的工作線程來執行任務。如果已滿了,則交給飽和策略來處理這個任務。
- 線程池在執行excute方法時,主要有以下四種情況
1 如果當前運行的線程少于corePoolSize,則創建新線程來執行任務(需要獲得全局鎖)
2 如果運行的線程等于或多于corePoolSize ,則將任務加入BlockingQueue
3 如果無法將任務加入BlockingQueue(隊列已滿),則創建新的線程來處理任務(需要獲得全局鎖)
4 如果創建新線程將使當前運行的線程超出maxiumPoolSize,任務將被拒絕,并調用RejectedExecutionHandler.rejectedExecution()方法。
目錄列表
一、數據結構與算法基礎
二、Java基礎
三、JVM
四、多線程/并發
五、Linux使用與問題分析排查
六、框架使用
七、數據庫相關
八、網絡協議和網絡編程
九、Redis等緩存系統/中間件/NoSQL/一致性Hash等
十、設計模式與重構
本文是針對知乎文章《成為Java頂尖程序員,先過了下面問題》的解答