- 作者題解鏈接 題解鏈接
原文鏈接 開發者社區> 面試一點通> 正文
1.基於BIO實現的Server端,當建立了100個連接時,會有多少個線程?如果基於NIO,又會是多少個線程? 為什么?
-
BIO:默認,同步阻塞IO流, 每個獲取的連接都需要創建一個新的處理線程,故需要
100
個處理線程,服務端還需要一個1個Accept線程來接受連接請求;所以當連接建立后,將這個連接的IO讀寫放到一個專門的處理線程,所以當建立100個連接時,通常會產生1個Accept線程 + 100個處理線程
。 -
NIO: 同步非阻塞IO流, NIO通過事件來觸發。
a.NIO有Selector,通過while循環檢查或系統調用通知,檢查多個channel的狀態是否可讀寫,所以可以用1個線程
管理多個channel。NIO在處理IO的讀寫時,當從網卡緩沖區讀或寫入緩沖區,這個過程是串行的,所以用太多線程處理IO事件其實也沒什么意義,連接事件由於通常處理比較快,用1個線程去處理就可以。
b.IO事件呢,通常會采用cpu core數+1或cpu core數 * 2
,這個的原因是IO線程通常除了從緩沖區讀寫外,還會做些比較輕量的例如解析協議頭
等,這些是可以並發的,為什么不只用1個線程處理,是因為當並發的IO事件非常多時,1個線程的效率不足以發揮出多core的CPU的能力,從而導致這個地方成為瓶頸,這種在分布式cache類型的場景里會比較明顯,按照這個,也就更容易理解為什么在基於Netty等寫程序時,不要在IO線程里直接做過多動作,而應該把這些動作轉移到另外的線程池里去處理,就是為了能保持好IO事件能被高效處理。 從上面可以看出,對於大多數需要建立大量連接,但並發讀寫並不會同時的場景而言,NIO的優勢是非常明顯的。
2.通常來說基於NIO實現的Server端,會用多少個線程去處理IO事件,為什么?
- 問的是Server端會用多少個線程去處理IO事件
- 2*cpu核數 或 cpu核心數+1 左右 該答案參考自:http://www.sohu.com/a/298857998_494946
3. 一個典型的客戶端集群->LB->服務端集群這樣的結構中,如客戶端采用連接池,長連接的方式,這種設計你覺得可能會出現什么問題?如果客戶端采用的是單個長連接的方式呢?如果有問題,你覺得應該怎么解決?
LB 即LoadBlance(負載均衡,如nginx)
a. 如客戶端采用連接池,長連接的方式,長連接建立后一般不會斷開,也就是某個client會固定請求到某台server,隨着擴容縮容、服務器上下線,會負載不均衡。
b. 解決方案:比較根本的解決方法是在這樣的場景里把中間的LB去掉,換成類似通過服務注冊/發現的機制來解決。重啟負載高的機器.不用LB.
4.cglib和Java的動態代理相比,具體有什么不同?
- 說白了就是SpringAop的兩個實現模式,扔個連接(AOP-snailclimb)[https://snailclimb.gitee.io/javaguide/#/docs/system-design/framework/spring/Spring?id=aop]; 在Java中一般采用JDK動態代理模式,Spring AOP 同時支持 CGLIB、ASPECTJ、JDK動態代理
a.cglib 通過字節碼操作生成代理, 能夠對類做代理
b.JDK通過反射接口生成代理,所以被代理類必須有實現接口。
(不是很懂,先扔着) 5. 在基於Netty實現FrameDecoder時,下面兩種代碼的表現會有什么不同?
第一種的處理方式可以有效減少IO線程池和業務處理線程池的上下文切換,從而提高IO線程的處理效率。
6. 用Executors.newCachedThreadPool創建的線程池,在運行的過程中有可能產生的風險是?
阿里規范不建議這么做, Executors.newCachedThreadPool()會一直創建大量線程直到OOM.
7. new ThreadPoolExecutor(10,100,10,TimeUnit.MILLISECONDS,new LinkedBlockingQueue(10));一個這樣創建的線程池,當已經有10個任務在運行時,第11個任務提交到此線程池執行的時候會發生什么,為什么?
此題考查ThreadPoolExecutor的workQueue [工作、阻塞隊列]的簡單用法, 所以應該第11個任務會加進隊列中,而非開啟第11個線程來處理。
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
workQueue如下:
1) 同步阻塞隊列:core-》max-》超出max后直接拋異常 ,
2) 鏈表阻塞隊列:先core-》隊列-》max-》再拋異常,速度快點;
3)數組,有界;
4)延遲隊列:用於定時任務
5)優先級..
8. 實現一個自定義的ThreadFactory的作用通常是?
- 通常的作用是給線程取名字,便於以后查問題,查日志。
- 用法
public class ThreadFactoryMain implements ThreadFactory{
private final String groupName;
private AtomicInteger nextId = new AtomicInteger(1);
public ThreadFactoryMain(String groupName) {
this.groupName = "ThreadFactoryMain -" + groupName + "-worker-";
}
@Override
public Thread newThread(@NotNull Runnable r) {
String threadName = groupName + nextId.incrementAndGet(); // 名稱中添加一個自增的id
Thread thread = new Thread(null,r,threadName,0);
return thread;
}
}
———————————————
原文鏈接:https://blog.csdn.net/z69183787/article/details/91126481
9. 除了用Object.wait和Object.notifyAll來實現線程間的交互外,你還會常用哪些來實現?
考查JUC包, 也就是Java並發控制的常見機制
java的JUC包里的不管是BlockingQueue的實現,還是各種類似CountDownLatch、CyclicBarrier,都可以用來實現線程的交互。
CountDownLatch: 倒計數門閂,https://www.cnblogs.com/zhazhaacmer/p/11365805.html
CyclicBarrier: 循環柵欄,維持最低的線程並發量
BlockingQueue: 阻塞隊列,常見三類使用方式,阻塞式、超時式、查詢完直接返回式對資源取拿
參考:https://blog.csdn.net/zhou920786312/article/details/100175698
10. 為什么ConcurrentHashMap可以在高並發的情況下比HashMap更為高效?
分段式鎖,以及巧妙的使用final、volatile,
參考:https://snailclimb.gitee.io/javaguide/#/docs/java/Multithread/並發容器總結?id=二-concurrenthashmap
11. AtomicInteger、AtomicBoolean這些類之所以在高並發時高效,共同的原因是?
CAS+volatile機制,例如AtomicInteger 類主要利用 CAS (compare and swap) + volatile 和 native 方法來保證原子操作,從而避免 synchronized 的高開銷,執行效率大為提升。
參考:https://snailclimb.gitee.io/javaguide/#/docs/java/Multithread/Atomic?id=1-atomic-原子類介紹
12. 請合理的使用Queue來實現一個高並發的生產/消費的場景,給些核心的代碼片段。
例如通常可能會借助LinkedBlockingQueue來實現簡單的生產/消費, 或者使用BlockingQueue阻塞隊列來對ftp線程池的線程進行管理,頻繁的存取和釋放.
13. 請實現讓10個任務同時並發啟動,給些代碼片段。
定時任務,QuartZ
或者使用JUC的CyclicBarrier: 循環柵欄,維持最低的線程並發量
14. 在Java程序運行階段,可以用什么命令行工具來查看當前Java程序的一些啟動參數值,例如Heap Size等。
ps -ef | grep java
jinfo -flags
15. 用什么命令行工具可以查看運行的Java程序的GC狀況,請具體寫出命令行格式。
jstat -gc pid 5000
jstat -gcutil [pid] [頻率,例如多少毫秒一次] [多少次]
- 如果已經打開了gc log,可以直接查看gc日志。
- gc log通常怎么打開,具體的命令行參數,一段gc log的解讀