關於線程基礎

線程的生命週期

這裏寫圖片描述

創建:新建一個線程對象,new Thread = new Thread()

就緒:創建了線程對象後,調用了start()方法(注意線程此時只是進入了線程隊列,等待獲取CPU服務,具備了運行條件,但不代表可以馬上運行)

運行:處於就緒狀態的線程,一旦獲取了CPU服務,便進入了運行狀態,開始執行run()方法裏面的邏輯

終止:當線程run()方法執行完畢,或者線程調用了stop()方法,那麼線程就會終止

阻塞:一個正在執行的線程在某些情況下,由於某種原因暫時讓出了CPU資源,暫停了自己的執行,便進入了阻塞狀態,如調用了sleep()方法

線程狀態。 線程可以處於以下狀態之一:
NEW :尚未啓動的線程處於此狀態。 1
RUNNABLE :在Java虛擬機中執行的線程處於此狀態。 2
BLOCKED :被阻塞等待監視器鎖定的線程處於此狀態。 3
WAITING :正在等待另一個線程執行特定動作的線程處於此狀態。 4
TIMED_WAITING :正在等待另一個線程執行動作達到指定等待時間的線程處於此狀態。 5
TERMINATED :已退出的線程處於此狀態。 6

其中:

New對應的是創建
RUNNABLE對應的是就緒狀態&運行狀態
BLOCKED、WAITING、TIMED_WAITING對應的是阻塞狀態
TERMINATED對應的是終止狀態

守護線程

java線程有兩類

1、用戶線程:運行在前臺,執行具體任務

程序的主線程、連接網絡的子線程等都是用戶線程
(這類線程我們看的見他們在工作,但是摸不着)

2、守護線程:運行在後臺,爲其他前臺線程服務

特點:一旦所有的用戶線程都結束運行,那麼守護線程會覺得自己沒有存在的必要了,就會隨着JVM一起停止工作
應用:數據庫連接池中的監控線程(如連接池中連接的個數、連接超時的時間等)
…………JVM虛擬機啓動後的監控線程(如檢測內存使用的情況等),還有最常見的就是:垃圾回收線程!

3、如何設置守護線程

可以通過調用Tread類的setDaemon(true)方法來把當前的線程設置爲守護線程

注意事項:
1、setDaemon(true)的這個方法必須在start()方法前調用,不然會拋出illgalThreadstateException異常(首字母應大寫,這裏是博客的字體,i的大寫與l傻傻分不清楚)
2、在守護線程中產生的新線程也是守護線程
3、不是所有的任務都可以分配給守護線程來執行,如讀寫操作或者計算邏輯(如果守護線程執行讀寫操作或者計算邏輯,如果用戶線程都結束了,那麼守護線程也停止了,那麼程序執行不完就崩潰了)

代碼演示

場景:守護線程向文件中不斷的輸出字符串,當主線程阻塞停止時,守護線程退出關閉
import java.io.File;
import java.io.FileOutputStream;
import java.io.OutputStream;
import java.util.Scanner;

class DemonThread implements Runnable{
//用Runnable是爲了提高程序的可擴展性
    @Override
    public void run() {
        System.out.println("進入守護線程:"+Thread.currentThread().getName());
        try {
            writeToFile();
        } catch (Exception e) {
            e.printStackTrace();
        }
        //如果守護線程結束,那麼就打印退出守護線程
        System.out.println("退出守護線程:"+Thread.currentThread().getName());
    }
    public void writeToFile() throws Exception{//這裏是爲了方便把所有的異常全給拋出去了
        File file = new File("d:"+File.separator+"demo.text");//中間的相當於路徑的斜線/
        OutputStream os = new FileOutputStream(file,true);
        int count = 0;
        while(count<999){
            os.write(("\r\nword:"+count).getBytes());
            System.out.println("守護線程"+Thread.currentThread().getName()
                    +"向文件中寫入了"+count);
            count++;
            Thread.sleep(1000); 
        }
    }
}
public class DemonThreadDemo {

    public static void main(String[] args) {
        System.out.println("進入主線程"+Thread.currentThread().getName());
        DemonThread dt = new DemonThread();
        Thread thread = new Thread(dt);
        thread.setDaemon(true);
        thread.start();
        //主線程要乾的操作,鍵盤輸入導致線程阻塞
        Scanner sc = new Scanner(System.in);
        sc.next();
        System.out.println("退出主線程"+Thread.currentThread().getName());
    }
}

結果爲:
控制檯
這裏寫圖片描述
d:/demo.text
這裏寫圖片描述


使用jstack生成線程快照

//在jdk的文件夾 lib下,有jstack.exe(命令行工具)還有jvisualvm.exe(界面化工具)

jstack

作用:生成JVM當前時刻線程的快照(threaddump,即當前進程中的所有的線程信息)。

目的:幫助定位程序問題出現的原因,如長時間停頓、CPU佔用率過高等。

這裏寫圖片描述

常用參數:(以下參數可有可無)

-F jstack [-l] pid無法響應時,強制打印堆棧

-l l長列表. 打印關於鎖的附加信息,例如屬於java.util.concurrent的ownable synchronizers列表.

-m 混合模式輸出(包括java和本地c/c++片段)堆棧。

pid: java應用程序的進程號,可以通過來任務管理器獲得;
executable:產生core dump的java可執行程序;
core:打印出的core文件;
remote-hostname-or-ip:遠程debug服務器的名稱或IP;
server-id: 唯一id,假如一臺主機上多個遠程debug服務;
這裏寫圖片描述


補充:

爲了保證程序的可擴展性,建議使用實現Runnable接口的這種方式創建線程
1.程序中的同一資源指的是同一個Runnable對象,,,如果把其他類的對象也作爲共享對象,那麼繼承Tread類和實現Runnable接口,是都可以處理同一資源的

2.安全的賣票程序中需要加入同步(Synchronized)

發佈了25 篇原創文章 · 獲贊 12 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章