1. 程式人生 > >併發(執行緒)

併發(執行緒)

目錄

 

執行緒概念

執行緒的幾種狀態

執行緒建立的兩種方式

執行緒優先順序

執行緒讓步

執行緒休眠

執行緒插隊

執行緒同步

鎖物件

條件物件

多執行緒通訊


執行緒概念

  • 計算機中的每一個程式都可以看作是一個程序,在一個程序中還可以有多個執行單元同時執行,這些執行單元可以看做程式執行的一個個線索,被稱為執行緒。

執行緒的幾種狀態

  • 可以通過呼叫執行緒的getState()方法檢視執行緒的狀態
  • New(新建立)
    • 當建立一個程序,例如new Thread(Object obj),該執行緒還沒有開始執行,它的狀態為new
  • Runnable(可執行:就緒)
    • 當呼叫了執行緒的start()方式,執行緒的狀態將變為Runnable(可執行狀態)。一個處於可執行狀態的程序可能在執行,也可能沒在執行。JVM採用的處理機分配機制為搶佔式排程,當執行緒分配到處理機時為正在執行;當處理機分配給執行緒的時間片結束之後,執行緒沒有在執行。
  • Blocked(阻塞)
  • Waiting(等待)
  • Time waiting(計時等待)
  • Terminated(終止)
    • 執行緒有兩種可能進入終止狀態
      • run方法執行完畢,正常退出
      • 因為一個沒有捕獲的異常終止了run方法
  • 執行緒之間的轉換

執行緒建立的兩種方式

  • 繼承Thread類
  • package czbk;
    
    /**
     * @author Xxx
     * @Description: 多執行緒之繼承Thread類
     * @date 2018/11/18上午11:18
     **/
    public class Example02 {
        public static void main(String[] args) {
            Mythead mythead = new Mythead();
    //        獲取執行緒狀態(New:新建立)
            System.out.println(mythead.getState());
    //        啟動執行緒
            mythead.start();
    // 取執行緒狀態(Runnable:可執行)
            System.out.println(mythead.getState());
            while(true){
                System.out.println("main()方法正在執行");
            }
        }
    }
    
    class Mythead extends Thread{
    //    重寫run方法
        @Override
        public void run() {
            while(true){
                // 取執行緒狀態(Runnable:可執行)
                System.out.println(Thread.currentThread().getState());
                System.out.println("Mythread類的run()方法正在執行");
            }
        }
    }

     

  • 實現Runnable介面
  • package czbk;
    
    /**
     * @author Xxx
     * @Description: 通過實現Runnable介面建立執行緒
     * @date 2018/11/18上午11:35
     **/
    public class Example03 {
        public static void main(String[] args) {
            Mythead2 mythead2 = new Mythead2();
            Thread thread = new Thread(mythead2);
            thread.start();
            while(true){
                System.out.println("main方法開始執行");
            }
        }
    }
    
    class Mythead2 implements Runnable{
    
        /**
         * 當一個物件實現了Runnable介面被用做建立一個執行緒,
         * When an object implementing interface <code>Runnable</code> is used
         * 啟動這個執行緒會導致物件的run方法在單獨的執行緒中被呼叫
         * to create a thread, starting the thread causes the object's
         * <code>run</code> method to be called in that separately executing
         * thread.
         * <p>
         * The general contract of the method <code>run</code> is that it may
         * take any action whatsoever.
         *
         * @see Thread#run()
         */
        @Override
        public void run() {
            while(true){
                System.out.println("Mythead2的run()方法開始執行");
            }
        }
    }
    

 

執行緒優先順序

  • 在Java程式設計中,每一個執行緒都有一個優先順序,排程器會優先排程優先順序比較高的執行緒。一般情況下,執行緒會繼承他的父程序的優先順序。優先順序分為1-10是個層級,值越大,優先順序越高。可以通過呼叫setPriority()方法設定執行緒的優先順序。

執行緒讓步

  • static void yield()
  • 執行yield方法後,執行緒被處理機排程時執行yield()方法,則該執行緒將會放棄本次排程,進入就緒狀態(Runnable)。

執行緒休眠

  • static void sleep(long millis)
  • 執行yield方法後,執行緒被處理機排程時執行yield()方法,則該執行緒將會放棄本次排程,進入阻塞狀態(Blocked),阻塞時長為millis毫秒。

執行緒插隊

  • final void join()
  • 執行緒A在被排程時,呼叫執行緒B的join()方法,將使處理機排程執行緒B,只有執行緒B執行完畢後,處理機才會排程執行緒A

