1. 程式人生 > >【併發程式設計】Thread類的詳細介紹

【併發程式設計】Thread類的詳細介紹


本部落格系列是學習併發程式設計過程中的記錄總結。由於文章比較多,寫的時間也比較散,所以我整理了個目錄貼(傳送門),方便查閱。

併發程式設計系列部落格傳送門


Thread類是Java中實現多執行緒程式設計的基礎類。本篇部落格就來介紹下Thread類的常用API和常見用法。

Thread類常用的方法如下:

  • Thread.activeCount():這個方法用於返回當前執行緒的執行緒組中活動執行緒的數量,返回的值只是一個估計值,因為當此方法遍歷內部資料結構時,執行緒數可能會動態更改。)。
  • Thread.checkAccess(): 檢驗當前正在執行的執行緒是否有許可權修改thread的屬性,這個方法我們一般不自己進行呼叫,Thread類的set方法在進行屬性修改時都會先呼叫這個方法。
  • Thread.currentThread():獲取當前正在執行的執行緒。
  • Thread.dumpStack():輸出執行緒棧,一般在debug的時候呼叫。
  • Thread.enumerate(Thread tarray[]):??使用場景。
  • Thread.getAllStackTraces():獲取系統中所有執行緒的執行緒棧資訊。
  • thread.getName():獲取執行緒的名字。
  • thread.getPriority():獲取執行緒的優先順序。
  • thread.getStackTrace():獲取堆疊資訊。
  • thread.getState():獲取執行緒狀態。
  • thread.getThreadGroup():獲取執行緒所線上程組。
  • thread.interrupt():使得指定執行緒中斷阻塞狀態,並將阻塞標誌位置為true。
  • thread.interrupted():測試當前執行緒是否被中斷。
  • thread.isAlive():判斷執行緒是否還存活著。
  • thread.isDaemon():判斷執行緒是否是守護執行緒。
  • thread.join():在當前執行緒中加入指定執行緒,使得當前執行緒必須等待指定執行緒執行結束之後,才能結束。可以理解成執行緒插隊、等待該執行緒終止。
  • Thread.sleep(long):強制執行緒睡眠一段時間。
  • thread.start():啟動一個執行緒。
  • thread.setName(name):設定執行緒的名字。
  • thread.setPriority(priority):設定執行緒的優先順序。
  • thread.setDaemon(true):將指定執行緒設定為守護執行緒。
  • thread.yield():使得當前執行緒退讓出CPU資源,把CPU排程機會分配給同樣執行緒優先順序的執行緒。
  • object.wait()、object.notify()、object.notifyAll():Object類提供的執行緒等待和執行緒喚醒方法。

示例程式碼


public class MyThread  {

    public static void main(String[] args) {
          Thread thread = Thread.currentThread();
        
                //這個方法返回的是當前執行緒所線上程組以及這個執行緒組的子執行緒組內活動的執行緒數
                //這個值是一個估計值,所以這個方法的應用場景不大
                int activeCount = Thread.activeCount();
                System.out.println("當前系統中活動執行緒數["+activeCount+"]");
        
                //向標準錯誤輸出流輸出當前的執行緒棧,不會阻斷程式的繼續執行
                Thread.dumpStack();
        
                //獲取所有執行緒棧資訊
                Map<Thread, StackTraceElement[]> allStackTraces = Thread.getAllStackTraces();
        
                //獲取類載入器
                ClassLoader contextClassLoader = thread.getContextClassLoader();
        
                //獲取當前執行緒名字
                String threadName = thread.getName();
                System.out.println("current thread name["+threadName+"]");
        
                //獲取當前執行緒ID
                long threadId = thread.getId();
                System.out.println("current thread id["+threadId+"]");
        
                //獲取當前執行緒的優先順序,一共有1~10總共10個優先順序,這個優先順序並不是在
                //所有平臺都生效的
                int priority = thread.getPriority();
                System.out.println("current thread priority["+priority+"]");
        
                StackTraceElement[] stackTrace = thread.getStackTrace();
                System.out.println("-------------stackTrace info--------------");
                for (int i = 0; i < stackTrace.length; i++) {
                    StackTraceElement element = stackTrace[i];
                    System.out.println("className:["+element.getClassName()+"]");
                    System.out.println("fileName:["+element.getFileName()+"]");
                    System.out.println("line nunber:["+element.getLineNumber()+"]");
                    System.out.println("method name:["+element.getMethodName()+"]");
                    System.out.println("is native method:["+element.isNativeMethod()+"]");
                    System.out.println("------------------------------------------");
                }
        
                Thread.State state = thread.getState();
                System.out.println("thread state:["+state+"]");
        
                ThreadGroup threadGroup = thread.getThreadGroup();
                String threadGroupName = threadGroup.getName();
                System.out.println("thread group name:["+threadGroupName+"]");
        
                //執行緒睡眠,呼叫sleep方法會使得執行緒進入timed_waiting狀態,如果執行緒已經
                //獲得了鎖資源,呼叫sleep方法是不會釋放這個鎖的
                Thread.sleep(2000,500);
                Thread.sleep(1000);
                TimeUnit.SECONDS.sleep(2);
        
                Thread thread1 = new Thread(new Runnable() {
                    @SneakyThrows
                    @Override
                    public void run() {
                        TimeUnit.SECONDS.sleep(100);
                    }
                });
                thread1.start();
                thread1.join(50);

    }


}

