您好,登錄后才能下訂單哦!
關(guān)于JAVA并發(fā)編程的那些事。
記錄一下自己在學(xué)習(xí)并發(fā)編程的時候遇到的一些問題。方便自己查閱。
1.實(shí)現(xiàn)Runnable接口好在哪里?
從代碼架構(gòu)角度:具體的任務(wù)(run方法)應(yīng)該和“創(chuàng)建和運(yùn)行線程的機(jī)制”(Thread類)相解耦。
使用繼承Thread類的方式的話,那么每次想新建一個任務(wù),只能新建一個獨(dú)立的線程們這樣做的話損耗會比較大(比如重新創(chuàng)建一個線程,執(zhí)行完畢
以后再銷毀等。如果實(shí)際工作內(nèi)容,也就是run()函數(shù)里只是簡單的打印一行文字的話,那么可能線程的實(shí)際工作內(nèi)容還不如損耗來的大)。如果使用Runnable和線程池,就可以大大減少這樣的損耗。
繼承Thread類以后,由于Java語音不支持多繼承,這樣就無法再繼承其他的類,限制了可擴(kuò)展性。
2.“實(shí)現(xiàn)Runnable接口并傳入Thread類”和繼承Thread類,然后重寫run()方法的本質(zhì)對比?
在實(shí)現(xiàn)多線程的本質(zhì)上,并沒有區(qū)別,都是最終調(diào)用了start()方法來新建線程。這兩個方法最主要的區(qū)別是在于run()的了內(nèi)容來源。
//以下代碼在Thread類中,可以找到
@override
public void run(){
if(target!=null){
target.run();
}
}
實(shí)現(xiàn)Runnable接口最終調(diào)用target.run()。繼承Thread類,是整個run()方法都被重寫。
3.有幾種創(chuàng)建線程的方法?
從不同的角度看,會有不同的答案。
典型答案是兩種,分別是實(shí)現(xiàn)Runnable接口和繼承Thread類
但是我們看原理,其實(shí)Thread類實(shí)現(xiàn)了Runnable接口,并且Thread類的run()方法,會發(fā)現(xiàn)其實(shí)哪兩種本質(zhì)是一樣的。兩種方式在實(shí)現(xiàn)多線程的本質(zhì)上,并沒有區(qū)別。
還有其他實(shí)現(xiàn)線程的方法,例如線程池等,他們也能新建線程,但是細(xì)看源碼,并沒有逃過本質(zhì),也是實(shí)現(xiàn)Runnable接口和繼承Thread類。
結(jié)論:我們只能那個通過新建Thread類這一種方式來出創(chuàng)建線程,但是類里面的run方法有兩種方式實(shí)現(xiàn),的一種是重寫run()方法,第二種是實(shí)現(xiàn)Runnable接口的run方法,然后再把該Runnable實(shí)例傳給Thread類。除此之外,從表面上看線程池、定時器等工具類也能創(chuàng)建線程,但是他們的本質(zhì)也是逃不過剛說的范圍。
4.start方法的執(zhí)行流程是什么?
檢查線程狀態(tài),只有New狀態(tài)的線程才能繼續(xù),否則會拋出IllegalThreadStateException。運(yùn)行或已經(jīng)結(jié)束的線程都不能再啟動。
(這里可引申面試題:一個線程調(diào)用兩次start()方法會出現(xiàn)什么情況?why?解題思路就是1異常2線程狀態(tài))
被加入線程組
調(diào)用start0()方法啟動線程。
Tips:start方法是被synchronized修飾的方法,可以保證線程安全。并且由JVM產(chǎn)檢的main方法和system組線程,并不會通過start來啟動。
5.Java中如何正確停止線程?
用interrupt來請求停止線程。(僅僅是通知到被終止的線程應(yīng)該停止運(yùn)行了,被停止的線程自身擁有決定權(quán)。這是一個協(xié)作機(jī)制。)
想要停止線程,需要請求方,被停止方,子方法被調(diào)用方相互配合才行:
– a.作為被停止方:每次循環(huán)或者適時檢測中斷信號,并且在可能拋出interruptedException的地方處理該中斷信號;
– b.請求方:發(fā)出中斷信號。
– c.子方法調(diào)用方:要注意優(yōu)先在方法拋出InterruptedException,或者在檢查到中斷信號時,再次設(shè)置中斷狀態(tài)。(Catch里會重置這個狀態(tài),需要再次設(shè)置中斷狀態(tài),否則就被吞了)
最后,錯誤的方法: stop、suspend方法已經(jīng)被廢棄(stop容易造成臟數(shù)據(jù))
volatile的boolean標(biāo)記,無法處理長時間阻塞的情況(例如,生產(chǎn)者消費(fèi)者模式中,就存在這樣的情況,生產(chǎn)者生產(chǎn)速度快,消費(fèi)者消費(fèi)速度慢,生產(chǎn)者隊列阻塞)
6.無法響應(yīng)中斷時如何停止線程?
如果線程阻塞是由于調(diào)用了wait()、sleep()或者join()方法,你可以中斷線程,通過拋出interruptedException異常來喚醒該線程,但是對于不能響應(yīng)InterruptedException的阻塞,并沒有一個通用的解決方案。但是我們可以利用特定的其他可以響應(yīng)中斷的方法,比如Reentrantlock.lockInterruptibly(),比如關(guān)閉套接字使線程立即返回等方法來達(dá)到目的。答案有很多種,因?yàn)橛泻芏嘣驎斐删€程阻塞,所以針對不同的情況,喚起的方法不同??偨Y(jié)來說,如果不支持響應(yīng)中斷,就要用特定的方法來喚起。根據(jù)不同的類,調(diào)用不同的方法。
.可以響應(yīng)中斷而拋出InterruptedException的常見方法?
Object.wait()/wait(long)/wait(long,int)
Thread.sleep()/sleep(long,int)
Thread.join()/join(long)/join(long,int)
java.util.concurrent.BlockingQueue.take()/put(E)
java.util.concurrent.locks.Lock.lockInterruptibly()
java.util.concurrent.countDownLatch.await()
java.util.concurrent.cyclicBarrier.await()
java.uti.concurrent.Exchanger.exchange(V)
java.nio.channels.InterruptibleChannel相關(guān)方法
java.nio.channels.Selector相關(guān)方法
7.判斷線程是否被中斷的方法有哪些?
static boolean interrupted() //返回之后,清除標(biāo)記
boolean isInterrupted() //不清除
//Tip:注意Thread.intterupted()的目的對象時“當(dāng)前線程”,而不管本方法來自于哪個對象
8.線程都有哪幾個狀態(tài)?
6種狀態(tài)
New:已經(jīng)創(chuàng)建,但是還沒有執(zhí)行start方法
Runnable:一旦調(diào)用了start方法后,就一定會到Runnable,java中的Runnable對應(yīng)操作系統(tǒng)里的ready和running狀態(tài)
Blocked:當(dāng)一個線程進(jìn)入同步代碼塊(被Synchronized修飾),該鎖被其他線程拿走了。線程變成Blocked。只有Synchronized才能rag線程進(jìn)入這個狀態(tài)。
Wating:等待
Time_waiting:計時等待
Terminated:死亡
9.線程相關(guān)方法
Thread類:
sleep相關(guān)、
join() :等待其他線程執(zhí)行完畢
yield相關(guān) :放棄已經(jīng)獲得的CPU資源
currentThread:獲取當(dāng)前線程的引用
start,run方法:啟動線程相關(guān)
interrupt相關(guān)::中斷線程
stop() suspend() resuem()相關(guān) :已經(jīng)廢棄
Object類:
wait():讓線程短暫休息
notify/notifyAll 相關(guān) :喚醒線程
10.wait/notify/notifyAll的作用和用法?
階段 方法和作用
阻塞階段 調(diào)用wait()方法
喚醒階段 1、另一個線程調(diào)用這個對象的notify方法且剛好被喚醒的是本線程。2、另外一個線程調(diào)用這個對象的notifyAll方法。3、過了wait(long timeOut)的規(guī)定的超時時間,如果傳入0就是永久等待。4、線程自身調(diào)用了intterrupt()
遇到中斷 wait階段遇到中斷會拋出異常,并且釋放掉鎖
11.wait、notify、notifyAll特點(diǎn)?性質(zhì)?
用必須先擁有monitor鎖。(Synchronized)
notify只能喚醒一個線程。
屬于Object類,是所有對象的父類,所以任何對象都能調(diào)用,并且都是native final的。
類似Condition的功能
同時持有多個鎖的情況。釋放鎖,只能釋放現(xiàn)在wait所對應(yīng)的對象的那把鎖。
12.用wait/notify方法實(shí)現(xiàn)消費(fèi)者生產(chǎn)者模式?
package com.yue.consumer;
import java.util.Date;
import java.util.LinkedList;
//使用wait notify實(shí)現(xiàn)一個生產(chǎn)者消費(fèi)者模式
public class ProducerConsumerModel {
public static void main(String[] args) {
EventStorage storage = new EventStorage();
Producer producer = new Producer(storage);
Consumer consumer =new Consumer(storage);
new Thread(producer).start();
new Thread(consumer).start();
}
}
class Producer implements Runnable{
private EventStorage storage;
public Producer(EventStorage storage) {
this.storage = storage;
}
public void run() {
for (int i= 0;i<100;i++){
storage.put();
}
}
}
class Consumer implements Runnable{
private EventStorage storage;
public Consumer(EventStorage storage) {
this.storage = storage;
}
public void run() {
for (int i=0;i<100;i++){
storage.take();
}
}
}
class EventStorage{
private int maxSize;
private LinkedList storage;
public EventStorage() {
this.maxSize = 10;
this.storage = new LinkedList();
}
public synchronized void put(){
while(storage.size() == maxSize){
try {
wait();
}catch (InterruptedException e){
e.printStackTrace();
}
}
storage.add(new Date());
System.out.println("倉庫里有了"+storage.size()+"個產(chǎn)品");
notify();
}
public synchronized void take(){
while(storage.size()==0){
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}鄭州婦科在線醫(yī)生 http://www.zzkdfk120.com/
System.out.println("拿到了"+storage.poll()+",現(xiàn)在倉庫還剩下"+storage.size());
notify();
}
}
13.為什么wait需要在同步代碼塊中使用,而sleep不需要?
為了讓線程之間的通信更加可靠,防止死鎖或者永久等待。如果不放在synchronized中的話,那么久有可能在線程執(zhí)行到一個wait之前,切換到另外一個線程。而另外一個線程執(zhí)行完notify后,切換回來。這樣就沒有線程去喚醒它了。而sleep是針對自己線程的,和其他線程的關(guān)系不大。
14.為什么wait、notify和notifyAll定義在object類中,sleep定義在Thread類中?
因?yàn)樵贘ava中,這三個操作都是所級別的操作,而鎖是針對對象的。鎖是綁定到對象中,而不是綁定到線程。
15.wait是屬于Object對象的,那調(diào)用Thread.wait()會出現(xiàn)什么情況?
會導(dǎo)致流程問題。因?yàn)樵诰€程退出的時候,會自動執(zhí)行一個notify
16.sleep方法的作用?
作用:讓線程在預(yù)期執(zhí)行,其他時候不占用CPU資源
特點(diǎn):Sleep方法可以讓線程進(jìn)入waiting狀態(tài),并且不占用CPU資源,但是不釋放鎖(包含synchronized和lock),直到規(guī)定時間之后在執(zhí)行。休眠期內(nèi)如果被中斷,會拋出異常并清除中斷狀態(tài)。
Tips:
//這兩種方式其實(shí)都是一樣的,但是第一種比較優(yōu)雅
TimeUnit.SECONDS.sleep()
Thread.sleep()
17.wait和sleep方法的異同?
相同:
Wait和sleep方法都可以使線程阻塞,對應(yīng)線程狀態(tài)是Waiting或Time_Waiting。
wait和sleep方法都可以響應(yīng)中斷Thread.interrupt()。
不同點(diǎn):
wait方法的執(zhí)行必須在同步方法中進(jìn)行,而sleep則不需要。
在同步方法里執(zhí)行sleep方法時,不會釋放monitor鎖,但是wait方法會釋放monitor鎖。
sleep方法短暫休眠之后會主動退出阻塞,而沒有指定時間的 wait方法則需要被其他線程中斷后才能退出阻塞。
wait()和notify(),notifyAll()是Object類的方法,sleep()和yield()是Thread類的方法
TIps:Java設(shè)計的時候把對象都當(dāng)成一把鎖,對象頭中都有鎖的狀態(tài)
18. join()方法的作用?
作用:因?yàn)樾戮€程加入我們,所以得等他執(zhí)行完再出發(fā);通常是,主線程等待子線程,而不是子線程等待主線程。例如一般是main等thread1執(zhí)行完。join遇到中斷時候是主線程被中斷,是主線程拋出異常;在join期間狀態(tài)是waiting
Tips:CountDownLatch或CyclicBarrier類封裝了join。建議使用封裝好的工具
源碼:
調(diào)用了thread.wait方法,而這方法會在thread執(zhí)行結(jié)束后悔自動調(diào)用notify。這也是為什么不要使用這個的原因。
19.yield()方法的作用?
作用:釋放cpu時間片,線程狀態(tài)是runnable,而不是bolcked,也不是waiting。常用于并發(fā)包中。
yield 和 sleep:
sleep期間屬于被阻塞,yield不是阻塞,隨時是runable狀態(tài)。而且JVM是不保證遵循的。
20.線程都有哪些屬性?
編號(ID):每個編程都有自己的ID,用于標(biāo)識不同的線程。
名稱(Name):作用是讓用戶或者程序員在開發(fā)、調(diào)試或運(yùn)行過程中,更容易區(qū)分每個不同的線程,定位問題等。
是否是守護(hù)線程(isDeamon) :true代表該線程是守護(hù)線程,false代表線程是非守護(hù)線程,也就是用戶線程。
– 作用:給用戶線程提供服務(wù)。例如垃圾處理器。
– 特性:線程類型默認(rèn)繼承自父線程。被誰啟動,一般都是JVM啟動的,(main)。不影響JVM退出。
– 區(qū)別:整體無區(qū)別。唯一區(qū)別在于是否影響JVM退出。
優(yōu)先級(Priority):優(yōu)先級這個屬性的目的是告訴線程調(diào)度器,用戶希望哪些線程相對多運(yùn)行,哪些少運(yùn)行。
– 10個優(yōu)先級,默認(rèn)5.
– 程序的設(shè)計不應(yīng)該優(yōu)先級
21.實(shí)際工作中,如何全局處理異常?為什么要全局處理?不處理行不行?
主線程可以啟動發(fā)現(xiàn)異常,子線程卻不行。比如主線程操作非常多,子線程雖然報異常,但是日志太多,不好發(fā)覺。并且在子線程發(fā)現(xiàn)問題后,并沒有停止執(zhí)行。
子線程異常無法用傳統(tǒng)方法捕獲。
不能直接捕獲的后果,可能線程掛掉打印堆棧。用了全局處理之后提高健壯性,可以在發(fā)生未知異常后,重啟線程或者通知程序員等。
22.關(guān)于線程異常的兩種處理方法?
方案一:手動在每個run()方法里進(jìn)行try catch (不推薦)
方案二:利用UncaughtExceptionHanler接口
– void uncaughtException (Thread t,Throwable e)
– 異常處理器的調(diào)用策略:首先會檢查父線程,一直往上找,查找是否有人能夠處理。
– 實(shí)現(xiàn):
首先,自定義一個類實(shí)現(xiàn)Thread.UncaughtExceptionHandler。重寫內(nèi)置方法uncaughtException(Thread t,Throwable e)方法,里面寫自己的邏輯。(Tips:可以通過構(gòu)造方法來傳模塊名字)
然后,在需要配置的類中setDefaultUncaughtExceptionHandler(new HandlerInstance);
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。