1. 程式人生 > >java執行緒 全面詳細的講解

java執行緒 全面詳細的講解

Java執行緒:概念與原理

一、作業系統中執行緒和程序的概念

現在的作業系統是多工作業系統。多執行緒是實現多工的一種方式。

程序是指一個記憶體中執行的應用程式,每個程序都有自己獨立的一塊記憶體空間,一個程序中可以啟動多個執行緒。比如在Windows系統中,一個執行的exe就是一個程序。

執行緒是指程序中的一個執行流程,一個程序中可以執行多個執行緒。比如Java.exe程序中可以執行很多執行緒。執行緒總是屬於某個程序,程序中的多個執行緒共享程序的記憶體。

同時執行是人的感覺,線上程之間實際上輪換執行。

二、Java中的執行緒

Java

中,執行緒指兩件不同的事情:
1
java.lang.Thread類的一個例項;

2、執行緒的執行。

使用java.lang.Thread類或者java.lang.Runnable介面編寫程式碼來定義、例項化和啟動新執行緒。

一個Thread類例項只是一個物件,像Java中的任何其他物件一樣,具有變數和方法,生死於堆上。

Java中,每個執行緒都有一個呼叫棧,即使不在程式中建立任何新的執行緒,執行緒也在後臺執行著。

一個Java應用總是從main()方法開始執行,mian()方法執行在一個執行緒內,它被稱為主執行緒。

一旦建立一個新的執行緒,就產生一個新的呼叫棧。

執行緒總體分兩類:使用者執行緒和守候執行緒。

當所有使用者執行緒執行完畢的時候,JVM自動關閉。但是守候執行緒卻不獨立於JVM,守候執行緒一般是由作業系統或者使用者自己建立的

Java執行緒:建立與啟動

一、定義執行緒

1、擴充套件java.lang.Thread類。

此類中有個run()方法,應該注意其用法:

public void run()

如果該執行緒是使用獨立的Runnable執行物件構造的,則呼叫該Runnable物件的run方法;否則,該方法不執行任何操作並返回。

Thread的子類應該重寫該方法。

2

、實現java.lang.Runnable介面。

void run()

使用實現介面Runnable的物件建立一個執行緒時,啟動該執行緒將導致在獨立執行的執行緒中呼叫物件的run方法。

方法run的常規協定是,它可能執行任何所需的操作。

二、例項化執行緒

1、如果是擴充套件java.lang.Thread類的執行緒,則直接new即可。

2、如果是實現了java.lang.Runnable介面的類,則用Thread的構造方法:

Thread(Runnable target) 
Thread(Runnable target, String name) 
Thread(ThreadGroup group, Runnable target) 
Thread(ThreadGroup group, Runnable target, String name) 
Thread(ThreadGroup group, Runnable target, String name, long stackSize)

三、啟動執行緒

線上程的Thread物件上呼叫start()方法,而不是run()或者別的方法。

在呼叫start()方法之前:執行緒處於新狀態中,新狀態指有一個Thread物件,但還沒有一個真正的執行緒。

在呼叫start()方法之後:發生了一系列複雜的事情

啟動新的執行執行緒(具有新的呼叫棧);

該執行緒從新狀態轉移到可執行狀態;

當該執行緒獲得機會執行時,其目標run()方法將執行。

注意:對Java來說,run()方法沒有任何特別之處。像main()方法一樣,它只是新執行緒知道呼叫的方法名稱(和簽名)。因此,在Runnable上或者Thread上呼叫run方法是合法的。但並不啟動新的執行緒。

四、例子

1、實現Runnable介面的多執行緒例子

/** 
實現Runnable介面的類

* @author leizhimin 2008-9-13 18:12:10 
*/
publicclass DoSomethingimplements Runnable {
    
private String name;

    
public DoSomething(String name) {
        
this.name = name;
    } 

    
publicvoid run() {
        
for (int i = 0; i < 5; i++) {
            
for (long k = 0; k < 100000000; k++) ;
            System.out.println(name + 
": " + i);
        } 
    } 
}