執行緒同步

  • 多個執行緒共享對同一資料的存取,若不加以控制,將使資源的狀態發生意想不到的錯誤。
  • 例如,銀行對一筆錢進行操作時,執行緒A對金額進行了更改,在A準備將金額寫入資料庫時,處理機排程了執行緒B,B又對金額進行了更改並寫入資料庫,然後處理機排程執行緒A,A將此時的金額寫入資料庫。最後得到的結構並不是人們想要的,因為B抹去了A對金額的操作。之所以發生這樣的情況是因為執行緒對金額的操作並不是原子性並且執行緒的排程具有非同步性。
  • Java提供了兩種機制方式程式碼塊受併發訪問的干擾,分別是鎖物件和條件物件
  • 鎖物件

    • package czbk;
      
      import java.util.concurrent.locks.Lock;
      import java.util.concurrent.locks.ReentrantLock;
      
      /**
       * @author Xxx
       * @Description: 加入了執行緒同步(synchronized)的售票機制
       * @date 2018/11/18下午4:51
       **/
      public class Example09 {
          public static void main(String[] args) {
              TicketWindow2 ticketWindow2 = new TicketWindow2();
              new Thread(ticketWindow2, "視窗1").start();
              new Thread(ticketWindow2, "視窗2").start();
              new Thread(ticketWindow2, "視窗3").start();
              new Thread(ticketWindow2, "視窗4").start();
      
              TicketWindow3 ticketWindow3 = new TicketWindow3();
              new Thread(ticketWindow3, "視窗1").start();
              new Thread(ticketWindow3, "視窗2").start();
              new Thread(ticketWindow3, "視窗3").start();
              new Thread(ticketWindow3, "視窗4").start();
      
          }
      }
      
      class TicketWindow2 implements Runnable {
          private Object lock = new Object();
          //    這個變數是ticketWindow2的,被所有的執行緒共享
          private int tickets = 100;
      
          //    每個執行緒被處理器排程時都會執行該方法
          @Override
          public void run() {
              while (true) {
      //            在synchronized方法塊中的程式碼,一個時刻只允許一個執行緒訪問.
      //            lock的初始值為1,當有線層訪問該方法時,會將lock的值置為0,禁止其他執行緒訪問,訪問完畢後將lick的值置為1,允許其他執行緒訪問.
                  synchronized (lock) {
                      try {
                          Thread.sleep(10);
                      } catch (InterruptedException e) {
                          e.printStackTrace();
                      }
                      if (tickets >= 1)
                          System.out.println(Thread.currentThread().getName() + "正在發售第:" + tickets-- + "張票");
                      else break;
                  }
              }
      
          }
      }
      
      class TicketWindow3 implements Runnable {
          private Lock lock = new ReentrantLock();
          private int tickets2 = 100000;
      
          @Override
          public void run() {
              while (true) {
                  lock.lock();
                  try {
                      if (tickets2 >= 1)
                          System.out.println(Thread.currentThread().getName() + "正在發售第:" + tickets2-- + "張票");
                      else
                          break;
                  } finally {
                      lock.unlock();
                  }
              }
          }
      }
      

       

  • 條件物件

多執行緒通訊

  • package czbk;
    
    /**
     * @author Xxx
     * @Description: 多執行緒通訊(條件物件加鎖)
     * @date 2018/11/18下午5:09
     **/
    public class Example10 {
        public static void main(String[] args) {
            Stroge stroge = new Stroge();
            Input input = new Input(stroge);
            Output output = new Output(stroge);
            new Thread(input).start();
            new Thread(output).start();
        }
    }
    
    class Input implements Runnable {
        private Stroge stroge;
        private int num;
    
        public Input(Stroge stroge) {
            this.stroge = stroge;
        }
    
        @Override
        public void run() {
            while (true) {
                stroge.put(num++);
            }
        }
    }
    
    class Output implements Runnable {
        private Stroge stroge;
    
        public Output(Stroge stroge) {
            this.stroge = stroge;
        }
    
        @Override
        public void run() {
            while (true) {
                stroge.get();
            }
        }
    }
    
    class Stroge {
    //    存放資料的陣列
        private int[] cells = new int[10];
    //    放入和取出的下標
        private int inPos, outPos;
    //    陣列中元素的個數
        private int count;
    
    //    定義執行緒同步方法
        public synchronized void put(int num) {
            try {
    //            當陣列中元素已滿時,讓執行緒進入等待狀態(加鎖)
                while (count == cells.length) {
                    this.wait();
                }
                cells[inPos] = num;
                System.out.println("在cells[" + inPos + "]中放入資料---" + cells[inPos]);
                inPos++;
                if (inPos == cells.length)
                    inPos = 0;
                count++;
    //            喚醒此同步鎖上等待的第一個執行緒
                this.notify();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    
        public synchronized void get() {
            try {
                while (count == 0)
                    this.wait();
                int data = cells[outPos];
                System.out.println("在cells[" + outPos + "]中取出資料---" + cells[outPos]);
                cells[outPos]=0;
                outPos++;
                if (outPos == cells.length)
                    outPos = 0;
                count--;
                this.notify();
            } catch (Exception e) {
                e.printStackTrace();
            }
    
        }
    }