程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> 關於JAVA >> Java並發編程總結——慎用CAS詳解

Java並發編程總結——慎用CAS詳解

編輯:關於JAVA

Java並發編程總結——慎用CAS詳解。本站提示廣大學習愛好者:(Java並發編程總結——慎用CAS詳解)文章只能為提供參考,不一定能成為您想要的結果。以下是Java並發編程總結——慎用CAS詳解正文


1、CAS和synchronized實用場景

1、關於資本競爭較少的情形,應用synchronized同步鎖停止線程壅塞和叫醒切換和用戶態內核態間的切換操作額定糟蹋消費cpu資本;而CAS基於硬件完成,不須要進入內核,不須要切換線程,操作自旋概率較少,是以可以取得更高的機能。

2、關於資本競爭嚴重的情形,CAS自旋的幾率會比擬年夜,從而糟蹋更多的CPU資本,效力低於synchronized。以java.util.concurrent.atomic包中AtomicInteger類為例,其getAndIncrement()辦法完成以下:

public final int getAndIncrement() {
    for (;;) {
      int current = get();
      int next = current + 1;
      if (compareAndSet(current, next))
        return current;
    }
}

假如compareAndSet(current, next)辦法勝利履行,則直接前往;假如線程競爭劇烈,招致compareAndSet(current, next)辦法一向不克不及勝利履行,則會一向輪回期待,直到耗盡cpu分派給該線程的時光片,從而年夜幅下降效力。

2、CAS毛病的應用場景

public class CASDemo {
  private final int THREAD_NUM = 1000;
  private final int MAX_VALUE = 20000000;
  private AtomicInteger casI = new AtomicInteger(0);
  private int syncI = 0;
  private String path = "/Users/pingping/DataCenter/Books/Linux/Linux經常使用敕令詳解.txt";

  public void casAdd() throws InterruptedException {
    long begin = System.currentTimeMillis();
    Thread[] threads = new Thread[THREAD_NUM];
    for (int i = 0; i < THREAD_NUM; i++) {
      threads[i] = new Thread(new Runnable() {
        public void run() {
          while (casI.get() < MAX_VALUE) {
            casI.getAndIncrement();
          }
        }
      });
      threads[i].start();
    }
    for (int j = 0; j < THREAD_NUM; j++) {
      threads[j].join();
    }
    System.out.println("CAS costs time: " + (System.currentTimeMillis() - begin));
  }

  public void syncAdd() throws InterruptedException {
    long begin = System.currentTimeMillis();
    Thread[] threads = new Thread[THREAD_NUM];
    for (int i = 0; i < THREAD_NUM; i++) {
      threads[i] = new Thread(new Runnable() {
        public void run() {
          while (syncI < MAX_VALUE) {
            synchronized ("syncI") {
              ++syncI;
            }
          }
        }
      });
      threads[i].start();
    }
    for (int j = 0; j < THREAD_NUM; j++)
      threads[j].join();
    System.out.println("sync costs time: " + (System.currentTimeMillis() - begin));
  }
}

在我的雙核cpu上運轉,成果以下:

可見在分歧的線程下,采取CAS盤算消費的時光遠多於應用synchronized方法。緣由在於第15行

14           while (casI.get() < MAX_VALUE) {
15             casI.getAndIncrement();
16           }

的操作是一個耗時異常少的操作,15行履行完以後會連忙進入輪回,持續履行,從而招致線程抵觸嚴重。

3、改良的CAS應用場景

為懂得決上述成績,只須要讓每次輪回履行的時光變長,便可以年夜幅削減線程抵觸。修正代碼以下:

 

public class CASDemo {
  private final int THREAD_NUM = 1000;
  private final int MAX_VALUE = 1000;
  private AtomicInteger casI = new AtomicInteger(0);
  private int syncI = 0;
  private String path = "/Users/pingping/DataCenter/Books/Linux/Linux經常使用敕令詳解.txt";

  public void casAdd2() throws InterruptedException {
    long begin = System.currentTimeMillis();
    Thread[] threads = new Thread[THREAD_NUM];
    for (int i = 0; i < THREAD_NUM; i++) {
      threads[i] = new Thread(new Runnable() {
        public void run() {
          while (casI.get() < MAX_VALUE) {
            casI.getAndIncrement();
            try (InputStream in = new FileInputStream(new File(path))) {
                while (in.read() != -1);
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      });
      threads[i].start();
    }
    for (int j = 0; j < THREAD_NUM; j++)
      threads[j].join();
    System.out.println("CAS Random costs time: " + (System.currentTimeMillis() - begin));
  }

  public void syncAdd2() throws InterruptedException {
    long begin = System.currentTimeMillis();
    Thread[] threads = new Thread[THREAD_NUM];
    for (int i = 0; i < THREAD_NUM; i++) {
      threads[i] = new Thread(new Runnable() {
        public void run() {
          while (syncI < MAX_VALUE) {
            synchronized ("syncI") {
              ++syncI;
            }
            try (InputStream in = new FileInputStream(new File(path))) {
              while (in.read() != -1);
            } catch (IOException e) {
              e.printStackTrace();
            }
          }
        }
      });
      threads[i].start();
    }
    for (int j = 0; j < THREAD_NUM; j++)
      threads[j].join();
    System.out.println("sync costs time: " + (System.currentTimeMillis() - begin));
  }
}

 

在while輪回中,增長了一個讀取文件內容的操作,該操作年夜概須要耗時40ms,從而可以削減線程抵觸。測試成果以下:

可見在資本抵觸比擬小的情形下,采取CAS方法和synchronized同步效力差不多。為何CAS比擬synchronized沒有取得更高的機能呢?

測試應用的jdk為1.7,而從jdk1.6開端,對鎖的完成引入了年夜量的優化,如鎖粗化(Lock Coarsening)、鎖清除(Lock Elimination)、輕量級鎖(Lightweight Locking)、傾向鎖(Biased Locking)、順應性自旋(Adaptive Spinning)等技巧來削減鎖操作的開支。而個中自旋鎖的道理,相似於CAS自旋,乃至比CAS自旋更加優化。詳細內容請參考 深刻JVM鎖機制1-synchronized。

4、總結

1、應用CAS在線程抵觸嚴重時,會年夜幅下降法式機能;CAS只合適於線程抵觸較少的情形應用。

2、synchronized在jdk1.6以後,曾經改良優化。synchronized的底層完成重要依附Lock-Free的隊列,根本思緒是自旋後壅塞,競爭切換後持續競爭鎖,略微就義了公正性,但取得了高吞吐量。在線程抵觸較少的情形下,可以取得和CAS相似的機能;而線程抵觸嚴重的情形下,機能遠高於CAS。

以上這篇Java並發編程總結——慎用CAS詳解就是小編分享給年夜家的全體內容了,願望能給年夜家一個參考,也願望年夜家多多支撐。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved