线程同步有几种方法_架构师面试必问的多线程状态切换及常用方法
架構師面試必問的多線程狀態切換及常用方法
一、問題背景
Java架構師面試中,多線程狀態切換及常用方法幾乎是必問的,要掌握創建多線程的方式和方法。
二、創建多線程的幾種方式
2.1方式一繼承Thread
public class ThreadDemo extends Thread{
public void run()
{
}
}
直接繼承Thread可以創建線程
2.2方式二實現Runnable接口
通過實現Runnable接口,實現創建
public class Thread1 implements Runnable{
public void run()
{
}
}
new Thread(new Thread1()).start();
2.3方式三Callable接口的實現
Callable接口實現了多線程帶有返回值,線程運行可以返回結果值
class ThreadDemo implements Callable {
@Override
public Integer call() throws Exception {
int num= 0;
for (int i = 0; i <= 1000; i++) {
num+= i;
}
return num;
}
}
帶有返回值
ThreadDemo td = new ThreadDemo();
//1.執行 Callable 方式,需要 FutureTask 實現類的支持,用于接收運算結果。
FutureTask result = new FutureTask<>(td);
new Thread(result).start();
//2.接收線程運算后的結果
try {
Integer sum = result.get(); //FutureTask 可用于 閉鎖 類似于CountDownLatch的作用,在所有的線程沒有執行完成之后這里是不會執行的
System.out.println(sum);
System.out.println("------------------------------------");
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
線程result.get就是返回的結果值
三、多線程的生命周期
運行狀態轉換
Java線程具有五種基本狀態
新建狀態(New):當線程對象對創建后,即進入了新建狀態,如:Thread t = new CallThread();
就緒狀態(Runnable):當調用線程對象的start()方法(t.start();),線程即進入就緒狀態。處于就緒狀態的線程,只是說明此線程已經做好了準備,隨時等待CPU調度執行,并不是說執行了t.start()此線程立即就會執行;
運行狀態(Running):當CPU開始調度處于就緒狀態的線程時,此時線程才得以真正執行,即進入到運行狀態。注:就 緒狀態是進入到運行狀態的唯一入口,也就是說,線程要想進入運行狀態執行,首先必須處于就緒狀態中;
阻塞狀態(Blocked):處于運行狀態中的線程由于某種原因,暫時放棄對CPU的使用權,停止執行,此時進入阻塞狀態,直到其進入到就緒狀態,才 有機會再次被CPU調用以進入到運行狀態。根
3.1幾種阻塞狀態
據阻塞產生的原因不同,阻塞狀態又可以分為三種:
1.等待阻塞:運行狀態中的線程執行wait()方法,使本線程進入到等待阻塞狀態;
2.同步阻塞 -- 線程在獲取synchronized同步鎖失敗(因為鎖被其它線程所占用),它會進入同步阻塞狀態;
3.其他阻塞 -- 通過調用線程的sleep()或join()或發出了I/O請求時,線程會進入到阻塞狀態。當sleep()狀態超時、join()等待線程終止或者超時、或者I/O處理完畢時,線程重新轉入就緒狀態。
3.2結束生命周期
死亡狀態(Dead):線程執行完了或者因異常退出了run()方法,該線程結束生命周期。
3.3就緒、運行與死亡狀態之間的切換
當多線程獲得了處理器CPU資源,就從就緒狀態躍遷為運行狀態;
當多線程調用yield方法,多線程就從運行狀態變為就緒狀態;
當一個線程執行完了或遇到Exception就變為死亡狀態;
四、如何中止線程
使用Java原子變量volatile表示,一次只有一個線程能使用此狀態。如下,當volatile變量改變時,可以終止線程
public class MainThread extends Thread {
//volatile修飾符用來保證其它線程讀取的總是該變量的最新的值
public volatile boolean exit = false;
@Override
public void run() {
ServerSocket mainSocket = new ServerSocket(1090);
while(!exit){
mainSocket.accept(); //阻塞等待
...
}
}
使用stop可以終止線程,但是會造成線程不安全。stop會立刻停止run方法中的數據,包括catch和finally中的語句塊,會造成異常,導致文件數據庫得不到關閉。
使用interupt中斷線程,是線程安全的,不會立即結束線程,只是通知線程,提前告知:
try {
InterruptFGThread1 t = new InterruptFGThread1();
t.start();
Thread.sleep(1000);
t.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
public void run() {
super.run();
for(int i = 0; i <= 5000; i++) {
System.out.println("i=" + i);
}
}
設置了中斷狀態,但中斷不會起作用,此時得用到isInterrupted方法來終止線程
public void run() {
f();
for(int i = 0; i <= 5000; i++) {
//判斷是否被中斷
if(Thread.currentThread().isInterrupted()){
//處理中斷邏輯
break;
}
System.out.println("i=" + i);
}
當線程感知到interrupt中斷時,會通過isInterrupted()進行終止程序控制操作,可以控制。
Interrupt與Stop相比,線程安全,程序可控,并不實際終止線程。
五、sleep與wait方法的區別
sleep來自Thread類,和wait來自Object類。
sleep不出讓系統資源;wait是進入線程等待池等待,出讓系統資源,其他線程可以占用CPU。一般wait不會加時間限制,因為如果wait線程的運行資源不夠,再出來也沒用,要等待其他線程調用notify/notifyAll喚醒等待池中的所有線程,才會進入就緒隊列等待OS分配系統資源。
5.1wait方法只限于同步鎖狀態中
wait用于Object對象,發出wait后。可以通過notify和notifyAll來喚醒線程。但是只能用于synchronized同步塊中
synchronized(obj){
obj.notify()
//或者obj.wait()
}
5.2異常
sleep方法必須捕獲異常
try{
sleep(500);
}catch(Exception e)
{
log.error();
}
wait方法無需捕獲異常,作用于Object
使用wait后,采用notify與notifyAll喚醒從而變為運行狀態
多線程常見于面試中,一定要注意。
總結
以上是生活随笔為你收集整理的线程同步有几种方法_架构师面试必问的多线程状态切换及常用方法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 归并排序--Java
- 下一篇: html调用app store,iOS