1. 程式人生 > 實用技巧 >Java執行緒原理和5種同步方法

Java執行緒原理和5種同步方法

Java執行緒原理和5種同步方法

自己開發了一個股票智慧分析軟體,功能很強大,需要的點選下面的連結獲取:

https://www.cnblogs.com/bclshuai/p/11380657.html

目錄

1 Java執行緒原理和兩種實現方式... 1

1.1 java執行緒原理和原始碼解析... 1

1.2 實現 Runnable 介面實現run方法... 2

1.3 繼承Thread類重寫run方法... 4

2 執行緒的狀態變化... 5

3 執行緒函式... 6

4 Java執行緒同步方法... 7

4.1 Synchronized修飾方法或程式碼塊... 7

4.2 Volatile修飾的變數... 7

4.3 使用重入鎖實現執行緒同步... 7

1 Java執行緒原理和兩種實現方式

1.1 java執行緒原理和原始碼解析

執行緒是為了實現併發執行,java執行緒實現有兩種方式。一種是繼承 Thread 類,另一種就是實現 Runnable 介面,實現Runnable介面的run函式。Thread類實際上也是實現了runnable介面,並且在Thread類中實現了Runnable介面的run函式,只是這個run函式是一個Override函式,繼承Thread的類要麼重寫這個run函式,要入以入參的形式傳入Runnable 介面實現類物件,也就是下面的target物件。總而言之,就是要實現run方法,要麼重寫,要麼入參傳入Runnable實現類物件。

Public class Thread implements Runnable {
……
@Override
public void run() {
if (target != null) {
target.run();
}
}
……
Private Runnable target
//建構函式1,需要重寫run函式
public Thread(String name) {
init(null, null, name, 0);
}
//建構函式2,需要以入參傳入的Runnable介面物件
public Thread(Runnable target
,String name){
 init(null,target,name,0);
}
//初始化函式
private void init(ThreadGroup g,Runnable target,String name,long stackSize){
 ...
 this.target=target;
}

……

}

1.2 實現 Runnable 介面實現run方法

Runnable介面定義

interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}

class MyThread implements Runnable{ // 實現Runnable介面,作為執行緒的實現類
private String name ; // 表示執行緒的名稱
public MyThread(String name){
this.name = name ; // 通過構造方法配置name屬性
}
public void run(){ // 覆寫run()方法,作為執行緒的操作主體
for(int i=0;i<10;i++){
System.out.println(name + "執行,i = " + i) ;
}
}

public static void main(String args[]){
MyThread mt1 = new MyThread("執行緒A ") ; // 例項化物件
MyThread mt2 = new MyThread("執行緒B ") ; // 例項化物件
Thread t1 = new Thread(mt1) ; // 例項化Thread類物件
Thread t2 = new Thread(mt2) ; // 例項化Thread類物件
t1.start() ; // 啟動多執行緒
t2.start() ; // 啟動多執行緒
}
};

執行結果為:

執行緒B 執行,i = 0

執行緒B 執行,i = 1

執行緒B 執行,i = 2

執行緒B 執行,i = 3

執行緒B 執行,i = 4

執行緒B 執行,i = 5

執行緒B 執行,i = 6

執行緒B 執行,i = 7

執行緒B 執行,i = 8

執行緒B 執行,i = 9

執行緒A 執行,i = 0

執行緒A 執行,i = 1

執行緒A 執行,i = 2

執行緒A 執行,i = 3

執行緒A 執行,i = 4

執行緒A 執行,i = 5

執行緒A 執行,i = 6

執行緒A 執行,i = 7

執行緒A 執行,i = 8

執行緒A 執行,i = 9

1.3 繼承Thread類重寫run方法

class MyThread extends Thread{ // 繼承Thread類,作為執行緒的實現類

private String name ; // 表示執行緒的名稱

public MyThread(String name){

this.name = name ; // 通過構造方法配置name屬性

}

public void run(){ // 覆寫run()方法,作為執行緒 的操作主體

for(int i=0;i<10;i++){

System.out.println(name + "執行,i = " + i) ;

}

}

};

public class ThreadDemo02{

public static void main(String args[]){

MyThread mt1 = new MyThread("執行緒A ") ; // 例項化物件

MyThread mt2 = new MyThread("執行緒B ") ; // 例項化物件

mt1.start() ; // 呼叫執行緒主體

mt2.start() ; // 呼叫執行緒主體

}

};

執行緒A 執行,i = 0

執行緒B 執行,i = 0

執行緒B 執行,i = 1

執行緒B 執行,i = 2

執行緒B 執行,i = 3

執行緒B 執行,i = 4

執行緒B 執行,i = 5

執行緒B 執行,i = 6

執行緒B 執行,i = 7

執行緒B 執行,i = 8

執行緒B 執行,i = 9

執行緒A 執行,i = 1

執行緒A 執行,i = 2

執行緒A 執行,i = 3

執行緒A 執行,i = 4

執行緒A 執行,i = 5

執行緒A 執行,i = 6

執行緒A 執行,i = 7

執行緒A 執行,i = 8

執行緒A 執行,i = 9

2 執行緒的狀態變化

要想實現多執行緒,必須在主執行緒中建立新的執行緒物件。任何執行緒一般具有5種狀態,即建立,就緒,執行,阻塞,終止。下面分別介紹一下這幾種狀態:

  • 建立狀態

在程式中用構造方法建立了一個執行緒物件後,新的執行緒物件便處於新建狀態,此時它已經有了相應的記憶體空間和其他資源,但還處於不可執行狀態。新建一個執行緒物件可採用Thread 類的構造方法來實現,例如 “Thread thread=new Thread()”。

  • 就緒狀態

