Java中的多線程和線程安全問題

一、多線程

1、多線程的優缺點
優點
	1. 提升資源利用率
	2. 提高用戶體驗
缺點:
	1. 降低了其他線程的執行概率
	2. 用戶會感受到軟件的卡頓問題
	3. 增加的系統,資源壓力
	4. 多線程情況下的共享資源問題,線程衝突,線程安全問題
2、創建自定義線程類的方式
方式一:
	自定義線程類,繼承Thread類,重寫run方法
	創建自定義線程對象,直接調用start方法,開啓線程
方式二:
	自定義線程類,實現Runnable接口
	使用自定義實現接口Runnable實現類對象,作爲Thread構造方法參數
	藉助於Thread類對象和start方法,開啓線程

以上兩種方式,推薦使用方式二,實現Runnable接口來完成自定義線程,不影響正常的繼承邏輯,並且可以使用匿名內部類來完成線程代碼塊的書寫。

3、Thread類的一些方法
3.1 構造方法
Thread();
	分配一個新的線程對象,無目標,無指定名字
Thread(Runnable target);
	創建一個新的線程對象,並且在創建線程對象的過程中,使用Runnable接口的實現類
	對象作爲執行的線程代碼塊目標
Thread(String name);
	創建一個新的線程,無指定目標,但是指定當前線程的名字是什麼
Thread(Runnable target, String name);
	創建一個線程的線程對象,使用Runnable接口實現類對象,作爲執行目標,並且指定name作爲線程名
3.2 成員方法
void setName(String name);
String getName();
	以上兩個是name屬性setter和getter方法
void setPriority(int Priority);
	設置線程的優先級,只是增加執行的概率,優先級數值範圍 [1 - 10] 10最高 1最低 5默認
int getPriority();
	獲取線程優先級
void start();
	啓動線程對象
public static void sleep(int ms);
	當前方法是靜態方法,通過Thread類調用,要求是當前所在線程代碼塊對應的線程,
	進行休眠操作,休眠指定的毫秒數
public static Thread currentThread();
	當前方法是靜態方法,通過Thread類調用,獲取當前所處代碼塊對應的線程對象。

二、線程安全問題和解決方案

1、共享資源的使用問題

例如一個售票系統,多個售票點同時對外售票,那麼票的數量就是一個共享資源,多個售票點都可以對其進行操作,這就需要定義成靜態成員變量。靜態成員變量屬於類變量,所有的當前類對象,使用的靜態成員變量都是一個,而且一處修改,處處受影響。

2、同步代碼塊
synchronized (/* 鎖對象 */) {
}

特徵:
1. synchronized 小括號裏面的對象是鎖對象,並且要求如果是多線程的情況下,鎖對象必須是同一個對象。
2. synchronized 大括號中的代碼塊就是需要進行同步的代碼,或者說是加鎖的代碼,大括號裏面的內容,有且只允許一個線程進入。
3. 同步代碼塊越短越好,在保證安全的情況下,提高性能

問題:
1. 目前鎖對象比較隨意,存在一定的隱患
2. 代碼層級關係很複雜,看着有點麻煩

3、同步方法

synchronized 作爲關鍵字來修飾方法,修飾的方法就是對應的同步方法,有且只允許一個線程進入。

  1. 靜態成員方法
    鎖對象是當前類對應的字節碼文件.class
  2. 非靜態成員方法
    鎖對象就是當前類對象 this

選擇同步方法是否使用static修飾問題
1. 如果非static修飾,要保證執行的線程對象有且只有一個,因爲鎖對象就是當前線程對象
2. 如果是static修飾,鎖對象具有唯一性,多個線程使用的鎖是同一個鎖。

4、Lock鎖

加鎖操作是相對於同步代碼塊和同步方法而言更加廣泛的一種操作方式。

  1. 對象化操作。
    創建Lock構造方法 Lock lock = new ReentrantLock();
  2. 方法化操作。
    開鎖: unlock();
    加鎖: lock();

三、線程的狀態

1、六種線程狀態
狀態 導致狀態的發生條件
NEW(新建) 線程剛剛被創建,沒有啓動,沒有調用start方法
RUNNABLE(可運行) 線程已經可以在JVM中運行,但是是否運行不確定,看當前線程是否擁有CPU執行權
BLOCKED(鎖阻塞) 當前線程進入一個同步代碼需要獲取對應的鎖對象,但是發現當前鎖對象被其他線程持有,當前線程會進入一個BLOCKED。如果佔用鎖對象的線程打開鎖對象,當前線程持有對應鎖對象,進入Runnable狀態
WAITING(無限等待) 通過一個wait方法線程進入一個無限等待狀態,這裏需要另外一個線程進行喚醒操作。進入無限等待狀態的線程是無法自己回到Runnable狀態,需要其他線程通過notify或者notifyAll方法進行喚醒操作
TIMED_WAITING(計時等待) 當前線程的確是等待狀態,但是會在一定時間之後自動回到Runnable狀態,例如 Thread.sleep() 或者是Object類內的wait(int ms);
TERMINATED(被終止) 由於Run方法運行結束正常退出線程,或者說在運行的過程中因爲出現異常導致當前線程終止
2、TIMED_WAITING(計時等待)
Thread.sleep(int ms);
	在對應線程代碼塊中,當前線程休眠指定的時間。
Object類內  wait(int ms);
	讓當前線程進入一個計時等待狀態

在規定的時間及時完畢,線程回到可運行狀態。在等待時間內,通過其他線程被notify或者notifyAll喚醒。

Sleep方法
1. 調用之後休眠指定時間
2. sleep方法必須執行在run方法內,纔可以休眠線程
3. sleep不會打卡當前線程佔用的鎖對象。

3、BLOCKED(鎖阻塞)

線程中有鎖存在,線程需要進入帶有鎖操作的同步代碼,如果鎖對象被別人持有,只能在鎖外等待

鎖阻塞狀態的線程是否能夠搶到鎖對象有很多因素
1. 優先級問題,非決定因素
2. CPU執行概率問題。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章