多線程程序設計

寫在之前

以下是《Java8編程入門官方教程》中的一些知識,如有錯誤,煩請指正。涉及的程序如需下載請移步http://down1.tupwk.com.cn/qhwkdownpage/978-7-302-38738-1.zip

多線程

多任務包括:基于進程的多任務和基于線程的多任務。進程是正在執行的程序,相當于計算機允許同時運行多個程序的功能;線程是最小的可分派代碼單元,一個程序可以一次執行多個任務,比如文本編輯器可以在打印的同時格式化文本。

單核系統中并發執行的線程共享CPU,每個線程都收到一個CPU時間片;多核系統中,多個線程可以同時執行。多線程的功能在兩個系統中都可以實現。

Thread類和Runnable接口

方法 含義
final String getName() 獲取線程名
final int getPriority() 獲取線程優先級
final boolean isAlive() 確定線程是否仍在運行
final void join() 等待線程終止
static void sleep(long milliseconds) 按照指定的時間掛起線程,以毫秒為單位
void start() 通過調用run方法啟動線程
void run() 線程的進入點

線程創建

兩種方法:都要用Thread類來實例化、訪問、控制線程,僅僅是創建不同。

  1. 實現Runnable接口
  2. 擴展Thread類

比較:一般僅在通過某些方式增強或者修改類的時候才擴展這些類,因此如果不重寫Thread的任何其他方法,最好事先Runnable接口。另外實現Runnable接口可以讓線程繼承除Thread類外的其他類。

Runnable接口只定義了run方法,在run方法中可以調用其他方法,使用其他類,像主線程一樣聲明變量。具體的方法通過程序來說明。

// 通過實現Runnable接口創建進程
class MyThread implements Runnable {
  String thrdName;

  MyThread(String name) {
    thrdName = name;
  }

  // Entry point of thread.
  public void run() {
    System.out.println(thrdName + " starting.");
    try {
      for(int count=0; count < 10; count++) {
        Thread.sleep(400);
        System.out.println("In " + thrdName +
                           ", count is " + count);
      }
    }
    catch(InterruptedException exc) {
      System.out.println(thrdName + " interrupted.");
    }
    System.out.println(thrdName + " terminating.");
  }
}

class UseThreads {
  public static void main(String args[]) {
    System.out.println("Main thread starting.");

    // 1.創建一個可運行的線程對象
    MyThread mt = new MyThread("Child #1");

    // 2.在該對象上構造一個線程
    Thread newThrd = new Thread(mt);

    // 3.開始運行線程
    newThrd.start();

    for(int i=0; i<50; i++) {
      System.out.print(".");
      try {
        Thread.sleep(100);//主線程main延遲了5秒,線程mt延遲了4秒,確保main在mt結束之后才終止
      }
      catch(InterruptedException exc) {
        System.out.println("Main thread interrupted.");
      }
    }

    System.out.println("Main thread ending.");
  }
}

改進:MyThread類沒必要存儲線程名,可以在創建時就為其命名。Thread(Runnable threadob, String name)

//改進版
class MyThread implements Runnable {
  Thread thrd;

  // 建立一個新進程
  MyThread(String name) {
    thrd = new Thread(this, name);//創建時就命名
    thrd.start(); // 開始進程
  }

  // Begin execution of new thread.
  public void run() {
    System.out.println(thrd.getName() + " starting.");
    try {
      for(int count=0; count<10; count++) {
        Thread.sleep(400);
        System.out.println("In " + thrd.getName() +
                           ", count is " + count);
      }
    }
    catch(InterruptedException exc) {
      System.out.println(thrd.getName() + " interrupted.");
    }
    System.out.println(thrd.getName() + " terminating.");
  }
}

class UseThreadsImproved {
  public static void main(String args[]) {
    System.out.println("Main thread starting.");

    MyThread mt = new MyThread("Child #1");s//改進后創建時就啟動進程

    for(int i=0; i < 50; i++) {
      System.out.print(".");
      try {
        Thread.sleep(100);
      }
      catch(InterruptedException exc) {
        System.out.println("Main thread interrupted.");
      }
    }

    System.out.println("Main thread ending.");
  }
}

另一種方法:擴展Thread,必須重寫作為新建成進入點的run方法。

class MyThread extends Thread {

  // 建立一個新的線程.
  MyThread(String name) {
    super(name); // 線程命名
    start(); // 開始線程
  }

