[Java多線程]-並發,並行,synchonrized同步的用法


一、多線程的並發與並行:

並發:多個線程同時都處在運行中的狀態。線程之間相互干擾,存在競爭,(CPU,緩沖區),每個線程輪流使用CPU,當一個線程占有CPU時,其他線程處於掛起狀態,各線程斷續推進。

並行:多個線程同時執行,但是每個線程各自有自己的CPU,不存在CPU資源的競爭,他們之間也可能存在資源的競爭。

並發發生在同一段時間間隔內,並行發生在同一時刻內。並發執行的總時間是每個任務的時間和,而並行則取決於最長任務的時間。

下面看一下A,B兩個任務在並行和並發情況下是怎么執行的:[不考慮其他資源競爭,只考慮CPU競爭]

  A任務:a+b+c,

  A任務有三步:a=1+1,b=2+2,c=3+3

  B任務:x+y+z,

  A任務有三步:x=1*1,y=2*2,z=3*3

A,B並行操作:多核CPU,多任務並行。

  CPU1:

  CPU2:

  

  圖中可以看到A,B操作相互不受影響。

  當A任務開始的時候B任務也開始,他們可以同一時刻開始,如果每一個小任務耗時相同,那么他們可能同時結束。

A,B並發操作:單核cpu,多任務並發。

   

  圖中可以看出A,B同時執行的時候,一定有一個先,有一個后,因為CPU只有一個執行了A就不能執行B,但是為了讓兩個任務能夠“同時完成“,CPU先執行A的一部分,在執行B的一部分,當這個間隔時間非常短的時候我們看到的就是A,B都在運行。

舉個簡單的例子:

  左右手同時握住兩支筆,並排點一個點,點出一條線來,這就是並行。只有一只手握住一支筆,左點一下,右點一下知道畫出兩條線,這兩條線看似同時開始同時結束,實際是有時間差的,這就是並發。

實際操作中並不是嚴格的並發或者是並行,因為CPU有限,而任務是無限的。任務數量超過CPU核心數量的時候,就是並發並行共同存在的時候,不過這不是我們關注的重點,CPU資源的分配問題,是操作系統關心的重點。我們關心的重點是任務之間發生的資源競爭問題。

當多個線程對同一個資源進行訪問和操作的時候就會出現數據一致性問題。一致性問題得不到解決多個任務的操作永遠得不到正確的結果,解決一致性問題的方法就是同步機制。

二、synchonrized實現同步:

java每個對象都有一個內置鎖,當用synchonrized修飾方法或者代碼塊的時候就會調用這個鎖來保護方法和代碼塊。

同步方法:同步靜態方法,同步非靜態方法。

public synchronized void addMeethod2(){ num2++; }
public static synchronized void addMeethod2(){ num2++; }

同步代碼塊:

  synchronized(Object){...}:Object表示一個對象,synchronized獲取這個對象的同步鎖,保證線程獲取object的同步鎖之后其他線程不能訪問object的任何同步方法。但其他線程可以訪問非同步的部分。

public  void addMeethod3(){ synchronized(this){ num3++; } }
public  void addMeethod4(){ synchronized(num4){ num4++; } }
public  void addMeethod5(){ synchronized(Learnlocks.class){ num5++; } }

同步當前對象this:public void addMeethod3(){synchronized(this){num3++;}}和同步非靜態方法:public synchronized void addMeethod2(){ num2++;}他們的效果是一樣的都是作用與當前對象,即獲取的object都是當前對象。那么只有這個線程可以操作這個對象所有synchronized修飾的部分,執行完這部分內容才會釋放鎖,其他線程才能訪問。

下面看那一下示例:模擬三個線程分別對不同的同步機制保護的數據進行操作,看他們的具體表現。

import java.util.concurrent.atomic.AtomicInteger; public class NumAction { private  Integer num1=0; private  Integer num2=0; private  Integer num3=0; private  Integer num4=0; private  Integer num5=0; private  volatile Integer num6=0; private  AtomicInteger  num7=new AtomicInteger(0); public NumAction() { }   //省略get/set方法public void Initializers(NumAction lk){ lk.num1=0; lk.num2=0; lk.num3=0; lk.num4=0; lk.num5=0; lk.num6=0; lk.num7=new AtomicInteger(0); }
//----------------------------------------------------------重點部分------------------------------------------------------
public void addMeethod1(){ num1++; } public synchronized void addMeethod2(){ num2++; } public void addMeethod3(){ synchronized(this){ num3++; } } public void addMeethod4(){ synchronized(num4){ num4++; } } public void addMeethod5(){ synchronized(NumAction.class){ num5++; } } public void addMeethod6(){ num6++; } public void addMeethod7(){ num7.incrementAndGet(); } public void Add100() { for (int i = 0; i < 100; i++) { addMeethod1(); addMeethod2(); addMeethod3(); addMeethod4(); addMeethod5(); addMeethod6(); addMeethod7(); } } }

NumAction類:有七個屬性,八個方法,前七個方法分別給七個屬性自增一次。第八個方法是調用這七個方法100次。下面用多個線程來執行這個方法。

