Java多執行緒之二(Synchronized)
常用API
method | 註釋 |
---|---|
run() |
run() 方法是我們建立執行緒時必須要實現的方法,但是實際上該方法只是一個普通方法,直接呼叫並沒有開啟執行緒的作用。 |
start() |
start() 方法作用為使該執行緒開始執行;Java虛擬機器呼叫該執行緒的 run 方法。 但是該方法只能呼叫一次,如果執行緒已經啟動將會丟擲IllegalThreadStateException 異常。 |
yield() |
yield() 方法讓出CPU並且不會釋放鎖,讓當前執行緒變為可執行狀態,所以CPU下一次選擇的執行緒仍可能是當前執行緒。 |
wait() |
wait() 方法使得當前執行緒掛起,放棄CPU的同時也放棄同步資源(釋放鎖),讓其他等待這些資源的執行緒能繼續執行,只有當使用notify()/notifyAll() 方法是才會使得等待的執行緒被喚醒,使用此方法的前提是已經獲得鎖。 |
notify()/notifyAll() |
notify()/notifyAll() 方法將喚醒當前鎖上的一個(全部)執行緒,需要注意的事一般都是使用的notifyAll() 方法,因為notify() 方法的喚醒是隨機的,我們沒有辦法控制。 |
同步
上面已經介紹了比較常用的api,現在我們可以瞭解一下在多執行緒中佔據著重要地位的鎖了。
為什麼會出現執行緒不安全
在上一篇文章中有提到在現在作業系統中程式是作為資源分配的基本單位,而執行緒是作為排程的基本單位,一般而言,執行緒自己不擁有系統資源,但它可以訪問其隸屬程式的資源,即一個程式的程式碼段、資料段及所擁有的系統資源,如已開啟的檔案、I/O裝置等,可以供該程式中的所有執行緒所共享,一旦有多個執行緒在操作同樣的資源就可能造成執行緒安全的問題。
在我們熟悉的Java中存在著區域性變數和類變數,其中區域性變數是存放在棧幀中的,隨著方法呼叫而產生,方法結束就被釋放掉,而棧幀是獨屬於當前執行緒的,所以不會有執行緒安全的問題。而類變數是被存放在堆記憶體中,可以被所有執行緒共享,所以也會存線上程安全的問題。
synchronized
在Java中我們見得最多的同步的方法應該就是使用synchronized
關鍵字了。實際上synchronized
就是一個互斥鎖,當一個執行緒執行到使用了synchronized
的程式碼段時,首先檢查當前資源是否已經被其他執行緒所佔用,如果已經被佔用,那麼該執行緒則阻塞在這裡,直到擁有資源的執行緒釋放鎖,其他執行緒才可以繼續申請資源。
實現簡單理解
public static void test(){ synchronized (SyncDemo.class){ } }//編譯後的程式碼 public static void test(); Code: 0: ldc #3 //將一個常量載入到棧中這裡既是class com/learn/set/mutilthread/sync/SyncDemo 2: dup //複製棧頂元素(SyncDemo.class) 3: astore_0 //將棧頂元素儲存到區域性變數表 4: monitorenter //以位元組碼物件(SyncDemo.class)為鎖開始同步操作 5: aload_0 //將區域性變數表slot_0入棧(SyncDemo.class) 6: monitorexit //退出同步 7: goto 15 //到這裡程式跳轉到return語句正常結束,下面程式碼是異常路徑 10: astore_1 11: aload_0 12: monitorexit 13: aload_1 14: athrow 15: return
到這裡就差不多了,詳細的原理後面再談,這裡主要是談談synchronized
的使用。
synchronized的使用
在Java語言中,synchronized
關鍵字可以用來修飾方法以及程式碼塊:
修飾方法
//修飾普通方法 public synchronized void say(){ } //修飾靜態方法 public synchronized static void fun(){ }
修飾程式碼塊
public void fun1(){ //使用當前物件為鎖 synchronized (this){ //statement } } public void fun2(){ //使用當前類位元組碼物件為鎖 synchronized (SyncDemo.class){ //statement } }
synchronized在不同場景下的區別
實體類:
public class User { private static int age = 20; public synchronized void say(String user) throws InterruptedException {// synchronized (User.class){ System.out.println(age + ":" + Thread.currentThread().getName() + ":" + user); //當前執行緒休眠,判斷別的執行緒是否還能呼叫 Thread.sleep(1000); System.out.println(age + ":" + Thread.currentThread().getName() + ":" + user);// } } public synchronized void say1(String user) throws InterruptedException {// synchronized (User.class){ System.out.println(age + ":" + Thread.currentThread().getName() + ":" + user); Thread.sleep(1000); age = 15; System.out.println(age + ":" + Thread.currentThread().getName() + ":" + user);// } } }
測試類:
public class SyncTest{ private static User user1 = new User(); private static User user2 = new User(); private static class Sync1 extends Thread{ @Override public void run() { try { user1.say("user1"); } catch (InterruptedException e) { e.printStackTrace(); } } } private static class Sync2 extends Thread{ @Override public void run() { try { user2.say("user2"); } catch (InterruptedException e) { e.printStackTrace(); } } } public static void main(String[] args) { Sync1 sync1 = new Sync1(); Sync2 sync2 = new Sync2(); sync1.start(); sync2.start(); } } 執行結果: 第一次執行:20:Thread-1:user220:Thread-0:user120:Thread-0:user120:Thread-1:user2 第二次執行:20:Thread-1:user220:Thread-0:user120:Thread-1:user220:Thread-0:user1
執行結果表示在普通方法上加synchronized關鍵字實際上是鎖的當前物件,所以不同執行緒操作不同物件結果可能出現不一致。修改實體類User的say(...)方法為靜態方法:
public synchronized void say(String user) throws InterruptedException { System.out.println(age + ":" + Thread.currentThread().getName() + ":" + user); Thread.sleep(1000); System.out.println(age + ":" + Thread.currentThread().getName() + ":" + user); }
執行結果始終按照順序來:
20:Thread-0:user120:Thread-0:user120:Thread-1:user220:Thread-1:user2
說明在靜態(類)方法上加synchronized關鍵字實際上是鎖的當前類的位元組碼物件,因為在JVM中任何類的位元組碼物件都只有一個,所以只要對該位元組碼物件加鎖那麼任何對該類的操作也都是同步的。
在最初類的基礎上修改類Sync2,使得兩個執行緒操作統一物件:
private static class Sync2 extends Thread{ @Override public void run() { try { user1.say("user2"); } catch (InterruptedException e) { e.printStackTrace(); } } }
執行結果始終按照順序來:
20:Thread-0:user120:Thread-0:user120:Thread-1:user220:Thread-1:user2
同理可測試在使用synchronized修飾程式碼塊的作用,可得結果使用this
物件實際是鎖當前物件,與synchronized
修飾普通方法類似,使用User.class
位元組碼物件實際是鎖User類的位元組碼物件,與synchronized
修飾靜態方法類似。需要說明的事鎖程式碼塊實際上並不是必須使用當前類的this物件和位元組碼物件,而可以是任意的物件。而實際效果和使用當前類的物件一致。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/3137/viewspace-2817936/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Java多執行緒(三):SynchronizedJava執行緒synchronized
- java多執行緒之(synchronized)Java執行緒synchronized
- java多執行緒3:synchronizedJava執行緒synchronized
- Java多執行緒之執行緒同步【synchronized、Lock、volatitle】Java執行緒synchronized
- Java多執行緒之synchronized理論Java執行緒synchronized
- Java多執行緒——synchronized的使用示例Java執行緒synchronized
- Java多執行緒之synchronized詳解Java執行緒synchronized
- JAVA-多執行緒(關鍵字:synchronized)Java執行緒synchronized
- java多執行緒中的synchronized的byte[0]Java執行緒synchronized
- java synchronized 保護執行緒安全Javasynchronized執行緒
- 多執行緒(二)、內建鎖 synchronized執行緒synchronized
- Java多執行緒之—Synchronized方式和CAS方式實現執行緒安全效能對比Java執行緒synchronized
- Java多執行緒-執行緒中止Java執行緒
- 多工處理方式之二:多執行緒執行緒
- Java多執行緒程式設計筆記2:synchronized同步方法Java執行緒程式設計筆記synchronized
- 【Java多執行緒】輕鬆搞定Java多執行緒(二)Java執行緒
- 多執行緒基礎之synchronized和volatile執行緒synchronized
- java——多執行緒Java執行緒
- java多執行緒Java執行緒
- Java - 多執行緒Java執行緒
- java 多執行緒Java執行緒
- Java多執行緒之執行緒中止Java執行緒
- Java多執行緒-執行緒狀態Java執行緒
- Java多執行緒-執行緒通訊Java執行緒
- java 多執行緒守護執行緒Java執行緒
- Java多執行緒(2)執行緒鎖Java執行緒
- java多執行緒9:執行緒池Java執行緒
- 【java多執行緒】(二)執行緒停止Java執行緒
- Java多執行緒學習(一)Java多執行緒入門Java執行緒
- Java多執行緒(一)多執行緒入門篇Java執行緒
- @Java | Thread & synchronized – [ 執行緒同步鎖 基本使用]Javathreadsynchronized執行緒
- 【Java多執行緒】執行緒安全的集合Java執行緒
- 【Java】【多執行緒】執行緒池簡述Java執行緒
- Java多執行緒-執行緒池的使用Java執行緒
- java 多執行緒CountDownLatchJava執行緒CountDownLatch
- java 多執行緒-3Java執行緒
- java 多執行緒-2Java執行緒
- java 多執行緒 –同步Java執行緒