Java多線程與高併發(一)

順序:進程->線程->協程/纖程

進程:

進程是程序的一次靜態執行過程,佔用特定的地址資源,每個進程之間都是獨立存在,由三部分形成:cpu/data/code

缺點:內存的浪費,cpu負擔

線程概念

對於程序而言,線程就是一個指令的集合,一個進程裏面的不同執行路徑,稱爲線程,是進程中一個“單一連續的控制流程”/執行路徑,線程又被稱爲輕量級進程,一個進程可以擁有多個並行的線程,一個進程中的線程共享相同的內存單元,內存地址空間->可以訪問相同的變量和對象,而且它們從同一堆中分配對象->通信/數據交互/同步操作。由於線程之間的通信是在同一地址空間上進行的,所以不需要額外通信機制。這使得通信更簡單而且信息傳遞也更快。

協程/纖程

線程的又一次細分,這裏不多說

線程啓動三種方式

  • 繼承Threadl類,子類重寫父類run方法

package com.juc.c_01;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/21 21:33
 * @Description 繼承Thread類,重寫run方法
 * 實現線程注意
 * 1,繼承Thread類
 * 2,必須重寫run方法,run中寫核心執行邏輯代碼
 * 3,線程啓動的時候,不要直接調用run方法,而是調用線程start(),
 * 4.每次運行相同的代碼,出來的結果可能都不一定,因爲多線程誰先搶佔資源誰先執行無法人爲控制。
 */
public class T_001 extends Thread {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
    }

    public static void main(String[] args) {
        //調用線程
        // new T_001().run();
        new T_001().start();

        for (int i = 0; i < 5; i++) {

            System.out.println(Thread.currentThread().getName()+"-----"+i);
        }
    }
}

 調用run方法和調用start()方法的區別

運行結果

 1.start()調用結果

2.run()調用結果

  • 實現Runnable接口,實現run方法

package com.juc.c_01;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/21 22:15
 * @Description 實現Runnable接口,實現run方法
 * 1.實現runnable接口
 * 2.重寫run方法
 * 3.創建Thread對象,將剛剛創建的Runnable的子類,作爲構造參數,thread.start()調用線程,這個還使用了代理模式。
 */
public class T_002 implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("使用Thread方法實現線程一:" + Thread.currentThread().getName() + "---" + i);
        }
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName() + "---" + i);
        }
        T_002 t_002 = new T_002();
        //先創建runnable子類對象,然後創建Thread對象,將Runnable子類對象作爲構造參數,最後調用start()
        new Thread(t_002).start();
    }
}

執行結果

線程的生命週期

 1.新生狀態

    當線程創建好當前線程對象之後,沒有啓動之前,調用start()之前

    用new關鍵字建立一個線程後,該線程對象就處於新生狀態,

    處於新生狀態線程又自己的內存空間,通過調用start()方法進入就緒狀態。

     1.Thread    T_001 t_001=new T_001();

      2.Runnable  T_002 t_002=new T_002();

2.就緒狀態:

     準備開始執行,並沒有執行,表示調用start()方法之後

      -當對應的線程創建完成,且調用start方法之後,所有的線程會添加到一個就緒的隊列中,所有線程同時搶佔cpu資源,

      -處於就緒狀態線程具備了運行條件,但分配到cpu,處於線程就緒隊列,等待系統爲其分配CPU

      -當系統選定一個等待執行的線程後,它就會從就緒進入執行狀態,該動作稱爲CPU調度

      

3.運行狀態:

        -噹噹前進程獲取到cpu資源後,就緒隊列中的所有線程同時去搶佔cpu資源,誰先搶佔到誰先執行。在執行過程中叫做運                        行狀態,搶佔到cpu資源,執行代碼邏輯開始。

        -在運行狀態的線程執行的run方法中代碼,直到等待某資源二阻塞或完成任何而死亡。

        -如果在給定的時間片內沒有執行結束,就會被系統給換下來回到等待執行狀態。

4.死亡狀態:

       -當運行中的線程正常執行完所有代碼邏輯或者因爲異常導致程序結束,叫做死亡狀態。

       -在運行狀態的線程在某些情況下,如執行了sleep(睡眠)方法,或者等待I/O設備資源,將讓出cpu並暫時停止自己運行,進入阻         塞狀態。

       在阻塞狀態的線程不能進入就緒隊列,只有當引起阻塞原因消除,如睡眠時間已到,或等待的I/O設備空閒下來,線程便轉入就           緒狀態,重新到就緒列中排隊等待,被系統選中後從原來停止的位置開始繼續執行。

進入方式:

      1.程序正常結束

      2.認爲中斷,調用stop方法

       3.程序拋出未捕獲的異常