/** 
測試Runnable類實現的多執行緒程式

* @author leizhimin 2008-9-13 18:15:02 
*/
publicclass TestRunnable {
    
publicstaticvoid main(String[] args) {
        DoSomething ds1 = 
new DoSomething("阿三");
        DoSomething ds2 = 
new DoSomething("李四");

        Thread t1 = 
new Thread(ds1);
        Thread t2 = 
new Thread(ds2);

        t1.start(); 
        t2.start(); 
    } 
}

執行結果:

李四: 0
阿三: 0 
李四: 1 
阿三: 1 
李四: 2 
李四: 3 
阿三: 2 
李四: 4 
阿三: 3 
阿三: 4 

Process finished with exit code 0

2、擴充套件Thread類實現的多執行緒例子

/** 
測試擴充套件Thread類實現的多執行緒程式

* @author leizhimin 2008-9-13 18:22:13 
*/
publicclass TestThreadextends Thread{ 
    
public TestThread(String name) {
        
super(name);
    } 

    
publicvoid run() {
        
for(int i = 0;i<5;i++){
            
for(long k= 0; k <100000000;k++);
            System.out.println(
this.getName()+" :"+i);
        } 
    } 

    
publicstaticvoid main(String[] args) {
        Thread t1 = 
new TestThread("阿三");
        Thread t2 = 
new TestThread("李四");
        t1.start(); 
        t2.start(); 
    } 
}

執行結果:

阿三 :0
李四 :0 
阿三 :1 
李四 :1 
阿三 :2 
李四 :2 
阿三 :3 
阿三 :4 
李四 :3 
李四 :4 

Process finished with exit code 0

對於上面的多執行緒程式程式碼來說,輸出的結果是不確定的。其中的一條語句for(long k= 0; k <100000000;k++);是用來模擬一個非常耗時的操作的。

五、一些常見問題

1、執行緒的名字,一個執行中的執行緒總是有名字的,名字有兩個來源,一個是虛擬機器自己給的名字,一個是你自己的定的名字。在沒有指定執行緒名字的情況下,虛擬機器總會為執行緒指定名字,並且主執行緒的名字總是mian,非主執行緒的名字不確定。

2、執行緒都可以設定名字,也可以獲取執行緒的名字,連主執行緒也不例外。

3、獲取當前執行緒的物件的方法是:Thread.currentThread()

4、在上面的程式碼中,只能保證:每個執行緒都將啟動,每個執行緒都將執行直到完成。一系列執行緒以某種順序啟動並不意味著將按該順序執行。對於任何一組啟動的執行緒來說,排程程式不能保證其執行次序,持續時間也無法保證。

5、當執行緒目標run()方法結束時該執行緒完成。

6、一旦執行緒啟動,它就永遠不能再重新啟動。只有一個新的執行緒可以被啟動,並且只能一次。一個可執行的執行緒或死執行緒可以被重新啟動。

7、執行緒的排程是JVM的一部分,在一個CPU的機器上上,實際上一次只能執行一個執行緒。一次只有一個執行緒棧執行。JVM執行緒排程程式決定實際執行哪個處於可執行狀態的執行緒。

眾多可執行執行緒中的某一個會被選中做為當前執行緒。可執行執行緒被選擇執行的順序是沒有保障的。

8、儘管通常採用佇列形式,但這是沒有保障的。佇列形式是指當一個執行緒完成一輪時,它移到可執行佇列的尾部等待,直到它最終排隊到該佇列的前端為止,它才能被再次選中。事實上,我們把它稱為可執行池而不是一個可執行佇列,目的是幫助認識執行緒並不都是以某種有保障的順序排列唱呢個一個佇列的事實。

9、儘管我們沒有無法控制執行緒排程程式,但可以通過別的方式來影響執行緒排程的方式。

Java執行緒:執行緒棧模型與執行緒的變數

要理解執行緒排程的原理,以及執行緒執行過程,必須理解執行緒棧模型。

執行緒棧是指某時刻時記憶體中執行緒排程的棧資訊,當前呼叫的方法總是位於棧頂。執行緒棧的內容是隨著程式的執行動態變化的,因此研究執行緒棧必須選擇一個執行的時刻(實際上指程式碼執行到什麼地方)