  // 線程運行
  public void run() {
    System.out.println(getName() + " starting.");
    try {
      for(int count=0; count < 10; count++) {
        Thread.sleep(400);
        System.out.println("In " + getName() +
                           ", count is " + count);
      }
    }
    catch(InterruptedException exc) {
      System.out.println(getName() + " interrupted.");
    }

    System.out.println(getName() + " terminating.");
  }
}

class ExtendThread {
  public static void main(String args[]) {
    System.out.println("Main thread starting.");

    MyThread mt = new MyThread("Child #1");

    for(int i=0; i < 50; i++) {
      System.out.print(".");
      try {
        Thread.sleep(100);
      }
      catch(InterruptedException exc) {
        System.out.println("Main thread interrupted.");
      }
    } 

    System.out.println("Main thread ending.");
  }
}

創建多個線程

class MyThread implements Runnable {
  Thread thrd;

  // 構造線程
  MyThread(String name) {
    thrd = new Thread(this, name);

    thrd.start(); // 開始線程
  }

  // 執行線程
  public void run() {
    System.out.println(thrd.getName() + " starting.");
    try {
      for(int count=0; count < 10; count++) {
        Thread.sleep(400);
        System.out.println("In " + thrd.getName() +
                           ", count is " + count);
      }
    }
    catch(InterruptedException exc) {
      System.out.println(thrd.getName() + " interrupted.");
    }
    System.out.println(thrd.getName() + " terminating.");
  }
}

class MoreThreads {
  public static void main(String args[]) {
    System.out.println("Main thread starting.");

    MyThread mt1 = new MyThread("Child #1");//創建并開始執行3個線程
    MyThread mt2 = new MyThread("Child #2");
    MyThread mt3 = new MyThread("Child #3");

    for(int i=0; i < 50; i++) {
      System.out.print(".");
      try {
        Thread.sleep(100);
      }
      catch(InterruptedException exc) {
        System.out.println("Main thread interrupted.");
      }
    } 

    System.out.println("Main thread ending.");
  }
}

確定線程的結束

兩種方法:

final boolean isAlive()

class MoreThreads { 
  public static void main(String args[]) { 
    System.out.println("Main thread starting."); 
 
    MyThread mt1 = new MyThread("Child #1"); 
    MyThread mt2 = new MyThread("Child #2"); 
    MyThread mt3 = new MyThread("Child #3"); 
 
    do { 
      System.out.print("."); 
      try { 
        Thread.sleep(100); 
      } 
      catch(InterruptedException exc) { 
        System.out.println("Main thread interrupted."); 
      } 
    } while (mt1.thrd.isAlive() || 
             mt2.thrd.isAlive() || 
             mt3.thrd.isAlive()); //使用Alive等待所有線程終止
 
    System.out.println("Main thread ending."); 
  } 
}

final void join() Throws InterruptedException

等待,直到調用的線程終止

class MyThread implements Runnable {
  Thread thrd;

  // Construct a new thread.
  MyThread(String name) {
    thrd = new Thread(this, name);
    thrd.start(); // start the thread
  }

  // Begin execution of new thread.
  public void run() {
    System.out.println(thrd.getName() + " starting.");
    try {
      for(int count=0; count < 10; count++) {
        Thread.sleep(400);
        System.out.println("In " + thrd.getName() +
                           ", count is " + count);
      }
    }
    catch(InterruptedException exc) {
      System.out.println(thrd.getName() + " interrupted.");
    }
    System.out.println(thrd.getName() + " terminating.");
  }
}

class JoinThreads {
  public static void main(String args[]) {
    System.out.println("Main thread starting.");

    MyThread mt1 = new MyThread("Child #1");
    MyThread mt2 = new MyThread("Child #2");
    MyThread mt3 = new MyThread("Child #3");

    try {
      mt1.thrd.join();//等待,直到線程中終結,下同
      System.out.println("Child #1 joined.");
      mt2.thrd.join();
      System.out.println("Child #2 joined.");
      mt3.thrd.join();
      System.out.println("Child #3 joined.");
    }
    catch(InterruptedException exc) {
      System.out.println("Main thread interrupted.");
    }
    System.out.println("Main thread ending.");
  }
}

線程優先級

可以通過Thread的成員方法來修改線程的優先級。

final void setPriority(int level)

level的值必須在MIN_PRIORITYMAX_PRIORITY的范圍內,即1-10.優先級定義為static final變量。

可以通過Thread的成員方法獲取當前優先級設置。

final int getPriority

//線程的優先級
class Priority implements Runnable { 
  int count; 
  Thread thrd; 
 
  static boolean stop = false; 
  static String currentName; 
 
