1.并發和并行有什么區別
并發是指多核CPU上的多任務處理,多個任務在同一時刻真正同時執行。
并行是指單核CPU上的多任務處理,多個任務在同一時間段內交替執行,通過時間片輪轉實現交替執行,用于解決IO密集型瓶頸。
如何理解線程安全呢?
如果一段代碼塊或者一個方法被多個線程同時并發執行,其中的共享內容還能夠有序正確的執行,就說明是線程安全的。
線程安全滿足三個要素:
原子性:一個操作如果執行要么成功,要么失敗,不會只執行一部分。
可以通過同步關鍵字 synchronized 或原?操作,如 AtomicInteger 來保證原子性。
可見性:當一個線程修改共享變量后,其他變量可以及時看到。
可以通過 volatile 關鍵字來保證可?性。
有序性:確保線程不會因為死鎖、饑餓、活鎖等問題導致無法繼續執行。
2.線程和進程的區別
進程:進程簡單點說就是我們的電腦上啟動的一個個應用,它是操作系統分配資源的最小單位。
線程:線程是進程中的獨立執行單元。多個線程可以共享一個進程的資源,如內存,每個線程都有自己獨立的棧和寄存器。
線程間是如何進行通信的?
原則上可以通過消息傳遞和共享內存兩種?法來實現。
Java 采用的是共享內存的并發模型。 這個模型被稱為 Java 內存模型,簡寫為 JMM,它決定了?個線程對共享變量的寫入,何時對另外?個線程可見。 當然了,本地內存是 JMM 的?個抽象概念,并不真實存在。
用一句話來概括就是:共享變量存儲在主內存中,每個線程的私有本地內存,存儲的是這個共享變量的副本。
線程A和線程B之間要通信,有兩個步驟:
1、線程A把本地內存A中的共享變量副本刷新到主內存中。
2、線程 B 到主內存中讀取線程 A 刷新過的共享變量,再同步到??的共享變量副本中。
3.線程有幾種創建方式?
有三種,繼承Thread類、實現Runnable接口、實現Callable接口。
第一種需要重寫父類的run()方法,并調用start()方法啟動線程。
class ThreadTask extends Thread {
????????public void run() {
????????????????System.out.println("我是繼承Thread類創建多線程");
??????}
????????public static void main(String[] args) {
????????????????ThreadTask task = new ThreadTask();
????????????????task.start();
????????}
}
這種繼承Thread類創建線程的方法缺點是,如果該類已經繼承其他類,就不能繼承Thread類了,因為Java是不支持多重繼承的。
第二種實現Runnable接口并重寫run()方法, 并且將實現類的對象作為參數傳遞給Thread類的構造方法,最后調用start()方法開啟線程。
class RunnableTask implements Runnable {
public void run() {
????????System.out.println("我是通過實現Runnable接口創建多線程");
}
public static void main(String[] args) {
????????????????RunnableTask task = new RunnableTask();
????????????????Thread thread = new Thread(task);
???????????????? thread.start();
????????}
}
這種方法的優點是避免了Java的單繼承的限制,且更加符合面向對象的編程思想,因為Runnable接口將任務代碼和線程控制的代碼解耦了。
第三種實現Callable接口并重寫call()方法,創建FutureTask對象,參數為實現Callable接口的示例對象,然后創建Thread對象,參數為FutureTask對象,最后調用start()方法啟動線程。
class CallableTask implements Callable {
public String call() {
????????return "我是通過實現Callable接口來創建多線程";
?}
public static void main(String[] args) throws ExecutionException, InterruptedException {
????????CallableTask task = new CallableTask();
????????FutureTask futureTask = new FutureTask<>(task);
????????Thread thread = new Thread(futureTask);
????????thread.start();
????????System.out.println(futureTask.get());
????????}
} ?
這種?法的優點是可以獲取線程的執?結果。
啟動一個Java程序,你能說說里面有哪些線程嗎??
首先是main線程,這是程序執行的入口
垃圾回收線程,它是一個后臺線程,負責回收不再使用的對象
編譯器線程,如JIT,負責把一部分熱點代碼編譯后放到codeCache
調用start()方法可以執行run()方法,為什么不直接調用run()方法?
當調用start()方法時,會創建一個新的線程,并異步執行run()方法。
如果直接調用run()方法,就是一個普通方法的調用,不會創建新的線程,會在當前線程下直接執行,也就無法達到多線程的目的了。
也就是說,調用start() ?法會通知 JVM,去調?底層的線程調度機制來啟動新線程。
調用start()方法后,線程處于就緒狀態,等待底層操作系統調度,一旦調度執行,就會執行run()方法中的內容。
4.線程中有哪些常用的調度方法?
如start()啟用線程等待操作系統調度執行,sleep()讓線程休眠一段時間,wait()會讓當前線程進入等待,notify()會喚醒一個正在等待的線程。
講講wait和notify方法
當線程 A 調?共享對象的 wait() ?法時,線程 A 會被阻塞掛起,直到線程 B 調用了共享對象的 notify() 方法或者 notifyAll() 方法;
其他線程調用線程A的interrupt() ?法,導致線程 A 拋出 InterruptedException 異常。
線程 A 調用共享對象的 wait(timeout) ?法后,沒有在指定的 timeout 時間內被其它線程喚醒,那么這個方法會 因為超時而返回。
當線程 A 調?共享對象的 notify() ?法后,會喚醒?個在這個共享對象上調用wait 系列方法被掛起的線程。
共享對象上可能會有多個線程在等待,具體喚醒哪個線程是隨機的。
講講sleep方法
當線程A調用了Thread的sleep方法后,線程A會暫時讓出指定時間的執行權。
指定的睡眠時間到了之后,線程A會繼續參與執行權的爭奪。
講講yield方法
yield方法是讓當前線程讓出CPU的使用權,回到就緒狀態。但是線程調度器可能會忽略。
講講Interrupt方法
interrupt() 方法用于通知線程停?,但不會直接終?線程,需要線程自行處理中斷標志。 常與 isInterrupted() 或 Thread.interrupted() 配合使用。
5.線程有幾種狀態?
一般6種,new代表線程創建并未啟動,runnable代表線程處于就緒或正在運行的狀態,由操作系統調度,blocked代表線程被阻塞,可能是獲取鎖失敗,waiting代表等待線程的通知timed_waiting
代表線程等待一段時間,超時會自動恢復,terminated 代表線程執?完畢,生命周期結束。
也就是說,線程的周期可以分為五個階段:新建,就緒,運行,阻塞,終止。線程運行時會根據狀態的變化來隨時變化。
通過一個表格對線程狀態的總結:
狀態 | 說明 |
NEW | 當線程被創建后,如通過new Thread(),它處于新建狀態。此時,線程已經被分配了必要的資源,但還沒有開始執? |
RUNNABLE | 當調?線程的start()?法后,線程進?可運?狀態。在這個狀態下,線程可能正在運 ?也可能正在等待獲取 CPU 時間?,具體取決于線程調度器的調度策略。 |
BLOCKED | 線程在試圖獲取?個鎖以進?同步塊/?法時,如果鎖被其他線程持有,線程將進?阻塞 狀態,直到它獲取到鎖 |
WAITING | 線程進?等待狀態是因為調?了如下?法之?:Object.wait()或 LockSupport.park()。在等待狀態下,線程需要其他線程顯式地喚醒,否則不會?動 執? |
TIME_WAITING | 當線程調?帶有超時參數的?法時,如Thread.sleep(long millis)、 Object.wait(long timeout) 或LockSupport.parkNanos(),它將進?超時等待狀 態。線程在指定的等待時間過后會?動返回可運?狀態。 |
TERMINATED | 當線程的run()?法執?完畢后,或者因為?個未捕獲的異常終?了執?,線程進?終止狀態。?旦線程終?,它的?命周期結束,不能再被重新啟動。 |
?