前段時間在慕課網直播上聽小馬哥面試勸退("面試虐我千百遍,Java 併發真討厭"),發現講得東西比本身拿到offer還要高興,因而本身在線下作了一點小筆記,供各位參考。html
課程地址:https://www.bilibili.com/vide...java
源碼文檔地址:https://github.com/mercyblitz...git
本文來自於個人 慕課網手記: 小馬哥Java面試題課程總結,轉載請保留連接 ;)
有哪些方法建立線程?github
僅僅只有new thread這種方法建立線程面試
public class ThreadCreationQuestion { public static void main(String[] args) { // main 線程 -> 子線程 Thread thread = new Thread(() -> { }, "子線程-1"); } /** * 不鼓勵自定義(擴展) Thread */ private static class MyThread extends Thread { /** * 多態的方式,覆蓋父類實現 */ @Override public void run(){ super.run(); } } }
與運行線程方法區分:
java.lang.Runnable() 或 java.lang.Thread類編程
如何經過Java 建立進程?設計模式
public class ProcessCreationQuestion { public static void main(String[] args) throws IOException { // 獲取 Java Runtime Runtime runtime = Runtime.getRuntime(); Process process = runtime.exec("cmd /k start http://www.baidu.com"); process.exitValue(); } }
如何銷燬一個線程?數組
public class ThreadStateQuestion { public static void main(String[] args) { // main 線程 -> 子線程 Thread thread = new Thread(() -> { // new Runnable(){ public void run(){...}}; System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 }, "子線程-1"); // 啓動線程 thread.start(); // 先於 Runnable 執行 System.out.printf("線程[%s] 是否還活着: %s\n", thread.getName(), thread.isAlive()); // 1 // 在 Java 中,執行線程 Java 是沒有辦法銷燬它的, // 可是當 Thread.isAlive() 返回 false 時,實際底層的 Thread 已經被銷燬了 }
Java代碼中是沒法實現的,只能表現一個線程的狀態。安全
而CPP是能夠實現的。多線程
如何經過 Java API 啓動線程?
thread.start();
當有線程 T一、T2 以及 T3,如何實現T1 -> T2 -> T3的執行順序?
private static void threadJoinOneByOne() throws InterruptedException { Thread t1 = new Thread(ThreadExecutionQuestion::action, "t1"); Thread t2 = new Thread(ThreadExecutionQuestion::action, "t2"); Thread t3 = new Thread(ThreadExecutionQuestion::action, "t3"); // start() 僅是通知線程啓動 t1.start(); // join() 控制線程必須執行完成 t1.join(); t2.start(); t2.join(); t3.start(); t3.join(); } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } }
CountDownLatch也能夠實現;
調整優先級並不能保證優先級高的線程先執行。
以上問題請至少提供另一種實現?(1.5)
一、spin 方法
private static void threadLoop() { Thread t1 = new Thread(ThreadExecutionQuestion::action, "t1"); Thread t2 = new Thread(ThreadExecutionQuestion::action, "t2"); Thread t3 = new Thread(ThreadExecutionQuestion::action, "t3"); t1.start(); while (t1.isAlive()) { // 自旋 Spin } t2.start(); while (t2.isAlive()) { } t3.start(); while (t3.isAlive()) { } }
二、sleep 方法
private static void threadSleep() throws InterruptedException { Thread t1 = new Thread(ThreadExecutionQuestion::action, "t1"); Thread t2 = new Thread(ThreadExecutionQuestion::action, "t2"); Thread t3 = new Thread(ThreadExecutionQuestion::action, "t3"); t1.start(); while (t1.isAlive()) { // sleep Thread.sleep(0); } t2.start(); while (t2.isAlive()) { Thread.sleep(0); } t3.start(); while (t3.isAlive()) { Thread.sleep(0); } }
三、while 方法
private static void threadWait() throws InterruptedException { Thread t1 = new Thread(ThreadExecutionQuestion::action, "t1"); Thread t2 = new Thread(ThreadExecutionQuestion::action, "t2"); Thread t3 = new Thread(ThreadExecutionQuestion::action, "t3"); threadStartAndWait(t1); threadStartAndWait(t2); threadStartAndWait(t3); } private static void threadStartAndWait(Thread thread) { if (Thread.State.NEW.equals(thread.getState())) { thread.start(); } while (thread.isAlive()) { synchronized (thread) { try { thread.wait(); // 究竟是誰通知 Thread -> thread.notify(); JVM幫它喚起 // LockSupport.park(); // 死鎖發生 } catch (Exception e) { throw new RuntimeException(e); } } } }
如何中止一個線程?
public class HowToStopThreadQuestion { public static void main(String[] args) throws InterruptedException { Action action = new Action(); // 方法一 Thread t1 = new Thread(action, "t1"); t1.start(); // 改變 action stopped 狀態 action.setStopped(true); t1.join(); // 方法二 Thread t2 = new Thread(() -> { if (!Thread.currentThread().isInterrupted()) { action(); } }, "t2"); t2.start(); // 中斷操做(僅僅設置狀態,而並不是停止線程) t2.interrupt(); t2.join(); } private static class Action implements Runnable { // 線程安全問題,確保可見性(Happens-Before) private volatile boolean stopped = false; @Override public void run() { if (!stopped) { // 執行動做 action(); } } public void setStopped(boolean stopped) { this.stopped = stopped; } } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } }
想要中止一個線程是不可能的,真正的只能中止邏輯。
爲何 Java 要放棄 Thread 的 stop()方法?
Because it is inherently unsafe. Stopping a thread causes it to unlock all the monitors that it has locked.(The monitors are unlocked as the ThreadDeath exception propagates up the stack.) If any of the objects previously protected by these monitors were in an inconsistent state, other threads may now view these objects in an inconsistent state. Such objects are said to be damaged. When threads operate on damaged objects, arbitrary behavior can result. This behavior may be subtle and difficult to detect, or it may be pronounced. Unlike other unchecked exceptions, ThreadDeath kills threads silently; thus, the user has no warning that his program may be corrupted. The corruption can manifest itself at any time after the actual damage occurs, even hours or days in the future.
Why is Thread.stop deprecated?
簡單的說,防止死鎖,以及狀態不一致的狀況出現。
請說明 Thread interrupt()、isInterrupted() 以及 interrupted()的區別以及意義?
Thread interrupt(): 設置狀態,調JVM的本地(native)interrupt0
()方法。
public void interrupt() { if (this != Thread.currentThread()) checkAccess(); synchronized (blockerLock) { Interruptible b = blocker; if (b != null) { interrupt0(); // Just to set the interrupt flag //--> private native void interrupt0(); b.interrupt(this); return; } } interrupt0(); }
isInterrupted(): 調的是靜態方法isInterrupted()
,當且僅當狀態設置爲中斷時,返回false
,並不清除狀態。
public static boolean interrupted() { return currentThread().isInterrupted(true); } /** * Tests whether this thread has been interrupted. The <i>interrupted * status</i> of the thread is unaffected by this method. * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if this thread has been interrupted; * <code>false</code> otherwise. * @see #interrupted() * @revised 6.0 */ public boolean isInterrupted() { return isInterrupted(false); }
interrupted(): 私有本地方法,即判斷中斷狀態,又清除狀態。
private native boolean isInterrupted(boolean ClearInterrupted);
當線程遇到異常時,到底發生了什麼?
線程會掛
public class ThreadExceptionQuestion { public static void main(String[] args) throws InterruptedException { //... // main 線程 -> 子線程 Thread t1 = new Thread(() -> { throw new RuntimeException("數據達到閾值"); }, "t1"); t1.start(); // main 線程會停止嗎? t1.join(); // Java Thread 是一個包裝,它由 GC 作垃圾回收 // JVM Thread 多是一個 OS Thread,JVM 管理, // 當線程執行完畢(正常或者異常) System.out.println(t1.isAlive()); } }
當線程遇到異常時,如何捕獲?
... Thread.setDefaultUncaughtExceptionHandler((thread, throwable) -> { System.out.printf("線程[%s] 遇到了異常,詳細信息:%s\n", thread.getName(), throwable.getMessage()); }); ...
當線程遇到異常時,ThreadPoolExecutor 如何捕獲異常?
public class ThreadPoolExecutorExceptionQuestion { public static void main(String[] args) throws InterruptedException { // ExecutorService executorService = Executors.newFixedThreadPool(2); ThreadPoolExecutor executorService = new ThreadPoolExecutor( 1, 1, 0, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<>() ) { /** * 經過覆蓋 {@link ThreadPoolExecutor#afterExecute(Runnable, Throwable)} 達到獲取異常的信息 * @param r * @param t */ @Override protected void afterExecute(Runnable r, Throwable t) { System.out.printf("線程[%s] 遇到了異常,詳細信息:%s\n", Thread.currentThread().getName(), t.getMessage()); } }; executorService.execute(() -> { throw new RuntimeException("數據達到閾值"); }); // 等待一秒鐘,確保提交的任務完成 executorService.awaitTermination(1, TimeUnit.SECONDS); // 關閉線程池 executorService.shutdown(); } }
Java 線程有哪些狀態,分別表明什麼含義?
NEW: Thread state for a thread which has not yet started.
未啓動的。不會出如今Dump中。
RUNNABLE: Thread state for a runnable thread. A thread in the runnable state is executing in the Java virtual machine, but it may be waiting for other resources from the operating system such as processor.
在虛擬機內執行的。運行中狀態,可能裏面還能看到locked字樣,代表它得到了某把鎖。
BLOCKE: Thread state for a thread blocked waiting for a monitor lock. A thread in the blocked state is waiting for a monitor lock to enter a synchronized block/method or reenter a synchronized block/method after calling {@link Object#wait() Object.wait}.
受阻塞並等待監視器鎖。被某個鎖(synchronizers)給block住了。
WAITING: Thread state for a waiting thread. A thread is in the waiting state due to calling one of the following methods:
<ul> <li>{@link Object#wait() Object.wait} with no timeout</li> <li>{@link #join() Thread.join} with no timeout</li> <li>{@link LockSupport#park() LockSupport.park}</li> </ul>
<p>A thread in the waiting state is waiting for another thread to perform a particular action.
For example, a thread that has called <tt>Object.wait()</tt> on an object is waiting for another thread to call <tt>Object.notify()</tt> or <tt>Object.notifyAll()</tt> on that object. A thread that has called <tt>Thread.join()</tt> is waiting for a specified thread to terminate.
無限期等待另外一個線程執行特定操做。等待某個condition或monitor發生,通常停留在park(), wait(), sleep(),join() 等語句裏。
TIMED_WAITING: Thread state for a waiting thread with a specified waiting time. A thread is in the timed waiting state due to calling one of the following methods with a specified positive waiting time:
<ul> <li>{@link #sleep Thread.sleep}</li> <li>{@link Object#wait(long) Object.wait} with timeout</li> <li>{@link #join(long) Thread.join} with timeout</li> <li>{@link LockSupport#parkNanos LockSupport.parkNanos}</li> <li>{@link LockSupport#parkUntil LockSupport.parkUntil}</li> </ul>
有時限的等待另外一個線程的特定操做。和WAITING的區別是wait() 等語句加上了時間限制 wait(timeout)。
TERMINATED: 已退出的; Thread state for a terminated thread. The thread has completed execution.
如何獲取當前JVM 全部的現場狀態?
方法一:命令
jstack
: jstack用於打印出給定的java進程ID或core file或遠程調試服務的Java堆棧信息。主要用來查看Java線程的調用堆棧的,能夠用來分析線程問題(如死鎖)。
jsp
jsp [option/ -l] pid
方法二:ThreadMXBean
public class AllThreadStackQuestion { public static void main(String[] args) { ThreadMXBean threadMXBean = ManagementFactory.getThreadMXBean(); long[] threadIds = threadMXBean.getAllThreadIds(); for (long threadId : threadIds) { ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId); System.out.println(threadInfo.toString()); } } }
如何獲取線程的資源消費狀況?
public class AllThreadInfoQuestion { public static void main(String[] args) { ThreadMXBean threadMXBean = (ThreadMXBean) ManagementFactory.getThreadMXBean(); long[] threadIds = threadMXBean.getAllThreadIds(); for (long threadId : threadIds) { // ThreadInfo threadInfo = threadMXBean.getThreadInfo(threadId); // System.out.println(threadInfo.toString()); long bytes = threadMXBean.getThreadAllocatedBytes(threadId); long kBytes = bytes / 1024; System.out.printf("線程[ID:%d] 分配內存: %s KB\n", threadId, kBytes); } } }
請說明 synchronized 關鍵字在修飾方法與代碼塊中的區別?
字節碼的區別 (一個monitor,一個synchronized關鍵字)
public class SynchronizedKeywordQuestion { public static void main(String[] args) { } private static void synchronizedBlock() { synchronized (SynchronizedKeywordQuestion.class) { } } private synchronized static void synchronizedMethod() { } }
請說明 synchronized 關鍵字與 ReentrantLock 之間的區別?
相比synchronized,ReentrantLock增長了一些高級功能。主要來講主要有三點:①等待可中斷;②可實現公平鎖;③可實現選擇性通知(鎖能夠綁定多個條件)
談談 synchronized 和 ReentrantLock 的區別
請解釋偏向鎖對 synchronized 與 ReentrantLock 的價值?
偏向鎖只對 synchronized 有用,而 ReentrantLock 已經實現了偏向鎖。
Synchronization and Object Locking
爲何 wait() 和 notify() 以及 notifyAll() 方法屬於 Object ,並解釋它們的做用?
Java全部對象都是來自 Object
wait():
notify():
notifyAll():
爲何 Object wait() notify() 以及 notifyAll() 方法必須 synchronized 之中執行?
wait(): 得到鎖的對象,釋放鎖,當前線程又被阻塞,等同於Java 5 LockSupport 中的park方法
notify(): 已經得到鎖,喚起一個被阻塞的線程,等同於Java 5 LockSupport 中的unpark()方法
notifyAll():
請經過 Java 代碼模擬實現 wait() 和 notify() 以及 notifyAll() 的語義?
當主線程退出時,守護線程會執行完畢嗎?
不必定執行完畢
public class DaemonThreadQuestion { public static void main(String[] args) { // main 線程 Thread t1 = new Thread(() -> { System.out.println("Hello,World"); // Thread currentThread = Thread.currentThread(); // System.out.printf("線程[name : %s, daemon:%s]: Hello,World\n", // currentThread.getName(), // currentThread.isDaemon() // ); }, "daemon"); // 編程守候線程 t1.setDaemon(true); t1.start(); // 守候線程的執行依賴於執行時間(非惟一評判) } }
請說明 ShutdownHook 線程的使用場景,以及如何觸發執行?
public class ShutdownHookQuestion { public static void main(String[] args) { Runtime runtime = Runtime.getRuntime(); runtime.addShutdownHook(new Thread(ShutdownHookQuestion::action, "Shutdown Hook Question")); } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } }
使用場景:Spring 中 AbstractApplicationContext 的 registerShutdownHook()
如何確保主線程退出前,全部線程執行完畢?
public class CompleteAllThreadsQuestion { public static void main(String[] args) throws InterruptedException { // main 線程 -> 子線程 Thread t1 = new Thread(CompleteAllThreadsQuestion::action, "t1"); Thread t2 = new Thread(CompleteAllThreadsQuestion::action, "t2"); Thread t3 = new Thread(CompleteAllThreadsQuestion::action, "t3"); // 不肯定 t一、t二、t3 是否調用 start() t1.start(); t2.start(); t3.start(); // 建立了 N Thread Thread mainThread = Thread.currentThread(); // 獲取 main 線程組 ThreadGroup threadGroup = mainThread.getThreadGroup(); // 活躍的線程數 int count = threadGroup.activeCount(); Thread[] threads = new Thread[count]; // 把全部的線程複製 threads 數組 threadGroup.enumerate(threads, true); for (Thread thread : threads) { System.out.printf("當前活躍線程: %s\n", thread.getName()); } } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } }
請在 Java 集合框架以及 J.U.C 框架中各列舉出 List、Set 以及 Map 的實現?
Java 集合框架: LinkedList、ArrayList、HashSet、TreeSet、HashMap
J.U.C 框架: CopyOnWriteArrayList、CopyOnWriteArraySet、ConcurrentSkipListSet、ConcurrentSkipListMap、ConcurrentHashMap
如何將普通 List、Set 以及 Map 轉化爲線程安全對象?
public class ThreadSafeCollectionQuestion { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); Set<Integer> set = Set.of(1, 2, 3, 4, 5); Map<Integer, String> map = Map.of(1, "A"); // 以上實現都是不變對象,不過第一個除外 // 經過 Collections#sychronized* 方法返回 // Wrapper 設計模式(全部的方法都被 synchronized 同步或互斥) list = Collections.synchronizedList(list); set = Collections.synchronizedSet(set); map = Collections.synchronizedMap(map); } }
如何在 Java 9+ 實現以上問題?
public class ThreadSafeCollectionQuestion { public static void main(String[] args) { // Java 9 的實現 List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // Java 9 + of 工廠方法,返回 Immutable 對象 list = List.of(1, 2, 3, 4, 5); Set<Integer> set = Set.of(1, 2, 3, 4, 5); Map<Integer, String> map = Map.of(1, "A"); // 以上實現都是不變對象,不過第一個除外 // 經過 Collections#sychronized* 方法返回 // Wrapper 設計模式(全部的方法都被 synchronized 同步或互斥) list = Collections.synchronizedList(list); set = Collections.synchronizedSet(set); map = Collections.synchronizedMap(map); // list = new CopyOnWriteArrayList<>(list); set = new CopyOnWriteArraySet<>(set); map = new ConcurrentHashMap<>(map); } }
請說明 List、Vector 以及 CopyOnWriteArrayList 的相同點和不一樣點?
相同點:
Vector、CopyOnWriteArrayList 是 List 的實現。
不一樣點:
Vector 是同步的,任什麼時候候不加鎖。而且在設計中有個 interator ,返回的對象是 fail-fast
;
CopyOnWriteArrayList 讀的時候是不加鎖;弱一致性,while true的時候不報錯。
請說明 Collections#synchromizedList(List) 與 Vector 的相同點和不一樣點?
相同點:
都是synchromized
的實現方式。
不一樣點:
synchromized 返回的是list, 實現原理方式是 Wrapper 實現;
而 Vector 是 List 的實現。實現原理方式是非 Wrapper 實現。
Arrays#asList(Object...)方法是線程安全的嗎?若是不是的話,若是實現替代方案?
public class ArraysAsListMethodQuestion { public static void main(String[] args) { List<Integer> list = Arrays.asList(1, 2, 3, 4, 5); // 調整第三個元素爲 9 list.set(2, 9); // 3 -> 9 // Arrays.asList 並不是線程安全 list.forEach(System.out::println); // Java < 5 , Collections#synchronizedList // Java 5+ , CopyOnWriteArrayList // Java 9+ , List.of(...) 只讀 } }
請至少舉出三種線程安全的 Set 實現?
synchronizedSet、CopyOnWriteArraySet、ConcurrentSkipListSet
在 J.U.C 框架中,存在HashSet 的線程安全實現?若是不存在的話,要如何實現?
不存在;
public class ConcurrentHashSetQuestion { public static void main(String[] args) { } private static class ConcurrentHashSet<E> implements Set<E> { private final Object OBJECT = new Object(); private final ConcurrentHashMap<E, Object> map = new ConcurrentHashMap<>(); private Set<E> keySet() { return map.keySet(); } @Override public int size() { return keySet().size(); } @Override public boolean isEmpty() { return keySet().isEmpty(); } @Override public boolean contains(Object o) { return keySet().contains(o); } @Override public Iterator<E> iterator() { return keySet().iterator(); } @Override public Object[] toArray() { return new Object[0]; } @Override public <T> T[] toArray(T[] a) { return null; } @Override public boolean add(E e) { return map.put(e, OBJECT) == null; } @Override public boolean remove(Object o) { return map.remove(o) != null; } @Override public boolean containsAll(Collection<?> c) { return false; } @Override public boolean addAll(Collection<? extends E> c) { return false; } @Override public boolean retainAll(Collection<?> c) { return false; } @Override public boolean removeAll(Collection<?> c) { return false; } @Override public void clear() { } } }
當 Set#iterator() 方法返回 Iterator 對象後,可否在其迭代中,給 Set 對象添加新的元素?
不必定;Set 在傳統實現中,會有fail-fast
問題;而在J.U.C中會出現弱一致性,對數據的一致性要求較低,是能夠給 Set 對象添加新的元素。
請說明 Hashtable、HashMap 以及 ConcurrentHashMap 的區別?
Hashtable: key、value值都不能爲空; 數組結構上,經過數組和鏈表實現。
HashMap: key、value值都能爲空;數組結構上,當閾值到達8時,經過紅黑樹實現。
ConcurrentHashMap: key、value值都不能爲空;數組結構上,當閾值到達8時,經過紅黑樹實現。
請說明 ConcurrentHashMap 在不一樣的JDK 中的實現?
JDK 1.6中,採用分離鎖的方式,在讀的時候,部分鎖;寫的時候,徹底鎖。而在JDK 1.七、1.8中,讀的時候不須要鎖的,寫的時候須要鎖的。而且JDK 1.8中在爲了解決Hash衝突,採用紅黑樹解決。
HashMap? ConcurrentHashMap? 相信看完這篇沒人能難住你!
請說明 ConcurrentHashMap 與 ConcurrentSkipListMap 各自的優點與不足?
在 java 6 和 8 中,ConcurrentHashMap 寫的時候,是加鎖的,因此內存佔得比較小,而 ConcurrentSkipListMap 寫的時候是不加鎖的,內存佔得相對比較大,經過空間換取時間上的成本,速度較快,但比前者要慢,ConcurrentHashMap 基本上是常量時間。ConcurrentSkipListMap 讀和寫都是log N實現,高性能相對穩定。
請說明 BlockingQueue 與 Queue 的區別?
BlockingQueue 繼承了 Queue 的實現;put 方法中有個阻塞的操做(InterruptedException),當隊列滿的時候,put 會被阻塞;當隊列空的時候,put方法可用。take 方法中,當數據存在時,才能夠返回,不然爲空。
請說明 LinkedBlockingQueue 與 ArrayBlockingQueue 的區別?
LinkedBlockingQueue 是鏈表結構;有兩個構造器,一個是(Integer.MAX_VALUE),無邊界,另外一個是(int capacity),有邊界;ArrayBlockingQueue 是數組結構;有邊界。
請說明 LinkedTransferQueue 與 LinkedBlockingQueue 的區別?
LinkedTransferQueue 是java 7中提供的新接口,性能比後者更優化。
public class priorityBlockingQueueQuiz{ public static void main(String[] args) throw Exception { BlockingQueue<Integer> queue = new PriorityBlockingQueue<>(2); // 1. PriorityBlockingQueue put(Object) 方法不阻塞,不拋異常 // 2. PriorityBlockingQueue offer(Object) 方法不限制,容許長度變長 // 3. PriorityBlockingQueue 插入對象會作排序,默認參照元素 Comparable 實現, // 或者顯示地傳遞 Comparator queue.put(9); queue.put(1); queue.put(8); System.out.println("queue.size() =" + queue.size()); System.out.println("queue.take() =" + queue.take()); System.out.println("queue =" + queue); } }
運行結果:
queue.size() = 3 queue.take() = 1 queue = [8,9]
public class SynchronusQueueQuiz{ public static void main(String[] args) throws Exception { BlockingQueue<Integer> queue = new SynchronousQueue<>(); // 1. SynchronousQueue 是無空間,offer 永遠返回 false // 2. SynchronousQueue take() 方法會被阻塞,必須被其餘線程顯示地調用 put(Object); System.out.pringln("queue.offer(1) = " + queue.offer(1)); System.out.pringln("queue.offer(2) = " + queue.offer(2)); System.out.pringln("queue.offer(3) = " + queue.offer(3)); System.out.println("queue.take() = " + queue.take()); System.out.println("queue.size = " + queue.size()); } }
運行結果:
queue.offer(1) = false queue.offer(2) = false queue.offer(3) = false
SynchronousQueue take() 方法會被阻塞
public class BlockingQueueQuiz{ public static void main(String[] args) throws Exception { offer(new ArrayBlockingQueue<>(2)); offer(new LinkedBlockingQueue<>(2)); offer(new PriorityBlockingQueue<>(2)); offer(new SynchronousQueue<>()); } } private static void offer(BlockingQueue<Integer> queue) throws Exception { System.out.println("queue.getClass() = " +queue.getClass().getName()); System.out.println("queue.offer(1) = " + queue.offer(1)); System.out.println("queue.offer(2) = " + queue.offer(2)); System.out.println("queue.offer(3) = " + queue.offer(3)); System.out.println("queue.size() = " + queue.size()); System.out.println("queue.take() = " + queue.take()); } }
運行結果:
queue.getClass() = java.util.concurrent.ArrayBlockingQueue queue.offer(1) = true queue.offer(2) = true queue.offer(3) = false queue.size() = 2 queue.take() = 1 queue.getClass() = java.util.concurrent.LinkedBlockingQueue queue.offer(1) = true queue.offer(2) = true queue.offer(3) = false queue.size() = 2 queue.take() = 1 queue.getClass() = java.util.concurrent.PriorityBlockingQueue queue.offer(1) = true queue.offer(2) = true queue.offer(3) = false queue.size() = 3 queue.take() = 1 queue.getClass() = java.util.concurrent.SynchronousQueue queue.offer(1) = false queue.offer(2) = false queue.offer(3) = false queue.size() = 0
queue.take() 方法會被阻塞
請說明 ReentranLock 與 ReentrantReadWriteLock 的區別?
jdk 1.5 之後,ReentranLock(重進入鎖)與 ReentrantReadWriteLock 都是可重進入的鎖,ReentranLock 都是互斥的,而 ReentrantReadWriteLock 是共享的,其中裏面有兩個類,一個是 ReadLock(共享,並行,強調數據一致性或者說可見性),另外一個是 WriteLock(互斥,串行)。
請解釋 ReentrantLock 爲何命名爲重進入?
public class ReentrantLockQuestion { /** * T1 , T2 , T3 * * T1(lock) , T2(park), T3(park) * Waited Queue -> Head-> T2 next -> T3 * T1(unlock) -> unpark all * Waited Queue -> Head-> T2 next -> T3 * T2(free), T3(free) * * -> T2(lock) , T3(park) * Waited Queue -> Head-> T3 * T2(unlock) -> unpark all * T3(free) */ private static ReentrantLock lock = new ReentrantLock(); public static void main(String[] args) { // thread[main] -> // lock lock lock // main -> action1() -> action2() -> action3() synchronizedAction(ReentrantLockQuestion::action1); } private static void action1() { synchronizedAction(ReentrantLockQuestion::action2); } private static void action2() { synchronizedAction(ReentrantLockQuestion::action3); } private static void action3() { System.out.println("Hello,World"); } private static void synchronizedAction(Runnable runnable) { lock.lock(); try { runnable.run(); } finally { lock.unlock(); } } }
請說明 Lock#lock() 與 Lock#lockInterruptibly() 的區別?
/** * java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued * 若是當前線程已被其餘線程調用了 interrupt() 方法時,這時會返回 true * acquireQueued 執行完時,interrupt 清空(false) * 再經過 selfInterrupt() 方法將狀態恢復(interrupt=true) */ public static void main(String[] args) { lockVsLockInterruptibly(); } private static void lockVsLockInterruptibly() { try { lock.lockInterruptibly(); action1(); } catch (InterruptedException e) { // 顯示地恢復中斷狀態 Thread.currentThread().interrupt(); // 當前線程並未消亡,線程池可能還在存活 } finally { lock.unlock(); } }
lock() 優先考慮獲取鎖,待獲取鎖成功後,才響應中斷。
lockInterruptibly() 優先考慮響應中斷,而不是響應鎖的普通獲取或重入獲取。
ReentrantLock.lockInterruptibly 容許在等待時由其它線程調用等待線程的 Thread.interrupt 方法來中斷等待線程的等待而直接返回,這時不用獲取鎖,而會拋出一個 InterruptedException。
ReentrantLock.lock 方法不容許 Thread.interrupt 中斷,即便檢測到 Thread.isInterrupted ,同樣會繼續嘗試獲取鎖,失敗則繼續休眠。只是在最後獲取鎖成功後再把當前線程置爲 interrupted 狀態,而後再中斷線程。
請舉例說明 Condition 使用場景?
請使用 Condition 實現 「生產者-消費者問題」?
請解釋 Condition await() 和 signal() 與 Object wait () 和 notify() 的相同與差別?
相同:阻塞和釋放
差別:Java Thread 對象和實際 JVM 執行的 OS Thread 不是相同對象,JVM Thread 回調 Java Thread.run() 方法,同時 Thread 提供一些 native 方法來獲取 JVM Thread 狀態,當JVM thread 執行後,自動 notify()了。
while (thread.isAlive()) { // Thread 特殊的 Object // 當線程 Thread isAlive() == false 時,thread.wait() 操做會被自動釋放 synchronized (thread) { try { thread.wait(); // 究竟是誰通知 Thread -> thread.notify(); // LockSupport.park(); // 死鎖發生 } catch (Exception e) { throw new RuntimeException(e); } }
請說明 CountDownLatch 與 CyclicBarrier 的區別?
CountDownLatch : 不可循環的,一次性操做(倒計時)。
public class CountDownLatchQuestion { public static void main(String[] args) throws InterruptedException { // 倒數計數 5 CountDownLatch latch = new CountDownLatch(5); ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 4; i++) { executorService.submit(() -> { action(); latch.countDown(); // -1 }); } // 等待完成 // 當計數 > 0,會被阻塞 latch.await(); System.out.println("Done"); // 關閉線程池 executorService.shutdown(); } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } }
CyclicBarrier:可循環的, 先計數 -1,再判斷當計數 > 0 時候,才阻塞。
public class CyclicBarrierQuestion { public static void main(String[] args) throws InterruptedException { CyclicBarrier barrier = new CyclicBarrier(5); // 5 ExecutorService executorService = Executors.newFixedThreadPool(5); // 3 for (int i = 0; i < 20; i++) { executorService.submit(() -> { action(); try { // CyclicBarrier.await() = CountDownLatch.countDown() + await() // 先計數 -1,再判斷當計數 > 0 時候,才阻塞 barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }); } // 儘量不要執行完成再 reset // 先等待 3 ms executorService.awaitTermination(3, TimeUnit.MILLISECONDS); // 再執行 CyclicBarrier reset // reset 方法是一個廢操做 barrier.reset(); System.out.println("Done"); // 關閉線程池 executorService.shutdown(); } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } }
請說明 Semaphore(信號量/燈) 的使用場景?
Semaphore 和Lock相似,比Lock靈活。其中有 acquire() 和 release() 兩種方法,arg 都等於 1。acquire() 會拋出 InterruptedException,同時從 sync.acquireSharedInterruptibly(arg:1)
能夠看出是讀模式(shared); release()中能夠計數,能夠控制數量,permits能夠傳遞N個數量。
請經過 Java 1.4 的語法實現一個 CountDownLatch?
public class LegacyCountDownLatchDemo { public static void main(String[] args) throws InterruptedException { // 倒數計數 5 MyCountDownLatch latch = new MyCountDownLatch(5); ExecutorService executorService = Executors.newFixedThreadPool(5); for (int i = 0; i < 5; i++) { executorService.submit(() -> { action(); latch.countDown(); // -1 }); } // 等待完成 // 當計數 > 0,會被阻塞 latch.await(); System.out.println("Done"); // 關閉線程池 executorService.shutdown(); } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } /** * Java 1.5+ Lock 實現 */ private static class MyCountDownLatch { private int count; private final Lock lock = new ReentrantLock(); private final Condition condition = lock.newCondition(); private MyCountDownLatch(int count) { this.count = count; } public void await() throws InterruptedException { // 當 count > 0 等待 if (Thread.interrupted()) { throw new InterruptedException(); } lock.lock(); try { while (count > 0) { condition.await(); // 阻塞當前線程 } } finally { lock.unlock(); } } public void countDown() { lock.lock(); try { if (count < 1) { return; } count--; if (count < 1) { // 當數量減小至0時,喚起被阻塞的線程 condition.signalAll(); } } finally { lock.unlock(); } } } /** * Java < 1.5 實現 */ private static class LegacyCountDownLatch { private int count; private LegacyCountDownLatch(int count) { this.count = count; } public void await() throws InterruptedException { // 當 count > 0 等待 if (Thread.interrupted()) { throw new InterruptedException(); } synchronized (this) { while (count > 0) { wait(); // 阻塞當前線程 } } } public void countDown() { synchronized (this) { if (count < 1) { return; } count--; if (count < 1) { // 當數量減小至0時,喚起被阻塞的線程 notifyAll(); } } } } }
請問 J.U.C 中內建了幾種 ExceptionService 實現?
1.5:ThreadPoolExecutor、ScheduledThreadPoolExecutor
1.7:ForkJoinPool
public class ExecutorServiceQuestion { public static void main(String[] args) { /** * 1.5 * ThreadPoolExecutor * ScheduledThreadPoolExecutor :: ThreadPoolExecutor * 1.7 * ForkJoinPool */ ExecutorService executorService = Executors.newFixedThreadPool(2); executorService = Executors.newScheduledThreadPool(2); // executorService 再也不被引用,它會被 GC -> finalize() -> shutdown() ExecutorService executorService2 = Executors.newSingleThreadExecutor(); } }
請分別解釋 ThreadPoolExecutor 構造器參數在運行時的做用?
/** * Creates a new {@code ThreadPoolExecutor} with the given initial * parameters. * * @param corePoolSize the number of threads to keep in the pool, even * if they are idle, unless {@code allowCoreThreadTimeOut} is set * @param maximumPoolSize the maximum number of threads to allow in the * pool * @param keepAliveTime when the number of threads is greater than * the core, this is the maximum time that excess idle threads * will wait for new tasks before terminating. * @param unit the time unit for the {@code keepAliveTime} argument * @param workQueue the queue to use for holding tasks before they are * executed. This queue will hold only the {@code Runnable} * tasks submitted by the {@code execute} method. * @param threadFactory the factory to use when the executor * creates a new thread * @param handler the handler to use when execution is blocked * because the thread bounds and queue capacities are reached * @throws IllegalArgumentException if one of the following holds:<br> * {@code corePoolSize < 0}<br> * {@code keepAliveTime < 0}<br> * {@code maximumPoolSize <= 0}<br> * {@code maximumPoolSize < corePoolSize} * @throws NullPointerException if {@code workQueue} * or {@code threadFactory} or {@code handler} is null */ public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler);
corePoolSize: 核心線程池大小。這個參數是否生效取決於allowCoreThreadTimeOut變量的值,該變量默認是false,即對於核心線程沒有超時限制,因此這種狀況下,corePoolSize參數是起效的。若是allowCoreThreadTimeOut爲true,那麼核心線程容許超時,而且超時時間就是keepAliveTime參數和unit共同決定的值,這種狀況下,若是線程池長時間空閒的話最終存活的線程會變爲0,也即corePoolSize參數失效。
maximumPoolSize: 線程池中最大的存活線程數。這個參數比較好理解,對於超出corePoolSize部分的線程,不管allowCoreThreadTimeOut變量的值是true仍是false,都會超時,超時時間由keepAliveTime和unit兩個參數算出。
keepAliveTime: 超時時間。
unit: 超時時間的單位,秒,毫秒,微秒,納秒等,與keepAliveTime參數共同決定超時時間。
workQueue: 線程等待隊列。當調用execute方法時,若是線程池中沒有空閒的可用線程,那麼就會把這個Runnable對象放到該隊列中。這個參數必須是一個實現BlockingQueue接口的阻塞隊列,由於要保證線程安全。有一個要注意的點是,只有在調用execute方法是,纔會向這個隊列中添加任務,那麼對於submit方法呢,難道submit方法提交任務時若是沒有可用的線程就直接扔掉嗎?固然不是,看一下AbstractExecutorService類中submit方法實現,其實submit方法只是把傳進來的Runnable對象或Callable對象包裝成一個新的Runnable對象,而後調用execute方法,並將包裝後的FutureTask對象做爲一個Future引用返回給調用者。Future的阻塞特性實際是在FutureTask中實現的,具體怎麼實現感興趣的話能夠看一下FutureTask的源碼。
threadFactory: 線程建立工廠。用於在須要的時候生成新的線程。默認實現是Executors.defaultThreadFactory(),即new 一個Thread對象,並設置線程名稱,daemon等屬性。
handler: 拒絕策略。這個參數的做用是當提交任務時既沒有空閒線程,任務隊列也滿了,這時候就會調用handler的rejectedExecution方法。默認的實現是拋出一個RejectedExecutionException異常。
如何獲取 ThreadPoolExecutor 正在運行的線程?
public class ThreadPoolExecutorThreadQuestion { public static void main(String[] args) throws InterruptedException { // main 線程啓動子線程,子線程的創造來自於 Executors.defaultThreadFactory() ExecutorService executorService = Executors.newCachedThreadPool(); // 以前瞭解 ThreadPoolExecutor beforeExecute 和 afterExecute 可以獲取當前線程數量 Set<Thread> threadsContainer = new HashSet<>(); setThreadFactory(executorService, threadsContainer); for (int i = 0; i < 9; i++) { // 開啓 9 個線程 executorService.submit(() -> { }); } // 線程池等待執行 3 ms executorService.awaitTermination(3, TimeUnit.MILLISECONDS); threadsContainer.stream() .filter(Thread::isAlive) .forEach(thread -> { System.out.println("線程池創造的線程 : " + thread); }); Thread mainThread = Thread.currentThread(); ThreadGroup mainThreadGroup = mainThread.getThreadGroup(); int count = mainThreadGroup.activeCount(); Thread[] threads = new Thread[count]; mainThreadGroup.enumerate(threads, true); Stream.of(threads) .filter(Thread::isAlive) .forEach(thread -> { System.out.println("線程 : " + thread); }); // 關閉線程池 executorService.shutdown(); } private static void setThreadFactory(ExecutorService executorService, Set<Thread> threadsContainer) { if (executorService instanceof ThreadPoolExecutor) { ThreadPoolExecutor threadPoolExecutor = (ThreadPoolExecutor) executorService; ThreadFactory oldThreadFactory = threadPoolExecutor.getThreadFactory(); threadPoolExecutor.setThreadFactory(new DelegatingThreadFactory(oldThreadFactory, threadsContainer)); } } private static class DelegatingThreadFactory implements ThreadFactory { private final ThreadFactory delegate; private final Set<Thread> threadsContainer; private DelegatingThreadFactory(ThreadFactory delegate, Set<Thread> threadsContainer) { this.delegate = delegate; this.threadsContainer = threadsContainer; } @Override public Thread newThread(Runnable r) { Thread thread = delegate.newThread(r); // cache thread threadsContainer.add(thread); return thread; } } }
如何獲取 Future 對象?
submit()
請舉例 Future get() 以及 get(Long,TimeUnit) 方法的使用場景?
超時等待
InterruptedException
ExcutionException
TimeOutException
如何利用 Future 優雅地取消一個任務的執行?
public class CancellableFutureQuestion { public static void main(String[] args) { ExecutorService executorService = Executors.newSingleThreadExecutor(); Future future = executorService.submit(() -> { // 3秒內執行完成,纔算正常 action(5); }); try { future.get(3, TimeUnit.SECONDS); } catch (InterruptedException e) { // Thread 恢復中斷狀態 Thread.currentThread().interrupt(); } catch (ExecutionException e) { throw new RuntimeException(e); } catch (TimeoutException e) { // 執行超時,適當地關閉 Thread.currentThread().interrupt(); // 設置中斷狀態 future.cancel(true); // 嘗試取消 } executorService.shutdown(); } private static void action(int seconds) { try { Thread.sleep(TimeUnit.SECONDS.toMillis(seconds)); // 5 - 3 // seconds - timeout = 剩餘執行時間 if (Thread.interrupted()) { // 判斷而且清除中斷狀態 return; } action(); } catch (InterruptedException e) { } } private static void action() { System.out.printf("線程[%s] 正在執行...\n", Thread.currentThread().getName()); // 2 } }
在 Java 中,volatile 保證的是可見性仍是原子性?
volatile 既有可見性又有原子性(非我及彼),可見性是必定的,原子性是看狀況的。對象類型和原生類型都是可見性,原生類型是原子性。
在 Java 中,volatile long 和 double 是線程安全的嗎?
volatile long 和 double 是線程安全的。
在 Java 中,volatile 底層實現是基於什麼機制?
內存屏障(變量 Lock)機制:一個變量的原子性的保證。
爲何 AtomicBoolean 內部變量使用 int 實現,而非 boolean?
操做系統有 X86 和 X64,在虛擬機中,基於boolean 實現就是用 int 實現的,用哪種實現均可以。虛擬機只有32位和64位的,因此用32位的實現。
在變量原子操做時,Atomic* CAS 操做比 synchronized 關鍵字哪一個更重?
同線程的時候,synchronized 剛快;而多線程的時候則要分狀況討論。
public class AtomicQuestion { private static int actualValue = 3; public static void main(String[] args) { AtomicInteger atomicInteger = new AtomicInteger(3); // if( value == 3 ) // value = 5 atomicInteger.compareAndSet(3, 5); // 偏向鎖 < CAS 操做 < 重鎖(徹底互斥) // CAS 操做也是相對重的操做,它也是實現 synchronized 瘦鎖(thin lock)的關鍵 // 偏向鎖就是避免 CAS(Compare And Set/Swap)操做 } private synchronized static void compareAndSet(int expectedValue, int newValue) { if (actualValue == expectedValue) { actualValue = newValue; } } }
Atomic* CAS 的底層是如何實現?
彙編指令:cpmxchg
(Compare and Exchange)