JAVA多線程Thread VS Runnable詳解

jopen 9年前發布 | 32K 次閱讀 線程 Java開發

進程與線程

進程是程序在處理機中的一次運行。一個進程既包括其所要執行的指令,也包括了執行指令所需的系統資源,不同進程所占用的系統資源相對獨立。所以進程是重量級的任務,它們之間的通信和轉換都需要操作系統付出較大的開銷。

線程是進程中的一個實體,是被系統獨立調度和分派的基本單位。線程自己基本上不擁有系統資源,但它可以與同屬一個進程的其他線程共享進程所擁有的全部資源。所以線程是輕量級的任務,它們之間的通信和轉換只需要較小的系統開銷。

Java支持多線程編程,因此用Java編寫的應用程序可以同時執行多個任務。Java的多線程機制使用起來非常方便,用戶只需關注程序細節的實現,而不用擔心后臺的多任務系統。

Java語言里,線程表現為線程類。Thread線程類封裝了所有需要的線程操作控制。在設計程序時,必須很清晰地區分開線程對象和運行線程,可 以將線程對象看作是運行線程的控制面板。在線程對象里有很多方法來控制一個線程是否運行,睡眠,掛起或停止。線程類是控制線程行為的唯一的手段。一旦一個 Java程序啟動后,就已經有一個線程在運行。可通過調用Thread.currentThread方法來查看當前運行的是哪一個線程。

線程創建的兩種方法

JAVA中創建線程可以通過繼承Thread類和實現Runnable接口來創建一個線程。Runnable方式可以避免Thread 方式由于JAVA單繼承特性帶來的缺陷。Runnable的代碼可以被多個線程(Thread實例)共享,適合于多個線程處理同一資源的情況。

  • 繼承Thread類

class MyThread extends Thread{
  .....
  @Override
  public void run(){
  }
}
MyThread mt=new MyThread();
mt.start();

  • 實現Runnable接口

class MyThread implements Runnable{
  ....
  @Override
  public void run(){
  }
}
MyThread mt=new MyThread();
Thread td=new Thread(mt);
sd.start();

Thread&Runnable分別模擬賣火車票

  • Thread方式

class MyThread extends Thread
{
  private int ticketsCont=5; //一共有5張火車票
  private String name; //窗口, 也即是線程的名字
  public MyThread(String name){
    this.name=name;
  }
  @Override
  public void run(){
    while(ticketsCont>0){
      ticketsCont--; //如果還有票,就賣掉一張票
      System.out.println(name+"賣掉了1張票,剩余票數為:"+ticketsCont);
    }
  }
}
public class TicketsThread
{
  public static void main(String args[]){
    //創建三個線程,模擬三個窗口賣票
    MyThread mt1=new MyThread("窗口1");
    MyThread mt2=new MyThread("窗口2");
    MyThread mt3=new MyThread("窗口3");
    //啟動三個線程,也即是窗口,開始賣票
    mt1.start();
    mt2.start();
    mt3.start();
  }
}

  • Runnable方式

class MyThread2 implements Runnable
{
  private int ticketsCont=1000; //一共有5張火車票

  @Override
  public void run(){
      while(true){
         synchronized(this){
          if(ticketsCont<=0){
            break;
          }
          ticketsCont--; //如果還有票,就賣掉一張票

          System.out.println(Thread.currentThread().getName()+"賣掉了1張票,剩余票數為:"+ticketsCont);
          /*try{
            Thread.sleep(50);  //通過阻塞程序來查看效果
          }
          catch(Exception e){
            System.out.println(e);
          }*/
        }
      }
  }
  /*@Override  //不加同步鎖
  public void run(){
    while(ticketsCont>0){
      ticketsCont--; //如果還有票,就賣掉一張票
      System.out.println(Thread.currentThread().getName()+"賣掉了1張票,剩余票數為:"+ticketsCont);
    }
  }*/
}
public class TicketsRunnable
{
  public static void main(String args[]){
    MyThread2 mt=new MyThread2();
    //創建三個線程來模擬三個售票窗口
    Thread th1=new Thread(mt,"窗口1");
    Thread th2=new Thread(mt,"窗口2");
    Thread th3=new Thread(mt,"窗口3");
    //啟動三個線程,也即是三個窗口,開始賣票
    th1.start();
    th2.start();
    th3.start();
  }
}