5,阻塞狀態:

      -在程序運行過程中,發生某些異常情況,導致當前線程無法執行下去,這時會進入阻塞狀態,進入阻塞狀態的原因消除了,這時         候所有阻塞線程會進入就緒隊列,隨機搶佔cpu資源,等待執行。

     -死亡狀態是線程生命週期的最後一個階段,線程死亡原因有三個,一是線程正常的線程完成他的全部工作,二是線程被強制性地        終止,如stop方法來終止一個線程【不推薦這種方式,太粗暴了】,三是線程拋出未捕獲的異常。

進入方式:

      1.sleep方法

      2.等待I/O資源

      3.join 方法(代碼中執行,強制阻塞其他線程,優先執行當前線程)

  • 線程池Executors.newCachedThrad 【不多說】

線程API方法

   1.public static Thread currentThread():放回目前執行的線程。

  2.public final  String getName():返回線程名稱。

  3.public final int getPriority():返回線程的優先級。

  4.public  final setPriority(String name):設定線程名稱。

  5.public final Boolean  isAlive():判斷線程是否在運動,如果是,返回true,否則返回false。

  6.public  final  void  join():調用該方法的線程強制執行,其他線程處於阻塞狀態,該線程執行完畢後,期它線程再執行。

  7.public static  void sleep(long millis):使用當前正在執行的線程休眠millis秒,線程處於阻塞狀態。

  8.public static void yieId(): 當前線程正在執行的線程暫停一次,允許其他線程執行,不阻塞,線程進入就緒狀態

  9.public final void stop():強迫線程停止執行,已經過時,不推薦使用,做法太粗暴。

API方法示例代碼:

package com.juc.thread;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/24 15:46
 * @Description 線程的API
 * 方法
 */
public class ThreadApi implements Runnable {
    public static void main(String[] args) {
        //獲取當線程對象
        Thread thread = Thread.currentThread();
        //獲取當前線程名稱
        System.out.println("當前線程的名稱:" + thread.getName());
        //獲取當前線程Id
        System.out.println("當前線程ID:" + thread.getId());
        //獲取當前線程優先級,一般系統中範圍0-10的值,如果沒有人爲設置,默認值5,有些系統的範圍0-100
        System.out.println("獲取當前線程優先級:" + thread.getPriority());
        //設置當前線程的優先級
        /**
         * 線程優先級越高,一定有限執行嗎?
         *    答:不一定,只是優先執行的概率比較大而已。
         */
        thread.setPriority(10);
        System.out.println("獲取設置當前線程優先級:" + thread.getPriority());

        //判斷當前線程是否存活
        System.out.println("判斷當前線程是否存活:"+thread.isAlive());

        ThreadApi target = new ThreadApi();
        Thread thread1 = new Thread(target);
        System.out.println("判斷當前線程是否存活1:"+thread1.isAlive());
        thread1.start();
        System.out.println("判斷當前線程是否存活1:"+thread1.isAlive());
        System.out.println("線程優先級:"+thread1.getPriority());

        for (int i = 0; i <5 ; i++) {
            System.out.println(Thread.currentThread().getName()+"--------"+i);
            if (i==0){
                try {
                    //強制阻塞其他線程,有限執行當前線程
                    thread.join();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }

    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(Thread.currentThread().getName()+"--------"+i);
            if (i==2) {
                try {
                    //i=2,當前線程休眠1秒,休眠的時候線程會進入阻塞轉檯
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

 yieId()示例代碼

package com.juc.thread;

/**
 * @Author zcm
 * @Email [email protected]
 * @date 2020/5/24 17:57
 * @Description YieId演示
 */
public class YieIdTest implements Runnable {
    public static void main(String[] args) {
        YieIdTest yieIdTest = new YieIdTest();
        Thread thread = new Thread(yieIdTest);
        thread.start();
        for (int i = 0; i < 5; i++) {

            System.out.println(Thread.currentThread().getName() + "-" + i);
        }
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            if (i == 2) {
                Thread.yield();
                System.out.println(Thread.currentThread().getName() + "-" + i + "禮讓一次");
            } else {
                System.out.println(Thread.currentThread().getName() + "--" + i);
            }
        }
    }
}

Object類中有兩個方法用戶等待/喚醒線程,在多線程的時候可以實現喚醒等待的操做,但是喚醒的對象不是Thread類,而是設置的共享變量和共享對象。

   2.notify() 喚醒當前共享變量和共享對象

   3.notifyAll() 喚醒所有共享變量和共享對象

   4.wait() 等待

線程出現死鎖怎麼解決?

    1.繼承Thread方式

     缺點:每次啓動線程對象的時候都會創建自己對象的屬性值,相當於每個線程操作自己,沒有真正意義實現貢獻,怎麼解決?

            解決方法:將共享變量設置成static

     2.每次訪問共享對象數據據不一致,怎麼解決?

          解決方法:使用線程同步,synchronized

  2. 實現Runnable接口

     優點:每次只創建了一個共享對象,所有的線程都可以實現資源共享

    缺點:數據不一致的問題

           解決方法:線程同步synchronized

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