下面通過一個示例性的程式碼說明執行緒(呼叫)棧的變化過程。

這幅圖描述在程式碼執行到兩個不同時刻12時候,虛擬機器執行緒呼叫棧示意圖。

當程式執行到t.start();時候,程式多出一個分支(增加了一個呼叫棧B),這樣,棧A、棧B並行執行。

從這裡就可以看出方法呼叫和執行緒啟動的區別了。

Java執行緒:執行緒狀態的轉換

一、執行緒狀態

執行緒的狀態轉換是執行緒控制的基礎。執行緒狀態總的可分為五大狀態:分別是生、死、可執行、執行、等待/阻塞。用一個圖來描述如下:

1、新狀態:執行緒物件已經建立,還沒有在其上呼叫start()方法。

2、可執行狀態:當執行緒有資格執行,但排程程式還沒有把它選定為執行執行緒時執行緒所處的狀態。當start()方法呼叫時,執行緒首先進入可執行狀態。線上程執行之後或者從阻塞、等待或睡眠狀態回來後,也返回到可執行狀態。

3、執行狀態:執行緒排程程式從可執行池中選擇一個執行緒作為當前執行緒時執行緒所處的狀態。這也是執行緒進入執行狀態的唯一一種方式。

4、等待/阻塞/睡眠狀態:這是執行緒有資格執行時它所處的狀態。實際上這個三狀態組合為一種,其共同點是:執行緒仍舊是活的,但是當前沒有條件執行。換句話說,它是可執行的,但是如果某件事件出現,他可能返回到可執行狀態。

5、死亡態:當執行緒的run()方法完成時就認為它死去。這個執行緒物件也許是活的,但是,它已經不是一個單獨執行的執行緒。執行緒一旦死亡,就不能復生。如果在一個死去的執行緒上呼叫start()方法,會丟擲java.lang.IllegalThreadStateException異常。

有關詳細狀態轉換圖可以參看本人的中的圖

二、阻止執行緒執行

對於執行緒的阻止,考慮一下三個方面,不考慮IO阻塞的情況:

睡眠;

等待;

因為需要一個物件的鎖定而被阻塞。

1、睡眠

Thread.sleep(long millis)Thread.sleep(long millis, int nanos)靜態方法強制當前正在執行的執行緒休眠(暫停執行),以減慢執行緒。當執行緒睡眠時,它入睡在某個地方,在甦醒之前不會返回到可執行狀態。當睡眠時間到期,則返回到可執行狀態。

執行緒睡眠的原因:執行緒執行太快,或者需要強制進入下一輪,因為Java規範不保證合理的輪換。

睡眠的實現:呼叫靜態方法。

        try {
            Thread.sleep(123);
        } catch (InterruptedException e) {
            e.printStackTrace();  
        }

睡眠的位置:為了讓其他執行緒有機會執行,可以將Thread.sleep()的呼叫放執行緒run()之內。這樣才能保證該執行緒執行過程中會睡眠。

例如,在前面的例子中,將一個耗時的操作改為睡眠,以減慢執行緒的執行。可以這麼寫:

    public void run() {
        for(int i = 0;i<5;i++){

// 很耗時的操作,用來減慢執行緒的執行
//            for(long k= 0; k <100000000;k++);
            try {Thread.sleep(3);            } catch (InterruptedException e) {                e.printStackTrace();  .            }
            System.out.println(this.getName()+" :"+i);
        }
    }

執行結果:

阿三 :0
李四 :0 
阿三 :1 
阿三 :2 
阿三 :3 
李四 :1 
李四 :2 
阿三 :4 
李四 :3 
李四 :4 

Process finished with exit code 0

這樣,執行緒在每次執行過程中,總會睡眠3毫秒,睡眠了,其他的執行緒就有機會執行了。

注意:

1、執行緒睡眠是幫助所有執行緒獲得執行機會的最好方法。

2、執行緒睡眠到期自動甦醒,並返回到可執行狀態,不是執行狀態。sleep()中指定的時間是執行緒不會執行的最短時間。因此,sleep()方法不能保證該執行緒睡眠到期後就開始執行。