新建執行緒物件後,呼叫該執行緒的 start() 方法就可以啟動執行緒。當執行緒啟動時,執行緒進入就緒狀態。此時,執行緒將進入執行緒佇列排隊,等待 CPU 服務,這表明它已經具備了執行條件。

  • 執行狀態

當就緒狀態被呼叫並獲得處理器資源時,執行緒就進入了執行狀態。此時,自動呼叫該執行緒物件的 run() 方法。run() 方法定義該執行緒的操作和功能。

  • 阻塞狀態

一個正在執行的執行緒在某些特殊情況下,如被人為掛起或需要執行耗時的輸入/輸出操作,會讓 CPU 暫時中止自己的執行,進入阻塞狀態。在可執行狀態下,如果呼叫sleep(),suspend(),wait() 等方法,執行緒都將進入阻塞狀態,發生阻塞時執行緒不能進入排隊佇列,只有當引起阻塞的原因被消除後,執行緒才可以轉入就緒狀態。

  • 死亡狀態

執行緒呼叫 stop() 方法時或 run() 方法執行結束後,即處於死亡狀態。處於死亡狀態的執行緒不具有繼續執行的能力。

3 執行緒函式

(1)獨佔CPU啟動執行緒

join() 方法讓一個執行緒強制執行,執行緒強制執行期間,其他執行緒無法執行,必須等待此執行緒完成之後才可以繼續執行。

(2)執行緒休眠

Thread.sleep(500) 即可實現休眠500ms

(3)執行緒優先順序

執行緒將根據其優先順序的大小來決定哪個執行緒會先執行,但是需要注意並非優先順序越高就一定會先執行,哪個執行緒先執行將由 CPU 的排程決定。

t1.setPriority(Thread.MIN_PRIORITY) ; // 優先順序最低

t2.setPriority(Thread.MAX_PRIORITY) ; // 優先順序最高

t3.setPriority(Thread.NORM_PRIORITY) ; // 優先順序最中等

(4)中斷執行緒

當一個執行緒執行時,另外一個執行緒可以直接通過interrupt()方法中斷其執行狀態。

(5)執行緒禮讓

也可以使用 yield() 方法將一個執行緒的佔用資源暫時讓給其他執行緒執行。

4 Java執行緒同步方法

4.1 Synchronized修飾方法或程式碼塊

java的每個物件都有一個內建鎖,當用此關鍵字修飾方法時,內建鎖會保護整個方法。在呼叫該方法前,需要獲得內建鎖,否則就處於阻塞狀態。

public synchronized void save(){} 或者

synchronized (this)

{ account += money;}

4.2 Volatile修飾的變數

如果對聲明瞭volatile的變數進行寫操作,JVM就會向處理器傳送一條Lock字首的指令,將這個變數所在快取行的資料寫回到系統記憶體。多處理器下,其他處理器發現自己快取行對應的記憶體地址被修改,就會重新讀取資料。在需要同步操作的變數前加上volatile

private volatile int account = 100;

4.3 使用重入鎖實現執行緒同步

JavaSE5.0中新增了一個java.util.concurrent包來支援同步。 ReentrantLock類是可重入、互斥、實現了Lock介面的鎖。

ReentrantLock() : 建立一個ReentrantLock例項
lock() : 獲得鎖

unlock() : 釋放鎖

只給出要修改的程式碼,其餘程式碼與上同

class Bank {

private int account = 100;

//需要宣告這個鎖

private Lock lock = new ReentrantLock();

public int getAccount() {

return account;

}

//這裡不再需要synchronized

public void save(int money) {

lock.lock();

try{

account += money;

}finally{

lock.unlock();

}

}

4.4 原子變數實現執行緒同步

原子操作就是指將讀取變數值、修改變數值、儲存變數值看成一個整體來操作

即-這幾種行為要麼同時完成,要麼都不完成。

在java的util.concurrent.atomic包中提供了建立了原子型別變數的工具類。

其中AtomicInteger 表可以用原子方式更新int的值AtomicInteger類常用方法:

AtomicInteger(int initialValue) : 建立具有給定初始值的新的AtomicInteger,addAddGet(int dalta) : 以原子方式將給定值與當前值相加。get() : 獲取當前值。

程式碼例項

class Bank {

private AtomicInteger account = new AtomicInteger(100);

public AtomicInteger getAccount() {

return account;

}

public void save(int money) {

account.addAndGet(money);

}

}

4.5 阻塞佇列實現執行緒同步

(1)LinkedBlockingQueue內部實現是單鏈表結構。LinkedBlockingQueue插入和讀取是有兩把ReentrantLock鎖的,LinkedBlockingQueue 是插入和拿取資料都是阻塞執行的。內部採用原子變數AtomicInteger統計個數。

(2)ArrayBlockingQueue,內部實現是陣列。插入和讀取用的是同一把鎖,內部採用int變數統計數量。

LinkedBlockingQueue類常用方法

LinkedBlockingQueue() : 建立一個容量為Integer.MAX_VALUE的LinkedBlockingQueue

put(E e) : 在隊尾新增一個元素,如果佇列滿則阻塞

size() : 返回佇列中的元素個數

take() : 移除並返回隊頭元素,如果佇列空則阻塞

BlockingQueue<E>定義了阻塞佇列的常用方法,尤其是三種新增元素的方法,我們要多加註意,當佇列滿時:

add()方法會丟擲異常

offer()方法返回false

put()方法會阻塞