Java線程篇-線程的狀態和分類概述

2020-12-13 mg驛站

Java開發中線程是經常用到的技術,那麼讓我們來回顧一下一些線程中經常考慮的問題吧

1、線程分為用戶線程和守護線程,有什麼區別2、線程有哪些運行狀態

一、什麼是線程

線程,一個執行實體,正在執行的程序,擔當分配系統資源(CPU、內存)的實體。一個完整的線程包括,需要運行的邏輯和需要運行需要的資源。

二、線程的狀態

線程有哪些狀態呢,在Thread的代碼中的State枚舉已經很清楚了

public enum State {NEW,RUNNABLE,BLOCKED,WAITING,TIMED_WAITING,TERMINATED;}

線程的狀態實際是7種,新建、就緒、運行、阻塞、等待、超時等待、終止。不過一般將就緒和運行都算作運行中

新建:新建的線程對象,還未調用start方法

就緒:已經具備了運行條件,等待CPU服務,這個狀態下,線程在就緒隊列中等待

運行:線程具備運行條件,並獲得了CPU資源

阻塞:線程被掛起,一般是加鎖獲取同步狀態被阻塞,放到阻塞隊列中,阻塞狀態消除後,會進入就緒狀態

等待:等待狀態表示當前線程需要等待其他線程做出一些特定動作(等待通知機制)

超時等待:在等待的基礎上,加上一定時間後返回就緒狀態

終止:線程已經運行完畢

那麼這些狀態是如何切換的呢,借用java並發編程藝術中的一張圖

start方法調用後進入運行狀態,wait、sleep等方法進入等待狀態或,yeild方法進入就緒狀態,lock和synchronized進入阻塞狀態

三、線程的分類

線程可以分為守護線程和用戶線程兩種,那麼什麼是用戶線程,什麼是守護線程呢?

1、用戶線程

用戶線程就是我們平時使用的用來處理邏輯的線程。

2、守護線程

守護線程,是服務線程,程序運行時在後臺提供的一種通用服務的線程。最常見的就是jvm的垃圾回收線程。

那麼守護線程相對普通的線程有什麼不同,是用來做什麼的呢?

1)守護線程主要為其他線程提供服務的2)在jvm中所有用戶線程停止運行後,只剩守護線程了,那麼jvm也會退出

了解了守護線程的特點了,那麼用戶線程和守護線程運行的實際效果呢?

首先看下Thread類型中的setDaemon方法,通過這個方法來設置是守護線程和用戶線程,默認是用戶線程

首先,先創建一個用戶線程,代碼如下

