1. 程式人生 > >多線程小結

多線程小結

類名.class lis 消費 銷毀 延時處理 指定 生產者和消費者 notify 位數

CPU的基本執行單位數是線程
有進程才有線程
每個進程裏至少有一個線程
每個進程內可以有多個線程

分時調度模型:線程輪流使用cpu,平均分配時間,如果沒進行完等下次分配,如果進行完了還有時間就等待
搶占式:按優先級大小來使用cpu,如果優先級相同就隨機選擇線程執行

同一時刻,cpu只能執行一個線程
但是多線程提高了“程序運行速度” (假的,提高了cpu的使用效率)

同時能執行多個線程,但是同一時刻只能執行一個線程
由於多個線程之間cpu使用切換賊快,用戶無法肉眼區分,但是同一時刻確實是只執行了一個線程

雖然原理是提高運行效率,但是我們在開發時就可以認為是多個線程同時執行,然後相互搶奪cpu的使用權

當java執行main的時候就是在執行一個叫main的線程
可以在main方法執行時開啟多個線程,然後多個線程同時執行相互搶奪cpu

Thread類。java.lang包下的一個常用類,每個Thread類的對象就代表一個處於某種狀態(正在使用,沒有在使用等)的線程

開啟線程的步驟:
方式一
1.指定線程執行目標:定義一個Thread類的子類然後重寫run方法,將相關邏輯實現
run(),線程要執行的業務邏輯方法,相當於該線程的main方法
2.創建自定義的線程子類對象
3.開啟線程
start() 是線程開始執行
getName()獲取線程名稱,是線程類的方法
setName()指定線程名稱
Thread currentThread() 獲取當前線程對象

註意:如果創建了Thread的引用並傳給了Thread的currentThread方法,就會執行Thread的線程,因為這個是父線程

方式二:
(頻繁使用)
1.指定線程執行目標:
使用Runnable接口,他的對象就是線程要執行的目標
定義Runnable線程執行目標子類,重寫run方法,指定目標邏輯
2.通過指定線程執行目標的構造方法創建線程本身的對象
public Thread(Runnable target)
所以現在執行的不是當前對象,而是傳進來的Runnable對象,也可以說是產生的Runnable目標
對象:
1.線程執行目標對象
2.通過線程執行目標創建線程對象
3.開啟線程的動作(start方法)

兩種方式的區別
方式一簡單粗暴,但是由於是創建類繼承Thread類,Java的單繼承使得該類無法繼承其他業務邏輯類,但是產生的對象的內容互不幹擾
方式二更容易共享數據,因為這個類只創建了一歌對象然後被父線程對象調用後面產生的該線程的對象使用的都是MyRunnable中的那個成員,所以實現了成員內容共享
並且因為是實現了接口所以能繼承其他類

總結:方式一試類繼承了Thread類,所以不能實現其他業務邏輯類,並且是因為創建對象的時候是分開創建點的,不同對象之間互不影響
方式二是實現類,可以繼承其他的業務邏輯類,而且創建對象之後,要被Thread用構造方法調用才會出現線程,對象創建一次然後被多個Thread調用,可以被多個類共享對象的內容,使代碼的復用性更高,所以這個是頻繁使用的方式

線程的四個基本周期:新建狀態,可運行狀態,運行狀態,死亡狀態

線程池:將線程放入線程池中可以反復利用線程,避免了線程釋放的時候的資源浪費
Executors:newFixedThreadPool 產生一個線程數量為nThreads的線程池
Future<?> submit(Runnable task)接收一個Runnable,執行該線程執行目標,線程池執行多少個目標就執行多少次提交
線程池沒有銷毀的時候就會一直運行
線程如果超過了nThreads的話不會報錯

Thread的sleep(long milis)方法:線程睡眠,使線程延時處理

同級線程在執行過程中可以亂搶!
哪怕是第一個線程語句塊沒有執行完,第二個線程也可以搶,而第一個線程就停住了,直接開始第二個線程的操作
線程出現安全隱患的原因:操作共享數據,一次完整的操作過程不應該分割執行,即該完整操作過程操作期間,不應該被其他線程搶占cpu

synchronized(鎖對象){}
使用同步代碼塊的時候。使用synchronized的時候,並不在意創建的對象的類型是什麽,只要進行的線程是一個類型就行
只要多個線程使用同一把鎖就可以使多個線程操作相同的數據
執行原理:
當一個線程進入synchronized的時候,會獲取到這個鎖的對象,然後從裏面鎖上,假如其他線程想要搶cpu就會被鎖給拒絕(對象已經有了自己的值),然後synchronized結束了之後其他的才可以進來
如果鎖已經被鎖上了(同一把鎖),那麽不管這個執行代碼塊被復制多少次,也都是安全的

鎖的定義是在成員變量的位置
同步代碼塊:就是用synchronize的鎖上的代碼塊,在當前類的其他位置也有,並且鎖是同一把鎖
同步方法:用synchronized在方法返回值之前修飾,這樣方法內部,不管有沒有同步代碼塊都是同步的。(うそ)
鎖對象其實就是this,因為線程對象傳入的對象是同一個Runnable(其實是Runnable實現類)對象,當這個對象被一個進程占用的時候,其他進程無法使用。如果鎖用的是this,那麽就不用定義鎖對象了,同樣同步方法也是安全的

如果方法用static修飾,那麽鎖默認使用該類的字節碼文件.class(字節碼文件對象),這個時候鎖用的不是this,而是類名.class

synchronized要是鎖的話要在死循環內鎖,要是在死循環外鎖了一個死循環,結果是一直執行一個死循環其他進程不進行

生產者和消費者共享資源的原理:
要是兩邊的類各產生了一個對象,那麽這是兩個對象,並沒有進行數據之間的綁定。
而要是想要讓數據綁定同一個數據,就先在主方法裏創建一個共同的對象,然後要麽就在兩個類的方法級別位置創建對象然後用構造方法將對象綁定為同一個,要麽就是將對象的引用私有化,只讓構造方法引用,這樣這兩個類內部的對象就是同一個對象,就能實現了數據的綁定

要是多行的話就用鎖鎖住,要不會出現安全問題

等待喚醒的機制:
因為想要讓某些生產者和消費者交替執行,所以需要一個標誌位,當標誌位為某個值的時候就執行某個類。
規則:true有數據,生產者等待,false無數據,消費者等待
方法:
Object的方法:
wait() 等待
notify() 喚醒
先判斷flag值,如果滿足某個條件就wait(),然後在執行完畢之後將falg改變,用notify喚醒另一個類,然後交替

sleep由於線程會蘇醒,所以不會釋放鎖
wait會釋放鎖,然後等待其他線程喚醒該線程,然後接著wait下面的代碼執行

線程死鎖- -啥

多線程小結