併發模型—共享內存模型(線程與鎖)示例篇

共享內存模型,顧名思義就是經過共享內存來實現併發的模型,當多個線程在併發執行中使用共享資源時如不對所共享的資源進行約定或特殊處理時就會出現讀到髒數據、無效數據等問題;而爲了決解共享資源所引發的這些問題,Java中引入了同步、鎖、原子類型等這些用於處理共享資源的操做;
  在本篇文章中,將經過幾個Demo來介紹Java的synchronized、lock、atomic相關類等,Java的共享內存併發模型也就體如今同步(synchronized)、鎖(lock)等這些實現上; java

同步:
  Demo中開啓兩個線程調用一個計數器,這時計數器成了兩個線程的共享資源,兩個線程都在競爭共享資源發生了竟態條件,當不對竟態條件進行處理時獲得的數據就多是有異常的不正確的;編程

 1 /**
 2  * Created by linx on 2015-05-12.
 3  */
 4 public class Counter { 
 6     private int count = 0; 
 8     public  void increment() {
 9         ++count;
10     }
12     public int getCount() {
13         return count;
14     }
15 }
16 /**
17  * Created by linx on 2015-05-12.
18  */
19 public class CountThread extends Thread {
20 
21     private Counter counter;
22     public CountThread(Counter counter) {
23         this.counter = counter;
24     }
25     @Override
26     public void run() {
27         for (int i = 0; i < 5000; i++) {
28             counter.increment();
29         }
30     }
31 }
32 /**
33  * Created by linx on 2015-05-12.
34  */
35 public class CountMain {
36 
37     public static void main(String[] args) throws InterruptedException {
38 
39         Counter counter = new Counter();
40         AtomicCounter atomicCounter=new AtomicCounter();
41         CountThread t1 = new CountThread(counter);
42         CountThread t2 = new CountThread(counter);
43         t1.start();
44         t2.start(); 
45         t1.join();
46         t2.join();
47         System.out.println(counter.getCount());
48     } 
49 }

我在執行這代碼的時候幾乎每次獲得的結果都是不同的,結果以下:

由於這裏有竟態條件因此結果是不可預測的;
解決竟態條件的方法是對鎖競爭的資源進行加鎖同步,在java中能夠用synchronized或lock等鎖;
如今咱們再修改計數器的代碼:多線程

public synchronized void increment() {
  ++count;
}

這裏咱們只是在increment方法聲明處加了synchronized關鍵字,這時候咱們在執行程序,如今每次咱們獲得結果都會是10000,
由於咱們解決了竟態條件,同一時間就會有一個線程會進入到increment方法執行,因此這時候獲得的就是正確的結果;併發


在這裏咱們只是把上面Demo中的synchronized換成Lock對象,獲得的結果仍是相同的;分佈式

/**
 * Created by linx on 2015-05-12.
 */
public class Counter {
    private int count = 0;
    Lock lock=new ReentrantLock();
    public  void increment() {
        lock.lock();
        try {
            ++count;
        }finally {
            lock.unlock();
        }
    }
    public int getCount() {
        return count;
    }
}  

這裏咱們顯示的使用了顯試的ReentrantLock鎖對象給increment方法中的代碼塊進行了加鎖,其餘synchronized也是對方法進行了加鎖,不過它使用的是對象的內置鎖;
原子類型
    咱們上面的Demo只因此沒有同步或加鎖時會出現問題是由於++count不是原子的,它實際上是read-modify-write三個操做,只要能保證increment爲原子方法那麼這裏也就不是出現問題了,如今咱們吧count改成原子類型;ide

/**
 * Created by linx on 2015-05-12.
 */
public class AtomicCounter {
    private AtomicInteger count=new AtomicInteger();
    public  void increment() {
        count.incrementAndGet();
    } 
    public AtomicInteger getCount() {
        return count;
    }
}

這個計數器類咱們不進行任何同步或加鎖都不會出現問題,由於increment方法是原子的。性能

模型優缺點
  優勢:內存共享模型或稱線程與鎖模型使用面很廣,並且如今幾乎每一個操做系統中也存在這種模型,因此也算很是見的一種模型。 測試

  缺點:線程與鎖模型存在的一些問題有,沒有直接支持併發、沒法或難於實現分佈式共享內存的系統,線程與鎖模型有很是很差的地方就是難於測試,在多線程編程中不少時候不經意間就出現問題了這時都還不知道,並且當忽然出現了Bug這時每每咱們也難於重現這個Bug,共享內存模型又是不可創建數學模型的,其中有很大的不肯定性,而不肯定性就說明可能掩藏着問題,人的思惟也只是單線程的;this

還有因爲建立線程也是很是消耗資源的,而多線程間的竟態條件、鎖等競爭若是處理很差也是會很是影響性能的;atom

 

 文章首發地址:Solinxhttp://www.solinx.co/archives/190

相關文章
相關標籤/搜索