守護執行緒

守護執行緒可以理解為服務執行緒,他們的作用就是服務於其他使用者執行緒。當系統中不存在其他使用者執行緒時,這些守護執行緒也會自動消亡。比如JVM的垃圾清理執行緒就是守護執行緒。我們可以使用如下方法檢視和設定執行緒是否是守護執行緒。

thread.isDaemon();
thread.setDaemon(true);

join方法

呼叫執行緒的join方法會使得呼叫執行緒進入waiting狀態,直到被呼叫的執行緒執行結束,呼叫執行緒才會重新獲得執行的機會。


public class MyThread  {

    public static void main(String[] args) throws Exception {
    
        Thread thread1 = new Thread(new Runnable() {
            @SneakyThrows
            @Override
            public void run() {
                TimeUnit.SECONDS.sleep(100);
            }
        });
        thread1.start();
        thread1.join();
        System.out.println("main thread end...");
    }

}

上面的程式碼中,main執行緒呼叫了thread1的join方法,main執行緒會被掛起進入waiting狀態,直到thread1執行完畢之後,main執行緒才有機會重新獲得執行機會。

join方法還有一個過載方法,這個方法可以指定超時時間。

thread1.join(50);

如果thread1執行緒在50ms內還沒執行完,main執行緒就可以重新獲得執行機會。

yeild方法

呼叫執行緒的yield方法不是一定會成功。

  • 退讓成功時,退讓執行緒會由Running(執行)轉為Runnable(就緒)狀態。
  • 退讓了的執行緒,與其他同優先順序級別的執行緒一樣,同樣有再次獲取CPU使用權的機會。

中斷

先貼上一段網友對執行緒中斷的總結。

  • 除非是執行緒自己interrupt()自己,否則checkAccess()方法都會被呼叫,並可能丟擲一個SecurityException異常。
    如果當前執行緒處於blocked阻塞(因為呼叫wait、sleep和join造成的)狀態時被interrupt了,那麼[中斷標誌位]將被清除,並且收到一個InterruptedException異常。
  • 如果當前執行緒處於blocked阻塞(因為NIO的InterruptibleChannel進行的I/O操作造成的)狀態時被interrupt了,則會關閉channel,[中斷標誌位]將會被置為true,並且當前執行緒會收到一個ClosedByInterruptException異常。
  • 如果當前執行緒處於blocked阻塞(因為NIO的Selector造成的)狀態時被interrupt了,那麼[中斷標誌位]將被置為true,然後當前執行緒會立即從選擇器區域返回並返回值(可能為非零的值)。
  • 如果前面的情況都沒有發生,則執行緒會將[中斷標誌位]將被置為true。

更加易懂的說法(不包括NIO部分):

  • interrupt()方法並不是中斷執行緒,而是中斷阻塞狀態,或者將執行緒的[中斷標誌位]置為true。
  • 對於未阻塞的執行緒,interrupt()只是造成[中斷標誌位]=rue,執行緒本身執行狀態不受影響。
  • 對於阻塞的執行緒,interrupt()會中斷阻塞狀態,使其轉換成非阻塞狀態,並清除[中斷標誌位]。
  • 造成阻塞狀態的情況有:sleep()、wait()和join()。
  • 阻塞狀態的執行緒被中斷時,只是中斷了阻塞狀態,即sleep()、wait()和join(),執行緒本身還在繼續執行。