1. 程式人生 > >Java 多執行緒(一)—— 概念的引入

Java 多執行緒(一)—— 概念的引入

  併發和並行
  
  程序和執行緒
  
  如何建立多執行緒
  
  第一種方法:繼承 Thread 類
  
  第二種方法:實現 Runnable 介面
  
  第三種方法:使用匿名內部類建立執行緒
  
  正文
  
  回到頂部
  
  併發和並行
  
  並行:指兩個或多個時間在同一時刻發生(同時發生);
  
  併發:指兩個或多個事件在一個時間段內發生。
  
  在作業系統中,安裝了多個程式,併發指的是在一段時間內巨集觀上有多個程式同時執行,這在單 CPU 系統中,每一時刻只能有一道程式執行,即微觀上這些程式是分時的交替執行,只不過是給人的感覺是同時執行,那是因為分時交替執行的時間是非常短的。
  
  而在多個 CPU 系統中,則這些可以併發執行的程式便可以分配到多個處理器上(CPU),實現多工並行執行,即利用每個處理器來處理一個可以併發執行的程式,這樣多個程式便可以同時執行。
  
  目前電腦市場上說的多核 CPU,便是多核處理器,核 越多,並行處理的程式越多,能大大的提高電腦執行的效率。
  
  注意:單核處理器的計算機肯定不能並行的處理多個任務,只能是多個任務交替的在單個 CPU 上執行。
  
  回到頂部
  
  程序和執行緒
  
  程序:是指一個記憶體中執行的應用程式,每個程序都有一個獨立的記憶體空間,一個應用程式可以同時執行多個程序;程序也是程式的一次執行過程,是系統執行程式的基本單位;系統執行一個程式即是一個程序從建立、執行到消亡的過程。
  
  執行緒:程序內部的一個獨立執行單元;一個程序可以同時併發的執行多個執行緒,可以理解為一個程序便相當於一個單 CPU 作業系統,而執行緒便是這個系統中執行的多個任務。
  
  注意:1、因為一個程序中的多個執行緒是併發執行的,那麼從微觀角度看也是有先後順序的,哪個執行緒執行完全取決於 CPU 的排程,程式設計師是干涉不了的。而這也就造成的多執行緒的隨機性。
  
  2、Java 程式的程序裡面至少包含兩個執行緒,主程序也就是 main()方法執行緒,另外一個是垃圾回收機制執行緒。每當使用 java 命令執行一個類時,實際上都會啟動一個 JVM,每一個 JVM 實際上就是在作業系統中啟動了一個執行緒,java 本身具備了垃圾的收集機制,所以在 Java 執行時至少會啟動兩個執行緒。
  
  3、由於建立一個執行緒的開銷比建立一個程序的開銷小的多,那麼我們在開發多工執行的時候,通常考慮建立多執行緒,而不是建立多程序。
  
      4、多執行緒是為了同步完成多個任務,不是為了提高程式執行效率,而是通過提高資源使用效率來提高系統的效率。比如做一個新增功能,需要向很多使用者傳送簡訊和郵件,如果需要一分鐘,不能能讓使用者等一分鐘才響應,可以使用多執行緒做後臺傳送,如果是單核,則多個任務交替的在單個 CPU 上執行,理論上比不使用多執行緒耗時多,因為CPU切換任務需要耗費時間,如果是多核,則可以提高資源的使用效率,多核可以充分利用上。
  
  回到頂部
  
  如何建立多執行緒
  
  第一種方法:繼承 Thread 類
  
  步驟:1、定義一個執行緒類 A 繼承於 java.lang.Thread 類
  
  2、在 A 類中覆蓋 Thread 類的 run() 方法
  
  3、在 run() 方法中編寫需要執行的操作
  
  4、在 main 方法(執行緒)中,建立執行緒物件,並啟動執行緒
  
  建立執行緒類:A類 a = new A()類;
  
  呼叫 start() 方法啟動執行緒:a.start();
  
  複製程式碼
  
  /**
  
  * @author: ChenHao
  
  * 建立多執行緒的第一種方式,繼承java.lang.Thread類
  
  * @Description:建立一個子執行緒,完成1-100之間自然數的輸出。同樣,主執行緒執行同樣的操作
  
  * @Date: Created in 10:50 2018/10/29
  
  */
  
  public class TestThread {
  
  public static void main(String [] args){
  
  SubThread subThread1=new SubThread();
  
  SubThread subThread2=new SubThread();
  
  //呼叫執行緒的start(),啟動此執行緒;呼叫相應的run()方法
  
  subThread1.start();
  
  subThread2.start();
  
  //一個執行緒只能夠執行一次start(),start()中會判斷threadStatus的狀態是否為0,不為0則丟擲異常
  
  //subThread1.start();
  
  //不能通過Thread實現類物件的run()去啟動一個執行緒,此時只是主執行緒呼叫方法而已,並沒有啟動執行緒
  
  //subThread1.run();
  
  for (int i=0;i<=100;i++){
  
  System.out.println(Thread.currentThread().getName()+":"+i);
  
  }
  
  }
  
  }
  
  //1.建立一個繼承Thread的子類
  
  class SubThread extends Thread{
  
  //2.重寫run方法,方法內實現此子執行緒要完成的功能
  
  @Override
  
  public void run(www.fengshen157.com){
  
  for (int i=0;i<=100;i++){
  
  System.out.println(Thread.currentThread().getName()+":"+i);
  
  }
  
  }
  
  }
  
  複製程式碼
  
  注意:1、不能通過Thread實現類物件的run()去啟動一個執行緒,此時只是主執行緒呼叫方法而已,並沒有啟動執行緒,要啟動執行緒,必須通過Start()方法
  
  2、一個執行緒只能夠執行一次start(),start()中會判斷threadStatus的狀態是否為0,不為0則丟擲異常,所以一個執行緒呼叫兩次start()會報異常
  
  第二種方法:實現 Runnable 介面
  
  1、Runnable介面應由任何類實現,其例項將由執行緒執行。 該類必須定義一個無引數的方法,稱為run 。
  
  2、該介面旨在為希望在活動時執行程式碼的物件提供一個通用協議。此類整個只有一個 run() 抽象方法
  
  步驟:1、定義一個執行緒類 A 實現於 java.lang.Runnable 介面(注意:A類不是執行緒類,沒有 start()方法,不能直接 new A 的例項啟動執行緒)
  
  2、在 A 類中覆蓋 Runnable 介面的 run() 方法
  
  3、在 run() 方法中編寫需要執行的操作
  
  4、在 main 方法(執行緒)中,建立執行緒物件,並啟動執行緒
  
  建立執行緒類:Thread t = new Thread( new A類() ) ;
  
  呼叫 start() 方法啟動執行緒:t.start();
  
  複製程式碼
  
  package main.java.Thread;
  
  /**
  
  * @author: ChenHao
  
  * @Description:建立多執行緒的方式二:實現runnable
  
  * 對比一下繼承的方式 VS 實現的方式
  
  * 哪個方式好?實現的方式優於繼承的方式
  
  * why?   ①避免java單繼承的侷限性
  
  *        ②如果多個執行緒要操作同一份資源,更適合使用實現的方式
  
  * @Date: Created in 10:50 2018/10/29
  
  */
  
  public class TestThread2 {
  
  public static void main(String [] args){
  
  //此程式存線上程的安全問題,列印車票時,會出現重票、錯票,後面執行緒同步會講到
  
  Window window=new Window(www.chaoyueyule.net);
  
  Thread thread1=new Thread(window,"視窗一");
  
  Thread thread2=new Thread(window,"視窗二");
  
  Thread thread3=new Thread(window,"視窗三");
  
  thread1.start(www.yinmaoyule178.com );
  
  thread2.start();
  
  thread3.start();
  
  }
  
  }
  
  class Window implements  Runnable{
  
  int ticket=100;
  
  @Override
  
  public void run(){
  
  while (true){
  
  if(ticket > 0){
  
  try {
  
  Thread.currentThread().sleep(100);
  
  } catch (InterruptedException e) {
  
  e.printStackTrace();
  
  }
  
  System.out.println(Thread.currentThread().getName()+"售票,票號為:"+ticket--);
  
  }else {
  
  break;
  
  }
  
  }
  
  }
  
  }
  
  複製程式碼
  
  哪個方式好?實現的方式優於繼承的方式
  
  why? ①避免java單繼承的侷限性
  
  ②如果多個執行緒要操作同一份資源,更適合使用實現的方式
  
  注意:此程式存線上程的安全問題,列印車票時,會出現重票、錯票
  
  第三種方法:使用匿名內部類建立執行緒
  
  複製程式碼
  
  public static void main(String[www.dfgjpt.com/ ] args) {
  
  for(int i = 0 ; i < 10 ; i++){
  
  System.out.println("玩遊戲"+i);
  
  if(i==5){
  
  new Thread(new Runnable() {
  
  @Override
  
  public void run(www.ysyl157.com) {
  
  for(int i = 0 ; i < 10 ;i++){
  
  System.out.println("播放音樂"+i);
  
  }
  
  }
  
  }).start();
  
  }
  
  }
  
  }