【Java】深入理解Java线程
1 相關概念
并發:兩個或多個事件在同一時間段內發生【多個任務交替執行】
并行:兩個或多個事件在同一時刻發生【多個任務同時執行】
進程:進入內存的程序
內存:所有應用程序都要進入到內存中執行 臨時存儲RAM
線程:進程的一個執行單元,負責程序的執行
一個程序至少有一個進程,一個進程可以包含多個線程
CPU:中央處理器,對數據進行計算,指揮軟件和硬件
單線程:CPU在多個線程之間做高速的切換,輪流執行多個線程,效率低
多線程:多個線程在多個任務之間做高速的切換,速度是單線程的多倍,多個線程之間互不影響
線程調度:
- 分時調度:所有線程輪流使用CPU,平均分配每個線程占用CPU的時間
- 搶占式調度:優先讓優先級高的線程使用CPU,如果優先級相同,則隨機選擇一個,Java中使用搶占式調度。
2 主線程
主線程:執行main方法的線程
主線程的過程:JVM執行main方法,main方法進入到棧內存,JVM會找操作系統開辟一條main方法的執行路徑,CPU根據路徑來執行main方法,這個路徑就是主線程。
單線程:執行從main方法開始自上而下依次執行
3 創建多線程程序-法1
3.1 創建Thread類的子類
main壓棧執行后,在堆內存中創建線程子類對象,棧中保存對象地址。如果調用run方法,run方法壓棧執行,則是單線程處理。如果調用start方法,會開辟新的棧空間執行run方法。CPU可以選擇不同的棧空間。
start使線程開始執行,JVM調用線程的run方法,兩個線程并發運行
(main線程)<----->(創建新線程執行run)
同優先級下,隨機搶占,誰搶到誰執行
3.2 Thread類常用方法
獲取線程名稱:getName()、Tread.currentTread()
Tread t = new Tread(); sout(t.getName());//名稱 Tread t = Tread.currentTread(); sout(t);//名稱設置線程名稱:setName()、構造方法參數傳遞線程名稱
public class MyThreadTest {public static void main(String[] args) {MyThread mt1 = new MyThread("張三");mt1.start();MyThread mt2 = new MyThread();mt2.setName("李四");mt2.start();} } public class MyThread extends Thread {public MyThread(String name) {super(name);public MyThread() {}@Overridepublic void run() {System.out.println(getName());} }線程休眠:sleep(long millis) 毫秒結束后程序繼續執行
模擬秒表 示例:
public class MyThreadTest {public static void main(String[] args) throws InterruptedException {for (int i = 1; i <= 60; i++) {System.out.println(i);Thread.sleep(1000);}} }4 創建多線程程序-法2【推薦使用】
4.1 創建Runnable實現類
4.2 兩種實現方法的區別
Runnable的優點:
- 避免了單繼承的局限性,類繼承了Thread類就不能繼承其他類了,實現Runnable接口還可以實現其他接口。
- 增強了程序的擴展性,降低了程序的耦合性(解耦)。把設置線程任務和開啟線程進行了分離。實現類中重寫run方法來設置線程任務,創建Thread類對象調用start來開啟新線程。想要什么任務,就傳遞什么實現類對象。
5 匿名內部類創建線程
匿名內部類:簡化代碼。
把1.子類繼承父類 2.重寫父類 3.創建子類對象 —> 一步完成
把1.實現實現類接口 2.重寫接口方法 3.創建實現類對象 —> 一步完成
6 線程安全
共享資源產生安全問題
public class RunnableImpl implements Runnable {//共享票源private int tickets = 100;@Overridepublic void run() {//重復賣票while(true){if(tickets > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("賣第"+tickets+"張票");tickets--;}}} } public class MyThreadTest {public static void main(String[] args) throws InterruptedException {RunnableImpl r = new RunnableImpl();Thread t1 = new Thread(r);Thread t2 = new Thread(r);Thread t3 = new Thread(r);t1.start();t2.start();t3.start();} }出現了重復的票
窗口Thread-2在賣第100張票
窗口Thread-0在賣第100張票
窗口Thread-1在賣第100張票
窗口Thread-1在賣第97張票
窗口Thread-2在賣第97張票
窗口Thread-0在賣第97張票
窗口Thread-0在賣第94張票
窗口Thread-2在賣第94張票
出現了不存在的票
窗口Thread-2在賣第0張票
窗口Thread-1在賣第-1張票
線程安全問題都是由全局變量及靜態變量引起的。若每個線程中對全局變量、靜態變量只有讀操作,而無寫操作,一般來說,這個全局變量是線程安全的;若有多個線程同時執行寫操作,一般都需要考慮線程同步,否則的話就可能影響線程安全。
注意:訪問共享數據的時候,無論是否時區CPU執行權,其他線程只能等待,等當前線程完全結束后,其他線程再繼續。
7 同步技術的原理
使用了一個鎖對象,這個對象叫同步鎖,也叫對象監視器。多個線程一起搶奪CPU執行權,誰搶到了,誰執行run方法,遇到同步代碼塊。
此時,搶到CPU的當前線程T0會檢查同步代碼塊是否有鎖對象,如果有,則獲取鎖對象,進入到同步中進行。
另一進程T1搶到CPU后發現沒有鎖對象了,則進入阻塞狀態,等待鎖對象的歸還,直到上一進程T0執行完同步代碼塊才歸還鎖對象,T1進程才可以獲取到鎖對象,進入到同步中執行。
同步中的線程,沒有執行完畢不會釋放鎖,同步外的線程沒有鎖對象,無法進入同步代碼塊。同步保證了只有一個線程再同步中執行共享數據,保證安全,但犧牲了效率。
7.1 同步方法
定義同步方法解決線程安全問題
同步方法也會鎖住方法內部,只讓一個線程執行,鎖對象是實現類對象new RunnableImpl(),也就是this。
public class RunnableImpl implements Runnable {//共享票源private int tickets = 100;@Overridepublic void run() {while(true){sell();}}public synchronized void sell(){if(tickets > 0){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}String name = Thread.currentThread().getName();System.out.println("窗口"+name+"在賣第"+tickets+"張票");tickets--;}} }7.2 靜態同步方法
加static關鍵字,鎖對象不是this,this是創建對象之后產生的,static優先于對象的創建,靜態同步方法的鎖對象是本類的class屬性—>class文件對象(反射)
RunnableImpl.class7.3 Lock鎖
JDK1.5之后出現Lock接口,實現了synchronized方法和語句,可獲得更廣泛的鎖操作。
8 線程狀態
- 新建狀態:new
- 運行狀態:runnable
- 阻塞狀態:blocked
- 死亡狀態:terminated
- 休眠狀態:time_waiting【等待時間】
- 永久等待:waiting【等待喚醒】
new —> start() + CPU —> runnable
new —> start() - CPU —> blocked
runnable —> stop() / run(over) —> terminated
runnable —> sleep / wait —> timed_waiting
timed_waiting —> time over - CPU—> blocked
timed_waiting —> time over + CPU—> runnable
runnable —> Object.wait() —> waiting
waiting —> Object.notify() + CPU —> runnable
waiting —> Object.notify() - CPU —> blocked
9 線程通信
9.1 等待喚醒案例
Object.wait(long m):無參數的wait需要等待notify喚醒,有參數的wait等到時間結束后,進入到runnable(有CPU)或者blocked(無CPU)狀態,相當于sleep(long m),但如果時間結束前,notify被調用,則提前醒來。
Object.notifyAll():喚醒監視器上所有的線程。
9.2 生產者和消費者案例
10 線程池
10.1 概念
線程池:其實就是一個容納多個線程的容器,其中的線程可以反復使用,省去了頻繁創建線程對象的操作,無需反復創建線程而消耗過多資源。
線程池的優點:
10.2 線程池的使用
JDK1.5出現線程池的工廠類Executor用來生產線程池
Executors類的靜態方法:
- newFixedThreadPool(int nThread):創建可重用固定線程數的線程池,返回值是ExecutorService接口的實現類對象,使用ExecutorService接口接收【面向接口編程】
ExecutorService接口:
- shutdown:關閉銷毀線程池
- submit(Runnable task):提交一個Runnable任務用于執行
使用步驟
pool-1-thread-2創建了一個新的線程執行
pool-1-thread-1創建了一個新的線程執行
pool-1-thread-2創建了一個新的線程執行
pool-1-thread-1創建了一個新的線程執行
總結
以上是生活随笔為你收集整理的【Java】深入理解Java线程的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 领域应用 | NLP 和知识图谱:金融科
- 下一篇: SinglepassTextCluste