  /* Construct a new thread. Notice that this  
     constructor does not actually start the 
     threads running. */ 
  Priority(String name) { 
    thrd = new Thread(this, name); 
    count = 0; 
    currentName = name; 
  } 
 
  // Begin execution of new thread. 
  public void run() { 
    System.out.println(thrd.getName() + " starting."); 
    do { 
      count++; 
 
      if(currentName.compareTo(thrd.getName()) != 0) { 
        currentName = thrd.getName(); 
        System.out.println("In " + currentName); 
      } 
 
    } while(stop == false && count < 10000000); 
    stop = true; 
 
    System.out.println("\n" + thrd.getName() + 
                       " terminating."); 
  }
} 


class PriorityDemo { 
  public static void main(String args[]) { 
    Priority mt1 = new Priority("High Priority"); 
    Priority mt2 = new Priority("Low Priority"); 
 
    // set the priorities 
    mt1.thrd.setPriority(Thread.NORM_PRIORITY+2); 
    mt2.thrd.setPriority(Thread.NORM_PRIORITY-2); 
 
    // start the threads 
    mt1.thrd.start(); 
    mt2.thrd.start(); 
 
    try { 
      mt1.thrd.join(); //等待線程結束
      mt2.thrd.join(); 
    } 
    catch(InterruptedException exc) { 
      System.out.println("Main thread interrupted."); 
    } 
 
    System.out.println("\nHigh priority thread counted to " + 
                       mt1.count); 
    System.out.println("Low priority thread counted to " + 
                       mt2.count); 
  } 
}

同步

同步:控制對象訪問的監視器,監視器通過實現“鎖”來工作。當一個對象被一個線程鎖住后,其他線程就不能訪問該對象。當該線程退出時,要為對象解鎖,使其他進程可以訪問他。有兩種同步的方法。

通過synchronized關鍵字修改方法來同步對方法的訪問。當調用方法時,調用線程進入對象監視器,對象監視器鎖住對象。對象被鎖的同時,其他線程不能進入方法,也不能進入対象定義的其他同步方法。當線程從方法返回時,監視器為對象解鎖,允許下一個進程使用對象。

//使用同步方法控制訪問
class SumArray { 
  private int sum; 
 
  synchronized int sumArray(int nums[]) { //sumArray()被同步
    sum = 0; // reset sum 
 
    for(int i=0; i<nums.length; i++) { 
      sum += nums[i]; 
      System.out.println("Running total for " + 
             Thread.currentThread().getName() + 
             " is " + sum); 
      try { 
        Thread.sleep(10); // allow task-switch 
      } 
      catch(InterruptedException exc) { 
        System.out.println("Thread interrupted."); 
      } 
    } 
    return sum; 
  } 
}  
 
class MyThread implements Runnable { 
  Thread thrd; 
  static SumArray sa = new SumArray(); 
  int a[]; 
  int answer; 
 
  // Construct a new thread. 
  MyThread(String name, int nums[]) { 
    thrd = new Thread(this, name); 
    a = nums; 
    thrd.start(); // start the thread 
  } 
 
  // Begin execution of new thread. 
  public void run() { 
    int sum; 
 
    System.out.println(thrd.getName() + " starting."); 
 
    answer = sa.sumArray(a);          
    System.out.println("Sum for " + thrd.getName() + 
                       " is " + answer); 
 
    System.out.println(thrd.getName() + " terminating."); 
  } 
} 
 
class Sync { 
  public static void main(String args[]) { 
    int a[] = {1, 2, 3, 4, 5}; 
 
    MyThread mt1 = new MyThread("Child #1", a); 
    MyThread mt2 = new MyThread("Child #2", a); 

    try {
      mt1.thrd.join();
      mt2.thrd.join();
    }
    catch(InterruptedException exc) {
      System.out.println("Main thread interrupted.");
    }

  } 
} 

當上面提出的方法在無法訪問源代碼時,可以將對這種類定義的方法的調用放入synchronized代碼塊中。

// Use a synchronized block to control access to SumArray.  
class SumArray {  
  private int sum;  
  
  int sumArray(int nums[]) {  //沒有同步!!
    sum = 0; // reset sum  
  
    for(int i=0; i<nums.length; i++) {  
      sum += nums[i];  
      System.out.println("Running total for " +  
             Thread.currentThread().getName() +  
             " is " + sum);  
      try {  
        Thread.sleep(10); // allow task-switch  
      }  
      catch(InterruptedException exc) {  
        System.out.println("Thread interrupted.");  
      }  
    }  
    return sum; 
  }  
}   
  