package com.eshore.ljcx.locks; public class Learnlocks2 extends Thread{ private static NumAction numaction=new NumAction(); public void run() { numaction.Add100(); } public static void testRun(){ Learnlocks2 l1 = new Learnlocks2(); Learnlocks2 l2 = new Learnlocks2(); Learnlocks2 l3 = new Learnlocks2(); new Thread(l1).start(); new Thread(l2).start(); new Thread(l3).start(); try { Thread.sleep(7000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(numaction.getNum1()+","+numaction.getNum2()+","+numaction.getNum3()+","+numaction.getNum4()+","+numaction.getNum5()+","+numaction.getNum6()+","+numaction.getNum7()); } public static void main(String[] args) { for (int i = 0; i < 10; i++) { testRun(); numaction.Initializers(numaction); } } }

我們啟動了三個線程去執行給每個數據自增一百次的操作,理想狀態下最終每個數據應該是300,再將這個步驟重復10次,看一下是不是一樣的結果。

結果如下:num2,num3,num5,num7是300,其他的數據都不正確。說明這四個數據的同步起到了作用,他們分別是:(同步方法,同步當前對象,同步類對象,Atom原子對象。)而volatitle關鍵字被稱為輕量級的同步機制並沒有起到應有的效果。同步屬性對象num4也並沒有作用。

299,300,300,300,300,299,300
297,300,300,300,300,300,300
292,300,300,297,300,297,300
275,300,300,296,300,294,300
298,300,300,300,300,300,300
283,300,300,300,300,297,300
297,300,300,300,300,300,300
297,300,300,300,300,297,300
299,300,300,300,300,300,300
296,300,300,299,300,298,300

同步num4為什么沒有起到作用:因為實際上我們上鎖的是對象本身,並不是對象的引用。每次給對象自增,對象已經修改了,那么我們每次獲取的鎖也不是同一個鎖。自然沒有辦法保持同步。如果我們添加一個不會被修改的屬性對象num0。

private Integer num0 =0;

修改方法四:

public  void addMeethod4(){ synchronized(this.num0){ num4++; } }

結果如下:發現num4的輸出結果也是預期的300.

296,300,300,300,300,300,300
297,300,300,300,300,300,300
248,300,300,300,300,281,300
255,300,300,300,300,289,300
297,300,300,300,300,300,300
289,300,300,300,300,298,300
298,300,300,300,300,300,300
290,300,300,300,300,300,300
263,300,300,300,300,299,300
296,300,300,300,300,300,300

從num4可以看出來我們獲取num0的對象鎖,但是num4卻可以保持同步。這是因為num0這個對象鎖的代碼塊是num0對象鎖的作用域,要對num4操作,必須獲取num0對象鎖。很多時候誤區可能在於我們要對那個數據保持同步就要獲取那個數據的鎖,這是很錯誤的理解。首先synchonrized獲取的是對象鎖,數據不是對象就滿足不了條件(個人見解,僅供參考)。

總結下:(synchonrized的用法在示例代碼部分已經做了展示。)

//-------------------------------------------------------update:2017/3/23-------------------------------------------------

對於同步方法:

  1、所有靜態方法M0,M1。。:鎖定的對象是類

      多線程用類使用M0,M1會阻塞:M0,M1屬於類,類加鎖,鎖相同

  2、所有非靜態方法M2,M3:鎖定的對象是類的實例

      多線程用不同的對象使用M2不會阻塞:對象不同,鎖不同

      多線程用相同的對象使用M2會阻塞:對象相同,鎖相同

      多線程用相同的對象使用M2,M3會阻塞:對象相同,鎖相同

      多線程用不同的對象使用M2,M3不會阻塞:對象不同,鎖不同

    *&*:

      多線程訪問M1(靜態方法)和M2(非靜態方法)不會阻塞:M1的鎖是類,M2的鎖是對象,鎖不同

對於同步代碼塊:

  1、非靜態方法代碼塊:鎖定的是指定的對象Object

      同步代碼塊當Object==this的時候,和同步方法效果相同,但是如果只作用了整個方法的一部分代碼塊,那么效率會更高。【作用內容大小帶來的差異】

      同步代碼塊當Object==X(X==指定一個不變的對象 final Integer X=0)的時候,和同步方法效果不同,不同點在於此時作用域是所有以X為鎖的代碼塊,而同步方法會作用於對象下所有的同步方法。這也是同步代碼塊相較於同步方法效率會更高的原因。【作用方法的數量帶來的差異】

  2、靜態方法代碼塊:鎖定的是指定的對象Object

      同步代碼塊當Object==this的時候,不多說,還是類(靜態方法類調用,怎么也扯不到實例對象上去)

      同步代碼塊當Object==X(X==指定一個不變的對象 static final Integer X=0,X必須是靜態的,因為靜態方法只能使用靜態屬性)的時候,鎖的作用域是所有使用X作為鎖的代碼塊。而同步靜態方法作用域是所有的同步方法。

//-------------------------------------------------------------------------------------------------------------------------

並發機制提高任務效率,而同步機制是保證並發的安全,但是同時也會破壞並發效率,所以同步的鎖的粒度把握是個關鍵問題,在保證同步的情況下盡量保證性能才是關鍵。

對於非靜態字段中可更改的數據,通常使用非靜態方法訪問.

對於靜態字段中可更改的數據,通常使用靜態方法訪問,也可用非靜態訪問。不過是操作類。

參考:http://www.cnblogs.com/csniper/p/5478572.html。。。。。。。。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM