15Java進階 程式

小島的每一段verse發表於2021-07-31

1 執行緒控制

t.join():讓主執行緒進入執行緒池,等待t執行完才執行。

t.sleep():讓執行緒阻塞,休眠一段時間,休眠結束後進入就緒狀態。不會釋放鎖。

t.yield():讓執行緒讓出CPU,從執行態進入就緒態。可能會接著進入執行態。

t.setDaemon():設定為守護執行緒,非守護執行緒都死了的時候自動終止。

2 執行緒的分類

執行緒分為:守護執行緒 使用者執行緒

  • 守護執行緒和使用者執行緒基本上是相同的,唯一區別就是判斷JVM何時離開

  • 守護執行緒是用來服務使用者執行緒的。通過在start方法之前呼叫thread.setDaemon(true) 可以將一個使用者執行緒變成守護執行緒

  • java的垃圾回收 他是一個典型的守護執行緒

  • 如果JVM中都是守護執行緒,JVM將退出(使用者執行緒執行結束 守護執行緒無論是否結束 都將終止執行)

3 執行緒的生命週期

jdk中用Thread State定義了執行緒的狀態:

  • 執行緒狀態。執行緒可以處於以下狀態之一:

    • NEW 尚未啟動的執行緒處於此狀態。

    • RUNNABLE 在Java虛擬機器中執行的執行緒處於此狀態。

    • BLOCKED 被阻塞等待監視器鎖定的執行緒處於此狀態。

    • WAITING 正在等待另一個執行緒執行特定動作的執行緒處於此狀態。

    • TIMED_WAITING 正在等待另一個執行緒執行動作達到指定等待時間的執行緒處於此狀態。

    • TERMINATED 已退出的執行緒處於此狀態。

    執行緒的狀態通常分為5種狀態:

    新建:當一個Thread類及其子類的物件被宣告並建立時,此時的執行緒物件就處於新建狀態

    就緒 : 處於新建狀態的執行緒被start後,執行緒將進入CPU的執行佇列等待獲得CPU的執行權,此時的執行緒已經具備了執行的條件,只是還有獲得CPU的執行權

    執行:當就緒的執行緒獲得CPU的執行權 ,處於執行狀態

    阻塞:在某種特殊的情況下,被人為掛起或執行輸入輸出操作時,讓出CPU並臨時中止自己的執行。此時執行緒就進入阻塞狀態

    死亡:執行緒完成了他的全部工作或被執行緒被提前強制性的終止 或出現異常導致執行緒異常結束。

    執行緒狀態之間的相互轉換

 

 

一個執行緒一旦死亡 是不可以在重新啟動的。

4.執行緒同步

public class SellTicketDemo {
    public static void main(String[] args) {
        SellTicket st = new SellTicket();
        //建立三個執行緒
        Thread t1 = new Thread(st,"1號視窗");
        Thread t2 = new Thread(st,"2號視窗");
        Thread t3 = new Thread(st,"3號視窗");
        //啟動執行緒
        t1.start();
        t2.start();
        t3.start();
​
    }
}
public class SellTicket implements Runnable{
    private int ticktes = 100;
    @Override
    public void run() {
        while(true){
            if(ticktes > 0 ){
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"正在出售第" + ticktes+"張票");//會出現資料不同步的現象,與實際需求不符
                ticktes--;
            }
        }
    }
}

 

4.1 解決資料安全問題--同步程式碼塊

出現問題的條件:

1 多執行緒環境

2 有共享資料

3 有多條語句操作共享資料

如何解決執行緒安全問題:

基本的思想:讓程式沒有安全問題的環境

怎麼實現呢? 把多條語句操作共享資料的程式碼給鎖起來,讓任意時刻只能有一個執行緒執行。

java提供瞭解決的方式是使用同步程式碼塊或同步方法:synchronized 相當於給程式碼加鎖

可以用在程式碼塊和方法上 分別稱為同步程式碼塊和同步方法:

程式碼塊:synchronized(obj/this){

共享程式碼;

}

在同步程式碼塊中 誰來擔當這個所物件呢?

任意物件都可以充當所物件 一般情況下使用this

4.2 解決資料安全問題--同步方法

在方法的宣告上新增synchronized關鍵字

同步成員方法

靜態同步方法:static synchronized

靜態方法的同步程式碼塊:使用類名.class作為鎖物件

靜態的同步方法或者靜態方法中的同步程式碼塊的所物件是類名.class物件

單例設計模式的懶漢式的執行緒安全問題

public class Singleton {
    private static  Singleton instance;
    private Singleton(){ 
    }
    public static  Singleton getInstance(){
        if( instance == null){
            synchronized (Singleton.class){
                if(instance ==null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

 

4.6 執行緒安全的類

StringBuffer 執行緒安全的可變字元序列

StringBuilder 執行緒不安全的可變字元序列

Vector 執行緒同步的

HashTable 執行緒同步的 是一個雜湊表的實現

在實際使用時,如果不需要執行緒安全的實現,推薦使用與之功能相同的 但是執行緒不同步的實現

5 執行緒的死鎖的演示

死鎖是我們需要規避的問題

不同的執行緒分別佔用對方所需要的同步資源不放棄,都在等待對方放棄自己需要的同步資源,就行成了執行緒死鎖

出現死鎖 不會出現異常,不會出現提示,只是所有的執行緒都處於阻塞狀態 無法繼續

死鎖問題的出現是一個概率時間。

死鎖問題的解決:

1 減少同步程式碼塊或同步方法的巢狀

2 儘量減少同步資源的定義

3 使用專門的演算法

提示:

1 明確那些程式碼是多執行緒執行的程式碼,就是需要寫入run方法

2 明確那些資料是共享資料

3 明確多下稱執行程式碼中的那些語句操作了共享資料

6 執行緒通訊

6.1 什麼時候需要執行緒通訊

多個執行緒併發執行,在預設情況下CPU隨機切換執行緒,如果我們希望他們有規律的執行,就需要使用執行緒通訊。

6.2 執行緒間如何通訊

如果執行緒需要等待 就呼叫的wait().wait()方法可以等待時間結束或者被喚醒。如果要喚醒一個等待的執行緒 那麼就使用notify() /notifyAll()

6.3 互斥鎖

互斥鎖 依次最多隻能有一個執行緒持有鎖

鎖是用於通過多個執行緒控制對共享資源的訪問的工具。 通常,鎖提供對共享資源的獨佔訪問:一次只能有一個執行緒可以獲取鎖,並且對共享資源的所有訪問都要求首先獲取鎖

lock.lock():加鎖 lock.unlock()解鎖

Interface Lock 可以使用的實現類 ReentrantLock

  • 一個可重入互斥Lock具有與使用synchronized方法和語句訪問的隱式監視鎖相同的基本行為和語義,但具有擴充套件功能。

Condition

c1.await():使當前執行緒等待,直到c1.singal()或者interrupt()打斷阻塞狀態,或者到await()指定的時間。

c1.signal():喚醒一個等待執行緒

c1.signalAll():喚醒所有等待程式

不同的執行緒需要使用不同的 Condition 這樣就能區分喚醒額時候喚醒的是那個執行緒

public class ThreadLock {
    public static void main(String[] args) {
        ThreadLock tw = new ThreadLock();
        new Thread(new Runnable() {
            @Override
            public void run() {
                while(true){
                    tw.print1();
                }
​
            }
        }).start();
        new Thread(){
            @Override
            public void run() {
                while(true){
                    tw.print2() ;
                }
            }
        }.start();
        new Thread(){
            @Override
            public void run() {
                while(true){
                    tw.print3() ;
                }
            }
        }.start();
    }
    // 建立Lock鎖物件
    Lock lock = new ReentrantLock();
    //建立鎖使用的條件
    Condition c1 = lock.newCondition();
    Condition c2 = lock.newCondition();
    Condition c3 = lock.newCondition();
    // 建立一個喚醒標誌
    int  flag = 1;
    public      void  print1(){
        lock.lock();//給當前程式碼上鎖
        if(flag != 1){
            try {
                c1.await();//當前執行緒處於等待
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        System.out.print("中");
        System.out.print("北");
        System.out.print("大");
        System.out.print("學");
        System.out.println();
        flag = 2;
        c2.signal();//喚醒c2
        lock.unlock();//釋放鎖
    }
    public     void  print2(){
       lock.lock();
       if(flag != 2){
           try {
               c2.await();
           } catch (InterruptedException e) {
               e.printStackTrace();
           }
       }
        System.out.print("塔");
        System.out.print("裡");
        System.out.print("木");
        System.out.print("大");
        System.out.print("學");
        System.out.println();
         flag =3;
         c3.signal();
         lock.unlock();
    }
    public     void  print3(){
         lock.lock();
         if(flag != 3){
             try {
                 c3.await();
             } catch (InterruptedException e) {
                 e.printStackTrace();
             }
         }
        System.out.print("青");
        System.out.print("島");
        System.out.print("大");
        System.out.print("學");
        System.out.println();
         flag =1;
         c1.signal();
         lock.unlock();
    }
}

 

6.4生產者—消費者模型

生產者消費者模型的作用是什麼?

1通過平衡生產者的生產能力和消費者消費能力來提升整個系統的執行效率

2 解耦

多執行緒的學習重點:

1 執行緒的建立方式

2執行緒的生命週期(執行緒的五種狀態的轉換)

3 執行緒同步

4 執行緒通訊

 

 

 

 

 

相關文章