class MyThread implements Runnable {  
  Thread thrd;  
  static SumArray sa = new SumArray();  
  int a[];  
  int answer; 
 
  // Construct a new thread.  
  MyThread(String name, int nums[]) {  
    thrd = new Thread(this, name);  
    a = nums;  
    thrd.start(); // start the thread  
  }  
  
  // Begin execution of new thread.  
  public void run() {  
    int sum;  
  
    System.out.println(thrd.getName() + " starting.");  
  
    // synchronize calls to sumArray()  
    synchronized(sa) {  //對sumArray的調用被同步
      answer = sa.sumArray(a);           
    }  
    System.out.println("Sum for " + thrd.getName() +  
                       " is " + answer);  
  
    System.out.println(thrd.getName() + " terminating.");  
  }  
}  
  
class Sync {  
  public static void main(String args[]) {  
    int a[] = {1, 2, 3, 4, 5};  
  
    MyThread mt1 = new MyThread("Child #1", a);  
    MyThread mt2 = new MyThread("Child #2", a);  
  
    try {  
      mt1.thrd.join();  
      mt2.thrd.join();  
    } catch(InterruptedException exc) {  
      System.out.println("Main thread interrupted.");  
    }  
  }  
} 

線程通信

一個線程可以通知另一個線程它被阻塞,而其他進程也可以通知它繼續執行。Java使用wait()notify()notifyAll()支持進程間通信。三個方法是Object類實現的,是所有對象的一部分,只能在同步環境中被調用。當一個線程暫時阻塞無法運行,它調用wait(),導致進程睡眠,對象的監視器被釋放,允許其他線程使用該對象。過一段時間后,當另一個線程進入這個監視器的時候,調用notify()notifyAll(),睡眠進程被喚醒。

final void wait() throws InterruptedException//等待,直到被通知
final void wait(long millis) throws InterruptedException//或者等待一定時間后
final void wait(long millis,int nanos) throws InterruptedException
  
final void notify()     //恢復一個等待線程
final void notifyAll()  //通知所有線程

一個使用wait()和notify()的例子

//比較難,請多次閱讀或者運行
class TickTock { 

  String state; // contains the state of the clock
 
  synchronized void tick(boolean running) { 
    if(!running) { // stop the clock 
      state = "ticked";
      notify(); // notify any waiting threads 
      return; 
    } 
 
    System.out.print("Tick "); 

    state = "ticked"; // set the current state to ticked

    notify(); // let tock() run 
    try { 
      while(!state.equals("tocked"))
        wait(); // wait for tock() to complete 
    } 
    catch(InterruptedException exc) { 
      System.out.println("Thread interrupted."); 
    } 
  } 
 
  synchronized void tock(boolean running) { 
    if(!running) { // stop the clock 
      state = "tocked";
      notify(); // notify any waiting threads 
      return; 
    } 
 
    System.out.println("Tock"); 

    state = "tocked"; // set the current state to tocked

    notify(); // let tick() run 
    try { 
      while(!state.equals("ticked"))
        wait(); // wait for tick to complete 
    } 
    catch(InterruptedException exc) { 
      System.out.println("Thread interrupted."); 
    } 
  } 
}  
 
class MyThread implements Runnable { 
  Thread thrd; 
  TickTock ttOb; 
 
  // Construct a new thread. 
  MyThread(String name, TickTock tt) { 
    thrd = new Thread(this, name); 
    ttOb = tt; 
    thrd.start(); // start the thread 
  } 
 
  // Begin execution of new thread. 
  public void run() { 
 
    if(thrd.getName().compareTo("Tick") == 0) { 
      for(int is
    } 
  } 
} 
 
class ThreadCom { 
  public static void main(String args[]) { 
    TickTock tt = new TickTock(); 
    MyThread mt1 = new MyThread("Tick", tt); 
    MyThread mt2 = new MyThread("Tock", tt); 
 
    try { 
      mt1.thrd.join(); 
      mt2.thrd.join(); 
    } catch(InterruptedException exc) { 
      System.out.println("Main thread interrupted."); 
    } 
  } 
}

死鎖:一個線程等待另一個線程來做某事,而后者卻又在等待前者。這就像兩個過于禮貌的謙謙君子,都堅持讓對方先通過大門。

競爭:當兩個或多個線程嘗試同時訪問共享資源,但是沒有進行合適的同步時,就會發生競爭條件。

掛起、繼續執行、停止

一個標志變量用于設置掛起和繼續執行,分別是:suspendrunning

一個標志變量用于停止,設置為stop

//掛起、繼續執行、停止
class MyThread implements Runnable {  
  Thread thrd;  
  boolean suspended;  //當設置為true時掛起線程
  boolean stopped;  //當設置為true時終止線程
    