3sleep()是靜態方法,只能控制當前正在執行的執行緒。

下面給個例子:

/** 
一個計數器,計數到100,在每個數字之間暫停1秒,每隔10個數字輸出一個字串

* @author leizhimin 2008-9-14 9:53:49 
*/
publicclass MyThreadextends Thread {

    
publicvoid run() {
        
for (int i = 0; i < 100; i++) {
            
if ((i) % 10 == 0) {
                System.out.println(
"-------" + i);
            } 
            System.out.print(i); 
            
try {
                Thread.sleep(1); 
                System.out.print(
"    執行緒睡眠1毫秒!\n");
            } 
catch (InterruptedException e) {
                e.printStackTrace(); 
            } 
        } 
    } 

    
publicstaticvoid main(String[] args) {
        
new MyThread().start();
    } 
}

-------0 
0    
執行緒睡眠1毫秒!
1    
執行緒睡眠1毫秒!
2    
執行緒睡眠1毫秒!
3    
執行緒睡眠1毫秒!
4    
執行緒睡眠1毫秒!
5    
執行緒睡眠1毫秒!
6    
執行緒睡眠1毫秒!
7    
執行緒睡眠1毫秒!
8    
執行緒睡眠1毫秒!
9    
執行緒睡眠1毫秒!
-------10 
10    
執行緒睡眠1毫秒!
11    
執行緒睡眠1毫秒!
12    
執行緒睡眠1毫秒!
13    
執行緒睡眠1毫秒!
14    
執行緒睡眠1毫秒!
15    
執行緒睡眠1毫秒!
16    
執行緒睡眠1毫秒!
17    
執行緒睡眠1毫秒!
18    
執行緒睡眠1毫秒!
19    
執行緒睡眠1毫秒!
-------20 
20    
執行緒睡眠1毫秒!
21    
執行緒睡眠1毫秒!
22    
執行緒睡眠1毫秒!
23    
執行緒睡眠1毫秒!
24    
執行緒睡眠1毫秒!
25    
執行緒睡眠1毫秒!
26    
執行緒睡眠1毫秒!
27    
執行緒睡眠1毫秒!
28    
執行緒睡眠1毫秒!
29    
執行緒睡眠1毫秒!
-------30 
30    
執行緒睡眠1毫秒!
31    
執行緒睡眠1毫秒!
32    
執行緒睡眠1毫秒!
33    
執行緒睡眠1毫秒!
34    
執行緒睡眠1毫秒!
35    
執行緒睡眠1毫秒!
36    
執行緒睡眠1毫秒!
37    
執行緒睡眠1毫秒!
38    
執行緒睡眠1毫秒!
39    
執行緒睡眠1毫秒!
-------40 
40    
執行緒睡眠1毫秒!
41    
執行緒睡眠1毫秒!
42    
執行緒睡眠1毫秒!
43    
執行緒睡眠1毫秒!
44    
執行緒睡眠1毫秒!
45    
執行緒睡眠1毫秒!
46    
執行緒睡眠1毫秒!
47    
執行緒睡眠1毫秒!
48    
執行緒睡眠1毫秒!
49    
執行緒睡眠1毫秒!
-------50 
50    
執行緒睡眠1毫秒!
51    
執行緒睡眠1毫秒!
52    
執行緒睡眠1毫秒!
53    
執行緒睡眠1毫秒!
54    
執行緒睡眠1毫秒!
55    
執行緒睡眠1毫秒!
56    
執行緒睡眠1毫秒!
57    
執行緒睡眠1毫秒!
58    
執行緒睡眠1毫秒!
59    
執行緒睡眠1毫秒!
-------60 
60    
執行緒睡眠1毫秒!
61    
執行緒睡眠1毫秒!
62    
執行緒睡眠1毫秒!
63    
執行緒睡眠1毫秒!
64    
執行緒睡眠1毫秒!
65    
執行緒睡眠1毫秒!
66    
執行緒睡眠1毫秒!
67    
執行緒睡眠1毫秒!
68    
執行緒睡眠1毫秒!
69    
執行緒睡眠1毫秒!
-------70 
70    
執行緒睡眠1毫秒!
71    
執行緒睡眠1毫秒!
72    
執行緒睡眠1毫秒!
73    
執行緒睡眠1毫秒!
74    
執行緒睡眠1毫秒!
75    
執行緒睡眠1毫秒!
76    
執行緒睡眠1毫秒!
77    
執行緒睡眠1毫秒!
78    
執行緒睡眠1毫秒!
79    
執行緒睡眠1毫秒!
-------80 
80    
執行緒睡眠1毫秒!
81    
執行緒睡眠1毫秒!
82    
執行緒睡眠1毫秒!
83    
執行緒睡眠1毫秒!
84    
執行緒睡眠1毫秒!
85    
執行緒睡眠1毫秒!
86    
執行緒睡眠1毫秒!
87    
執行緒睡眠1毫秒!
88    
執行緒睡眠1毫秒!
89    
執行緒睡眠1毫秒!
-------90 
90    

