【JavaSE】多線程基礎

線程

進程中的單個順序控制流,是一條執行路徑


單線程:一個進程如果只有一條執行路徑,則稱爲單線程程序

多線程:一個進程如果有多條執行路徑,則稱爲多線程程序

多線程的實現方式

  • 繼承Thread類,並重寫run()方法
  • 實現Runnable接口,並重寫run()方法

run()方法:用來封裝被線程執行的代碼

start()方法:啓動線程;然後由JVM調用此線程的run()方法

相比繼承Thread類,實現Runnable接口的好處:

  • 避免了Java單繼承的侷限性
  • 適合多個相同程序的代碼去處理同一個資源的情況,把線程和程序的代碼、數據有效分離,較好的體現了面向對象的設計思想

設置和獲取線程名稱

Thread類中設置和獲取線程名稱的方法:

  • void setName(String name):將此線程的名稱更改爲等於參數name

  • String getName():返回此線程的名稱

  • 通過構造方法也可以設置線程名稱

    // 這是Thread類中的一個構造方法,傳遞的參數name就是線程名
    // 需要通過繼承Thread的類中的構造方法裏調用super(name)
    public Thread(String name) {
        init(null, null, name, 0);
    }
    

如何獲取main()方法所在的線程名稱?

  • public static Thread currentThread():返回對當前正在執行的線程對象的引用

線程調度(線程優先級)

線程有兩種調度模型

  • 分時調度模型:所有線程輪流使用CPU的使用權,平均分配每個線程佔用CPU的時間片
  • 搶佔式調度模型:優先讓優先級高的線程使用CPU,如果線程的優先級相同,那麼會隨機選擇一個,獲取的CPU時間片相對多一些

Java使用的是搶佔式調度模型

假如計算機只有一個CPU,那麼CPU在某一時刻只能執行一條指令,線程只有得到CPU時間片,也就是使用權,纔可以執行指令。所以說多線程程序的執行是有隨機性,因爲誰搶到CPU的使用權是不一定的

Thread類中設置和獲取線程優先級的方法:

  • public final int getPriority():返回此線程的優先級
  • public final void setPriority(int newPriority):更改此線程的優先級

線程默認優先級爲:5;線程優先級的範圍是:1-10

Thread類中有三個靜態變量分別代表了線程優先級的最大值、最小值和默認值:

// 線程優先級的範圍
System.out.println("這是優先級最大值:" + Thread.MAX_PRIORITY); // 10
System.out.println("這是優先級最小值:" + Thread.MIN_PRIORITY); // 1
System.out.println("這是優先級默認值:" + Thread.NORM_PRIORITY); // 5

線程優先級高僅僅表示線程獲取的CPU時間片的機率高,但是要在次數比較多,或者多次運行的時候才能看到你想要的效果

線程控制

Thread類中控制線程的幾個方法:

  • static void sleep(long millis):使當前正在執行的線程停留(暫停執行)指定的毫秒數**(注:1秒=1000毫秒)**

  • void join():等待這個線程死亡

    t.join()方法只會使主線程進入等待池並等待t線程執行完畢後纔會被喚醒。並不影響同一時刻處在運行狀態的其他線程

    // 啓動線程
    mt1.start();
    mt2.start();
    // mt1調用join()方法,等待mt1該線程死亡纔會繼續執行其他代碼
    // 但不影響同一時刻處在運行狀態的其他線程(mt2還在執行)
    mt1.join();
    // 這時mt1線程死亡,開始執行下面的代碼
    mt3.start();
    System.out.println("---");
    
  • void setDaemon(boolean on):將此線程標記爲守護線程,當運行的線程都是守護線程時,Java虛擬機將退出

線程安全

線程同步保證了多線程數據安全


同步代碼塊

  • 格式:
synchronized(任意對象){
    多條語句操作共享數據的代碼
}
  • synchronized(任意對象):就相當於給代碼加鎖了,任意對象就可以看成是一把鎖

同步代碼塊的好處和弊端

  • 好處:解決了多線程的數據安全問題
  • 弊端:當線程很多時,因爲每個線程都會去判斷同步上的鎖,這是很耗費資源的,無形中會降低程序的運行效率

同步方法

同步方法

就是把synchronized關鍵字加到方法上

  • 格式:

    修飾符 synchronized 返回值類型 方法名(方法參數){ }
    
同步方法的鎖對象
  • this

同步靜態方法

就是把synchronized關鍵字加到靜態方法上

  • 格式:

    修飾符 static synchronized 返回值類型 方法名 (方法參數){ }
    
同步靜態方法的鎖對象
  • 類名.class

線程安全的類

StringBuffer
  • 線程安全,可變的字符序列
  • 從版本JDK 5開始,被StringBuilder 替代。通常應該使用StringBuilder類,因爲它支持所有相同的操作,但它更快,因爲它不執行同步
Vector
  • 從Java 2平臺v1.2開始,該類改進了List接口,使其成爲Java Collections Framework的成員。與新的集合實現不同,Vector被同步。 如果不需要線程安全的實現,建議使用ArrayList代替Vector
Hashtable
  • 該類實現了一個哈希表,它將鍵映射到值。任何非null對象都可以用作鍵或者值
  • 從Java 2平臺v1.2開始,該類進行了改進,實現了Map接口,使其成爲Java Collections Framework的成員。與新的集合實現不同,Hashtable被同步。 如果不需要線程安全的實現, 建議使用HashMap代替Hashtable

Lock鎖

雖然我們可以理解同步代碼塊和同步方法的鎖對象問題,但是我們並沒有直接看到在哪裏加上了鎖,在哪裏釋放了鎖,爲了更清晰的表達如何加鎖和釋放鎖,JDK5以後提供了一個新的鎖對象Lock

Lock實現提供比使用synchronized方法和語句可以獲得更廣泛的鎖定操作

Lock中提供了獲得鎖和釋放鎖的方法

  • void lock():獲得鎖
  • void unlock():釋放鎖

Lock是接口不能直接實例化,這裏採用它的實現類ReentrantLock來實例化

ReentrantLock的構造方法

  • ReentrantLock():創建一個ReentrantLock的實例

注意事項

注意:必須在try裏調用lock()方法!

原因:如果程序出問題,釋放鎖的操作可能不會被執行。

格式:

// 這樣就算程序出錯,釋放鎖的操作依然會執行
try{
    lock.lock();
    ...
        }finally{ lock.unlock();
        }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章