  MyThread(String name) {  
    thrd = new Thread(this, name);  
    suspended = false;  
    stopped = false; 
    thrd.start();  
  }  
  
  // This is the entry point for thread.  
  public void run() {  
    System.out.println(thrd.getName() + " starting."); 
    try {  
      for(int i = 1; i < 1000; i++) {  
        System.out.print(i + " ");  
        if((i%10)==0) { 
          System.out.println(); 
          Thread.sleep(250); 
        } 
 
        // Use synchronized block to check suspended and stopped. 
        synchronized(this) {  
          while(suspended) {  
            wait();
          }  
          if(stopped) break; 
        }  
      } 
    } catch (InterruptedException exc) {  
      System.out.println(thrd.getName() + " interrupted.");  
    }  
    System.out.println(thrd.getName() + " exiting.");  
  }  
 
  // Stop the thread.  
  synchronized void mystop() {  
    stopped = true;  
 
   // The following ensures that a suspended thread can be stopped. 
    suspended = false; 
    notify(); 
  }  
 
  // Suspend the thread. 
  synchronized void mysuspend() {++++++++++
    suspended = true;  
  }  
 
  // Resume the thread.  
  synchronized void myresume() { 
    suspended = false;  
    notify();  
  }  
}  
  
class Suspend {  
  public static void main(String args[]) {  
    MyThread ob1 = new MyThread("My Thread");  
 
    try {  
      Thread.sleep(1000); // let ob1 thread start executing 
  
      ob1.mysuspend();  
      System.out.println("Suspending thread.");  
      Thread.sleep(1000); 
 
      ob1.myresume();  
      System.out.println("Resuming thread.");  
      Thread.sleep(1000); 
 
 
      ob1.mysuspend();  
      System.out.println("Suspending thread.");  
      Thread.sleep(1000); 
 
      ob1.myresume();  
      System.out.println("Resuming thread.");  
      Thread.sleep(1000); 
 
      ob1.mysuspend();  
      System.out.println("Stopping thread."); 
      ob1.mystop(); 
    } catch (InterruptedException e) {  
      System.out.println("Main thread Interrupted");  
    }  
  
    // wait for thread to finish  
    try {  
      ob1.thrd.join();  
    } catch (InterruptedException e) {  
      System.out.println("Main thread Interrupted");  
    }  
   
    System.out.println("Main thread exiting.");  
  }  
}

主線程

class UseMain { 
  public static void main(String args[]) { 
    Thread thrd; 
 
    // Get the main thread. 
    thrd = Thread.currentThread(); 
 
    // Display main thread's name. 
    System.out.println("Main thread is called: " + 
                       thrd.getName()); 
 
    // Display main thread's priority. 
    System.out.println("Priority: " + 
                       thrd.getPriority()); 
 
    System.out.println(); 
 
    // Set the name and priority. 
    System.out.println("Setting name and priority.\n"); 
    thrd.setName("Thread #1"); 
    thrd.setPriority(Thread.NORM_PRIORITY+3); 
 
    System.out.println("Main thread is now called: " + 
                       thrd.getName()); 
 
    System.out.println("Priority is now: " + 
                       thrd.getPriority()); 
  } 
}
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • 1. Java基礎部分 基礎部分的順序:基本語法,類相關的語法,內部類的語法,繼承相關的語法,異常的語法,線程的語...
    子非魚_t_閱讀 31,765評論 18 399
  • 本文主要講了java中多線程的使用方法、線程同步、線程數據傳遞、線程狀態及相應的一些線程函數用法、概述等。 首先講...
    李欣陽閱讀 2,493評論 1 15
  • Java多線程學習 [-] 一擴展javalangThread類 二實現javalangRunnable接口 三T...
    影馳閱讀 2,987評論 1 18
  • 一直挺喜歡云 一直忙于事物 一直想拍一片自己的天空 我想,我可以。 我想展示自己的世界 我想分享我的快樂 (ps...
    清霜寒露閱讀 161評論 0 3
  • 八月,他們都在新生群里憧憬著大學生活。 無意間,他們再次進入了同一個群聊里...同學們都很開朗,群里每天都很熱鬧。...
    A李哲哲閱讀 958評論 1 6