public static void main(String[] args){Thread task = new Thread(new Runnable(){@Overridepublic void run() {System.out.println("start thread ..." +System.currentTimeMillis());try {Thread.sleep(5000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end thread..."+System.currentTimeMillis());}});task.start();try {Thread.sleep(2000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end main..."+System.currentTimeMillis());}

運行結果

start thread ...1589291388584end main...1589291390585end thread...1589291393585

很明顯的看到在main函數結束以後,等待線程技術了,jvm才停止運行

那麼對代碼做一下修改,改為守護線程呢,代碼如下

public static void main(String[] args){Thread task = new Thread(new Runnable(){@Overridepublic void run(){System.out.println("start thread ..." +System.currentTimeMillis());try {Thread.sleep(5000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end thread..."+System.currentTimeMillis());}});task.setDaemon(true);task.start();try {Thread.sleep(2000);} catch (InterruptedException e){e.printStackTrace();}System.out.println("end main..."+System.currentTimeMillis());}

運行結果

start thread ...1589291497224end main...1589291499225

在main函數結束以後,jvm就退出來

守護線程的應用場景:

1、為用戶線程提供服務的線程

2、在任何情況下都能關閉的線程(文件的讀寫等不能直接關閉的操作最好不要使用守護線程)

守護線程的注意事項

1、守護線程狀態的設置必須在線程啟動之前

2、守護線程中創建的線程也是守護線程

那麼關於線程的知識點先說到這裡

上文中說到線程狀態的切換了,分享一個經常討論的問題sleep和wait的區別

常規的答案

1、sleep沒有釋放鎖,wait釋放了鎖

2、sleep是Thread的靜態方法,wait是鎖對象的方法

3、sleep是休眠一段時間,wait需要notify喚醒

是否感覺理解的太淺了,咱們一塊研究的深入一點

分別說一下sleep和wait從開始休眠到喚醒的狀態變化

1、sleep被調用後,釋放CPU資源,並在指定時間內不再去爭奪CPU資源,相當於告訴作業系統,在N時間內不需要管我了,這個過程中線程會進入等待隊列中。當休眠時間結束以後,線程進入就緒狀態,並進入就緒隊列,等待分配系統資源。

這裡經常會有一個誤區,就是認為在線程等到時間結束後,會直接進入運行狀態。線程只是進入就緒狀態。

現在了解了sleep調用過程中發生了什麼,那麼可以考慮下,在線程代碼中會經常發現有Thread.sleep(0)這種休眠0毫秒操作,這是為了什麼呢?

2、wait在調用的時候,首先釋放鎖狀態,然後線程進入到等待狀態,當其他線程調用notify時,會隨機喚醒一個等到線程,個人理解是把線程移入鎖對象的阻塞隊列當中,然後等到獲取鎖狀態,進入運行狀態。如果調用notifyAll方法會把所有的線程都喚醒,去搶佔鎖,在搶佔結束以後,沒有獲取到鎖的線程進入阻塞狀態。

關於Thread先說到這裡,如果有疑問可以留言。

相關焦點

  • java線程的6中狀態,你了解嗎?
    線程的6種狀態線程在生命周期中並不是固定處於某一個狀態而是隨著代碼的執行在不同狀態之間切換。Java 線程狀態變遷如下圖所示java線程狀態切換由上圖可以看出:一個線程在其被創建之後它將處於 NEW(新建)狀態,調用runnable.start()方法後開始運行,
  • Java中volatile特性概述
    A線程就處於就緒狀態,B線程處於運行狀態線程B也需要從主內存中讀取x變量的值,由於線程A沒有對x值做任何修改因此此時B讀取到的數據還是100線程B工作內存中x執行了+1操作,但是未刷新之主內存中此時CPU的執行權切換到了A線程上,由於此時線程B沒有將工作內存中的數據刷新到主內存
  • java的線程創建方式
    Thread類java語言中的Thread類是一個基本的線程類,用於創建線程、中斷線程、獲取線程的基本信息、運行狀態等。我們首先了解下利用Thread類創建線程實例的二種方式。繼承Thread類創建線程//繼承Thread實現自己的線程類class MyThread extends Thread{//重寫run方法,給線程賦予工作任務 @Override public void run() { //任務內容…… System.out.println("當前線程是:"+Thread.currentThread
  • Java中的線程(狀態轉換和線程間通信)
    Java中的線程(狀態轉換和線程間通信)什麼是線程線程是作業系統調度的最小單元,在一個進程中可以創建多個線程,進程中的線程可以共享資源,但是每個線程都有自己的線程棧空間。Java運行是從main方法開始執行,會生成一個名為main線程。Java中的線程Java中的線程Thread類,是用來創建和啟動線程。使用方法Thread.start()來啟動一個線程。實現一個線程通常有這麼幾個方法。1、繼承Thread類,重寫run方法。執行start方法啟動線程。
  • Java編寫線程安全類的7個技巧
    一、無狀態(No State)當多個線程訪問相同的實例或靜態變量時,必須以某種方式來協調對此變量的訪問。最簡單的方法就是避免使用實例或靜態變量。對於沒有實例變量的類,它的方法只使用局部變量和方法參數。以下示例顯示了java.lang.Math類的其中一部分:二、無共享狀態(No Shared State)如果你必須要使用狀態,那麼請不要共享狀態,即狀態應該只屬於一個單一的線程。這種技術的一個例子是SWT或Swing圖形用戶界面框架的事件處理線程。您可以通過擴展Thread類並添加實例變量來實現「本地線程」(thread-local)實例變量。
  • JAVA多線程 集合同步
    休眠時間結束後thread-1進入運行狀態去執行synchronizedArrayList的add(i)方法,但由於synchronizedArrayList已被synchronized限制(你必須清楚的是只有返回的迭代器是非線程安全外,synchronizedArrayList其餘的所有方法都是完全的線程安全的), 因此thread-1必須等待thread-2釋放synchronizedArrayList
  • 【堪稱經典】JAVA多線程和並發基礎面試問答
    當我們調用線程的start()方法時,狀態被改變為Runnable。線程調度器會為Runnable線程池中的線程分配CPU時間並且講它們的狀態改變為Running。其他的線程狀態還有Waiting,Blocked和Dead。讀這篇文章可以了解更多關於線程生命周期的知識。6. 可以直接調用Thread類的run()方法麼?
  • java線程的基礎問題講解
    1.1並發編程的目的:並發編程是為了讓程序運行得更快,當 並不是啟動更多的線程就能讓程序最大限度地並發執行,受限於死鎖和上下文切換問題。上下文切換:CPU通過分配算法循環分配任務,當前任務執行一個時間片後會切換到下一個任務,但是會保存上一個任務的狀態,以便下次切換為這個任務的時候,可以重新加載這個任務,所以任務從保存到加載為一個上下文切換。所以上下文切換會影響線程的執行速度。
  • Java面試題-多線程篇十三
    兩種方式:java.lang.Thread 類的實例就是一個線程但是它需要調用java.lang.Runnable接口來執行,由於線程類本身就是調用的Runnable接口所以你可以繼承java.lang.Thread 類或者直接調用Runnable接口來重寫run()方法實現線程。
  • Java之線程狀態的簡單介紹
    各位小夥伴們大家好,在之前的文章中,小編介紹了線程安全問題的相關知識,這次小編要針對線程的狀態做一些相關介紹。當線程被創建並啟動以後,它既不是一啟動線程就進入了執行狀態,也不是一直處於執行狀態。Blocked(鎖阻塞):當一個線程試圖獲取一個鎖對象,而該對象被其他的線程持有,則該線程進入Blocked狀態;當該線程持有鎖時,該線程將變成Runnable狀態。Waiting(無限等待):一個線程在等待另一個線程執行動作是,該線程進入Waiting狀態。
  • Java 線程面試題 Top 50
    java.lang.Thread 類的實例就是一個線程但是它需要調用java.lang.Runnable接口來執行,由於線程類本身就是調用的Runnable接口所以你可以繼承java.lang.Thread 類或者直接調用Runnable接口來重寫run()方法實現線程。4) 用Runnable還是Thread?
  • Java基礎知識點面試手冊(線程+JDK8)
    方式二通過主線程Join()方式三通過線程執行時Join()線程和進程的區別(必考)答:進程是一個 「執行中的程序」,是系統進行資源分配和調度的一個獨立單位;線程是進程的一個實體,一個進程中擁有多個線程,線程之間共享地址空間和其它資源(
  • java多線程之Thread構造函數(源碼分析)
    在上一篇文章中對線程狀態生命周期和常見的線程api進行了一個講解。這篇文章開始著重對其構造方法進行一個說明,也將揭曉為什麼我們調用了start方法就能啟動一個線程。一、守護線程和非守護線程我們獲取線程的id的時候會發現每次都不是0,這是因為在java虛擬機運行一個線程的時候會默認啟動一些其他的線程,來為我們的線程服務。
  • java基礎|驗證ArrayList的線程不安全
    javaDEMO本網站記錄了最全的各種JavaDEMO ,保證下載,複製就是可用的,包括基礎的, 集合的, spring的, Mybatis的等等各種
  • 「轉載」java架構之路(多線程)synchronized詳解以及鎖的膨脹升級...
    鎖的分類java中我們聽到很多的鎖,什麼顯示鎖,隱式鎖,公平鎖,重入鎖等等,下面我來總結一張圖來供大家學習使用。這次博客我們主要來說我們的隱示鎖,就是我們的無鎖到重量級鎖。無鎖狀態:開始時應該這樣的,線程A和線程B要去爭搶鎖對象,但還未開始爭搶,鎖對象的對象頭是無鎖的狀態也就是25bit位存的hashCode,4bit位存的對象的分代年齡,1bit位記錄是否為偏向鎖,2bit位記錄狀態,優先看最後2bit位,是01,所以說我們的對象可能無鎖或者偏向鎖狀態的
  • 面試前必看Java線程面試題
    java.lang.Thread 類的實例就是一個線程但是它需要調用java.lang.Runnable接口來執行,由於線程類本身就是調用的Runnable接口所以你可以繼承java.lang.Thread 類或者直接調用Runnable接口來重寫run()方法實現線程。更多詳細信息請點擊這裡。5. 什麼是線程安全?Vector是一個線程安全類嗎?
  • 大數據基礎:Java多線程入門
    更好的編程模型 java為多線程編程提供了考究並且一致的編程模型,使開發人員能夠更加專注於問題解決,即為所遇到的問題建立適合的模型,而不是絞盡腦汁地考慮如何將其多線程化。一旦開發人員建立好了模型,稍作修改總是能夠方便地映射到Java提供的多線程編程模型上。
  • Java多線程synchronized
    如何解決線程安全問題?那麼一般來說,是如何解決線程安全問題的呢?基本上所有的併發模式在解決線程安全問題時,都採用「序列化訪問臨界資源」的方案,即在同一時刻,只能有一個線程訪問臨界資源,也稱作同步互斥訪問。通常來說,是在訪問臨界資源的代碼前面加上一個鎖,當訪問完臨界資源後釋放鎖,讓其他線程繼續訪問。
  • java垃圾回收以及jvm參數調優概述
    目錄如下:一、概述二、java垃圾回收算法 2.1 引用計數算法2.2 根搜索算法2.3 標記-清除算法 2.4 複製算法2.5 標記-整理算法 2.6 分代收集算法 三、垃圾回收器3.1 垃圾回收器概述3.2 串行垃圾回收器  Serial
  • JAVA並發編程:線程並發工具類Callable、Future 和FutureTask的使用
    FutureTask 類實現了RunnableFuture 接口,RunnableFuture 繼承了Runnable接口和Future 接口,而FutureTask 實現了RunnableFuture 接口。所以它既可以作為Runnable被線程執行,又可以作為Future 得到Callable 的返回值。FutureTask,可取消的異步計算。