線程的生命周期

 JAVA多線程Thread VS Runnable詳解

  • 創建:新建一個線程對象,如Thread thd=new Thread()
  • 就緒:創建了線程對象后,調用了線程的start()方法(此時線程知識進入了線程隊列,等待獲取CPU服務 ,具備了運行的條件,但并不一定已經開始運行了)
  • 運行:處于就緒狀態的線程,一旦獲取了CPU資源,便進入到運行狀態,開始執行run()方法里面的邏輯
  • 終止:線程的run()方法執行完畢,或者線程調用了stop()方法,線程便進入終止狀態
  • 阻塞:一個正在執行的線程在某些情況系,由于某種原因而暫時讓出了CPU資源,暫停了自己的執行,便進入了阻塞狀態,如調用了sleep()方法

線程的分類

  • 用戶線程:運行在前臺,執行具有的任務程序的主線程,連接網絡的子線程等都是用戶線程
  • 守護線程:運行在后頭,為其他前臺線程服務。一旦所有用戶線程都結束運行,守護線程會隨JVM一起結束工作。可以通過調用Thread類的 setDaemon(true)方法來設置當前的線程為守護線程,該方法必須在start()方法之前調用,否則會拋出 IllegalThreadStateException異常。在守護線程中產生的新線程也是守護線程。不是所有的任務都可以分配給守護線程來執行,比如 讀寫操作或者計算邏輯。

守護線程測試案例

 JAVA多線程Thread VS Runnable詳解

場景:一個主線程,一個守護線程,守護線程會在很長一段時間內向本地文件中寫入數據,主線程進入阻塞狀態等待用戶的輸入,一旦確認了用戶的輸入阻塞就會解除掉,主線程繼續運行直到結束,守護線程也會隨虛擬機一同結束。

import java.io.*;
import java.util.Scanner;
class Daemon  implements Runnable
{
  @Override
  public void run(){
    System.out.println("進入守護線程");
    try{
      writeToFile();
    }
    catch(Exception e){
      e.printStackTrace();
    }
    System.out.println("退出守護線程");
  }
  private void writeToFile() throws Exception{
      File filename=new File("F:/慕課網(imooc)/細說多線程之Thread VS Runnable/daemon.txt");
      OutputStream os=new FileOutputStream(filename,true);
      int count=0;
      while(count<999){
        os.write(("\r\nword"+count).getBytes());
        System.out.println("守護線程"+Thread.currentThread().getName()+"向文件中寫入word"+count++);
        Thread.sleep(1000);
      }
  }
}
public class DaemonThreadDemo
{
  public static void main(String args[]){
    System.out.println("進入主線程"+Thread.currentThread().getName());
    Daemon daemonThread=new Daemon();
    Thread thread =new Thread(daemonThread);
    thread.setDaemon(true);
    thread.start();
    Scanner sc=new Scanner(System.in);
    sc.next();
    System.out.println("退出主線程"+Thread.currentThread().getName());
  }
}

測試結果

 JAVA多線程Thread VS Runnable詳解

使用jstack生成線程快照

jstack工具到jdk安裝目錄bin文件夾下。jstack能生成JVM當前時刻線程的快照(threaddump, 即當前進程中所有線程的信息)。幫助定位程序問題出現的原因,如長時間停頓、CPU占用率過高等。

使用方法

jstack [-l] <pid> : [-l]可有可無,表示關于鎖的二位信息;<pid>表示進程ID。

 JAVA多線程Thread VS Runnable詳解

總結

建議使用Runnable這種方式創建線程。 程序中的同一資源指的是同一個Runnable對象。安全的賣票程序中需要加入同步synchronized。

如以上文章或鏈接對你有幫助的話,別忘了在文章結尾處輕輕點擊一下 “ 還不錯 ”按鈕或到頁面右下角點擊 “ 贊一個 ” 按鈕哦。你也可以點擊頁面右邊“ 分享 ”懸浮按鈕哦,讓更多的人閱讀這篇文章。


原文  http://www.cnblogs.com/Li-Cheng/p/4332179.html

 本文由用戶 jopen 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!