相關推薦

java執行 全面詳細講解

Java執行緒:概念與原理 一、作業系統中執行緒和程序的概念 現在的作業系統是多工作業系統。多執行緒是實現多工的一種方式。 程序是指一個記憶體中執行的應用程式,每個程序都有自己獨立的一塊記憶體空間,一個程序中可以啟動多個執行緒。比如在Windows系

java執行詳細入門教程即原始碼解析

##1、執行緒池概念      執行緒池是執行緒的集合,通過執行緒池我們不需要自己建立執行緒,將任務提交給執行緒池即可。為什麼要使用執行緒池,首先,使用執行緒池可以重複利用已有的執行緒繼續執行任務,避免執行緒在建立和銷燬時造成的消耗。其次,由

Java執行詳細解讀

一:執行緒實現方式 詞典 p:程序 LWP:輕量級程序 K: 核心執行緒 基於核心執行緒實現        核心執行緒其實就是有作業系統核心支援的執行緒,這種執行緒有作業系統來負責執行緒的切換,核心通過操作排程器對執行緒進行排程;        程式一般不會直

java執行全面講解

執行緒的使用在開發中可以說是無處不在,場景特別多;當然也是很難控制的。當然你要玩的好,也是很好的。 簡單的講,執行緒本質上不能加快程式的執行(當然多cpu的機器例外了),只不過優化時間排程而已,在我們看來整體上快了點;但搞不好由於線上程間的切換消耗太多精力導致整個程式執行效

java執行池和佇列詳細講解

Java執行緒池使用說明 一簡介 執行緒的使用在java中佔有極其重要的地位,在jdk1.4極其之前的jdk版本中,關於執行緒池的使用是極其簡陋的。在jdk1.5之後這一情況有了很大的改觀。Jdk1.5之後加入了java.util.concurrent包,這個包中主要介紹j

java執行程式設計詳細入門教程

##1、概念      執行緒是jvm排程的最小單元,也叫做輕量級程序,程序是由執行緒組成,執行緒擁有私有的程式技術器以及棧,並且能夠訪問堆中的共享資源。這裡提出一個問題,為什麼要用多執行緒?有一下幾點,首先,隨著cpu核心數的增加,計算機硬

Java執行程式設計詳細解析

Java多執行緒程式設計詳細解析   一、理解多執行緒多執行緒是這樣一種機制,它允許在程式中併發執行多個指令流,每個指令流都稱為一個執行緒,彼此間互相獨立。執行緒又稱為輕量級程序,它和程序一樣擁有獨立的執行控制,由作業系統負責排程,區別在於執行緒沒有獨立的儲存空間,而是和所屬程

Java執行的6種狀態及切換(透徹講解)-本文系轉載

Java執行緒的6種狀態及切換(透徹講解) 轉自:https://mp.weixin.qq.com/s/hUOwck4lMp7c_rvY4TTeQQ Java中執行緒的狀態分為6種。 1.  初始(NEW):新建立了一個執行緒物件,但還沒有呼叫start()方法。

