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()方法會阻塞