Java執行的6種狀態及切換(透徹講解

來源:https://blog.csdn.net/pange1991/article/details/53860651 Java中執行緒的狀態分為6種。 1.初始(NEW):新建立了一個執行緒物件,但還沒有呼叫start()方法。 2.執行(RUNNABLE):J

Java執行的6種狀態及切換(透徹講解)

Java中執行緒的狀態分為6種。 1. 初始(NEW):新建立了一個執行緒物件,但還沒有呼叫start()方法。 2. 執行(RUNNABLE):Java執行緒中將就緒(ready)和執行中(running)兩種

java執行學習(四):執行等待wait()和通知notify()的詳細使用

執行緒等待wait()和通知notify(),主要用於多執行緒之間的協作,而且這兩個方法都是屬於Object類,說明任何物件都可以呼叫這兩個方法。 當在一個物件例項上呼叫wait()方法後,當前執行緒就會在這個物件上等待。直到另外的執行緒呼叫了notify()方法,出於等待的執行緒才得以

java執行學習(二): 終止執行講解:Stop()方法(後附如何正確終止執行)

本章來學習Java的stop執行緒終止方法; 老規矩,先看原始碼: @Deprecated public final void stop() { SecurityManager var1 = System.getSecurityManager(); if (var1 != n

Linux下Java執行詳細監控和其dump的分析使用----分析Java效能瓶頸[張振華-Jack]

作者:張振華(Jack) 這裡對linux下、sun(oracle) JDK的執行緒資源佔用問題的查詢步驟做一個小結; linux環境下,當發現java程序佔用CPU資源很高,且又要想更進一步

Java執行池使用說明【比較全面

一簡介 執行緒的使用在Java中佔有極其重要的地位,在jdk1.4極其之前的jdk版本中,關於執行緒池的使用是極其簡陋的。在jdk1.5之後這一情況有了很大的改觀。Jdk1.5之後加入了java.util.concurrent包,這個包中主要介紹java中執行緒以及執行

java執行學習(九):阻塞佇列BlockingQueue講解

上一章中學到了執行緒池的詳細使用以及核心執行緒池的部分原始碼,其中就包含有BlockingQueue的資訊,那麼到底BlockingQueue是什麼呢,有什麼用呢,本章就是學這個的。 Blocking翻譯過來為’阻塞’,Queue就是佇列的意思,那麼BlockingQueue就是阻塞隊列了,

Java執行的5種狀態及切換(透徹講解)

Java中的執行緒的生命週期大體可分為5種狀態。 ①NEW:這種情況指的是,通過New關鍵字建立了Thread類(或其子類)的物件 ②RUNNABLE:這種情況指的是Thread類的物件呼叫了start()方法,這時的執行緒就等待時間片輪轉到自己這,以便獲得CPU;第二種情況是執行緒在處於RUNNABLE狀

Java執行總結(八):併發包------讀寫鎖ReadWriteLock的簡單例子詳細理解

初次接觸ReadWriteLock類時也在網上查了很多資料,很容易瞭解到ReadWriteLock是讀寫鎖,並且讀寫鎖的機制有以下三個特點:  讀鎖---讀鎖    (不互斥)  讀鎖---寫鎖     (互斥)  寫鎖---寫鎖     (互斥)什麼意思呢?網上很多資料,

java中synchronized修飾程式碼塊(兩種建立執行的方式講解賣票程式)

格式: synchronized(類物件名 aa) { //同步程式碼塊 } 功能: synchronized(類物件名 aa)的含義是:判斷aa是否已經被其他執行緒所霸佔,如果發現已經被其他執行緒霸

詳細java執行池原始碼解讀

執行緒池的繼承關係是這樣的ThreadPoolExecutor繼承了AbstractExecutorService,AbstractExecutorService是一個抽象類,它實現了ExecutorService介面,ExecutorService又是繼承了Executo

Java執行---超詳細總結

        寫在前面的話:此文只能說是java多執行緒的一個入門,其實Java裡頭執行緒完全可以寫一本書了,但是如果最基本的你都學掌握好,又怎麼能更上一個臺階呢?如果你覺得此文很簡單,那推薦你看看Java併發包的的執行緒池(Java併發程式設計與技術內幕:執行