最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java原子變量類原理及實例解析

 更新時間:2019年12月28日 08:36:35   作者:靜默虛空  
這篇文章主要介紹了Java原子變量類原理及實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

這篇文章主要介紹了Java原子變量類原理及實例解析,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下

一、原子變量類簡介

為何需要原子變量類

保證線程安全是 Java 并發(fā)編程必須要解決的重要問題。Java 從原子性、可見性、有序性這三大特性入手,確保多線程的數(shù)據(jù)一致性。

確保線程安全最常見的做法是利用鎖機制(Lock、sychronized)來對共享數(shù)據(jù)做互斥同步,這樣在同一個時刻,只有一個線程可以執(zhí)行某個方法或者某個代碼塊,那么操作必然是原子性的,線程安全的?;コ馔阶钪饕膯栴}是線程阻塞和喚醒所帶來的性能問題。

volatile 是輕量級的鎖(自然比普通鎖性能要好),它保證了共享變量在多線程中的可見性,但無法保證原子性。所以,它只能在一些特定場景下使用。

為了兼顧原子性以及鎖帶來的性能問題,Java 引入了 CAS (主要體現(xiàn)在 Unsafe 類)來實現(xiàn)非阻塞同步(也叫樂觀鎖)。并基于 CAS ,提供了一套原子工具類。

原子變量類的作用

原子變量類 比鎖的粒度更細,更輕量級,并且對于在多處理器系統(tǒng)上實現(xiàn)高性能的并發(fā)代碼來說是非常關(guān)鍵的。原子變量將發(fā)生競爭的范圍縮小到單個變量上。

原子變量類相當(dāng)于一種泛化的 volatile 變量,能夠支持原子的、有條件的讀/改/寫操作。

原子類在內(nèi)部使用 CAS 指令(基于硬件的支持)來實現(xiàn)同步。這些指令通常比鎖更快。

原子變量類可以分為 4 組:

  • 基本類型
    • AtomicBoolean - 布爾類型原子類
    • AtomicInteger - 整型原子類
    • AtomicLong - 長整型原子類
  • 引用類型
    • AtomicReference - 引用類型原子類
    • AtomicMarkableReference - 帶有標(biāo)記位的引用類型原子類
    • AtomicStampedReference - 帶有版本號的引用類型原子類
  • 數(shù)組類型
    • AtomicIntegerArray - 整形數(shù)組原子類
    • AtomicLongArray - 長整型數(shù)組原子類
    • AtomicReferenceArray - 引用類型數(shù)組原子類
  • 屬性更新器類型
    • AtomicIntegerFieldUpdater - 整型字段的原子更新器。
    • AtomicLongFieldUpdater - 長整型字段的原子更新器。
    • AtomicReferenceFieldUpdater - 原子更新引用類型里的字段。

這里不對 CAS、volatile、互斥同步做深入探討。如果想了解更多細節(jié),不妨參考:Java 并發(fā)核心機制

二、基本類型

這一類型的原子類是針對 Java 基本類型進行操作。

  • AtomicBoolean - 布爾類型原子類
  • AtomicInteger - 整型原子類
  • AtomicLong - 長整型原子類

以上類都支持 CAS,此外,AtomicInteger、AtomicLong 還支持算術(shù)運算。

提示:

雖然 Java 只提供了 AtomicBoolean 、AtomicInteger、AtomicLong,但是可以模擬其他基本類型的原子變量。要想模擬其他基本類型的原子變量,可以將 short 或 byte 等類型與 int 類型進行轉(zhuǎn)換,以及使用 Float.floatToIntBits 、Double.doubleToLongBits 來轉(zhuǎn)換浮點數(shù)。

由于 AtomicBoolean、AtomicInteger、AtomicLong 實現(xiàn)方式、使用方式都相近,所以本文僅針對 AtomicInteger 進行介紹。

AtomicInteger 用法

public final int get() // 獲取當(dāng)前值
public final int getAndSet(int newValue) // 獲取當(dāng)前值,并設(shè)置新值
public final int getAndIncrement()// 獲取當(dāng)前值,并自增
public final int getAndDecrement() // 獲取當(dāng)前值,并自減
public final int getAndAdd(int delta) // 獲取當(dāng)前值,并加上預(yù)期值
boolean compareAndSet(int expect, int update) // 如果輸入值(update)等于預(yù)期值,將該值設(shè)置為輸入值
public final void lazySet(int newValue) // 最終設(shè)置為 newValue,使用 lazySet 設(shè)置之后可能導(dǎo)致其他線程在之后的一小段時間內(nèi)還是可以讀到舊的值。

AtomicInteger 使用示例:

public class AtomicIntegerDemo {

  public static void main(String[] args) throws InterruptedException {
    ExecutorService executorService = Executors.newFixedThreadPool(5);
    AtomicInteger count = new AtomicInteger(0);
    for (int i = 0; i < 1000; i++) {
      executorService.submit((Runnable) () -> {
        System.out.println(Thread.currentThread().getName() + " count=" + count.get());
        count.incrementAndGet();
      });
    }

    executorService.shutdown();
    executorService.awaitTermination(30, TimeUnit.SECONDS);
    System.out.println("Final Count is : " + count.get());
  }
}

AtomicInteger 實現(xiàn)

閱讀 AtomicInteger 源碼,可以看到如下定義:

private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;

static {
  try {
    valueOffset = unsafe.objectFieldOffset
      (AtomicInteger.class.getDeclaredField("value"));
  } catch (Exception ex) { throw new Error(ex); }
}

private volatile int value;

說明:

  • value - value 屬性使用 volatile 修飾,使得對 value 的修改在并發(fā)環(huán)境下對所有線程可見。
  • valueOffset - value 屬性的偏移量,通過這個偏移量可以快速定位到 value 字段,這個是實現(xiàn) AtomicInteger 的關(guān)鍵。
  • unsafe - Unsafe 類型的屬性,它為 AtomicInteger 提供了 CAS 操作。

三、引用類型

Java 數(shù)據(jù)類型分為 基本數(shù)據(jù)類型 和 引用數(shù)據(jù)類型 兩大類(不了解 Java 數(shù)據(jù)類型劃分可以參考: Java 基本數(shù)據(jù)類型 )。

上一節(jié)中提到了針對基本數(shù)據(jù)類型的原子類,那么如果想針對引用類型做原子操作怎么辦?Java 也提供了相關(guān)的原子類:

  • AtomicReference - 引用類型原子類
  • AtomicMarkableReference - 帶有標(biāo)記位的引用類型原子類
  • AtomicStampedReference - 帶有版本號的引用類型原子類

AtomicStampedReference 類在引用類型原子類中,徹底地解決了 ABA 問題,其它的 CAS 能力與另外兩個類相近,所以最具代表性。因此,本節(jié)只針對 AtomicStampedReference 進行說明。

示例:基于 AtomicReference 實現(xiàn)一個簡單的自旋鎖

public class AtomicReferenceDemo2 {

  private static int ticket = 10;

  public static void main(String[] args) {
    threadSafeDemo();
  }

  private static void threadSafeDemo() {
    SpinLock lock = new SpinLock();
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 5; i++) {
      executorService.execute(new MyThread(lock));
    }
    executorService.shutdown();
  }

  /**
   * 基于 {@link AtomicReference} 實現(xiàn)的簡單自旋鎖
   */
  static class SpinLock {

    private AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void lock() {
      Thread current = Thread.currentThread();
      while (!atomicReference.compareAndSet(null, current)) {}
    }

    public void unlock() {
      Thread current = Thread.currentThread();
      atomicReference.compareAndSet(current, null);
    }

  }

  /**
   * 利用自旋鎖 {@link SpinLock} 并發(fā)處理數(shù)據(jù)
   */
  static class MyThread implements Runnable {

    private SpinLock lock;

    public MyThread(SpinLock lock) {
      this.lock = lock;
    }

    @Override
    public void run() {
      while (ticket > 0) {
        lock.lock();
        if (ticket > 0) {
          System.out.println(Thread.currentThread().getName() + " 賣出了第 " + ticket + " 張票");
          ticket--;
        }
        lock.unlock();
      }
    }
  }
}

原子類的實現(xiàn)基于 CAS 機制,而 CAS 存在 ABA 問題(不了解 ABA 問題,可以參考:Java 并發(fā)基礎(chǔ)機制 - CAS 的問題)。正是為了解決 ABA 問題,才有了 AtomicMarkableReference 和 AtomicStampedReference。

AtomicMarkableReference 使用一個布爾值作為標(biāo)記,修改時在 true / false 之間切換。這種策略不能根本上解決 ABA 問題,但是可以降低 ABA 發(fā)生的幾率。常用于緩存或者狀態(tài)描述這樣的場景。

public class AtomicMarkableReferenceDemo {

  private final static String INIT_TEXT = "abc";

  public static void main(String[] args) throws InterruptedException {

    final AtomicMarkableReference<String> amr = new AtomicMarkableReference<>(INIT_TEXT, false);

    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 10; i++) {
      executorService.submit(new Runnable() {
        @Override
        public void run() {
          try {
            Thread.sleep(Math.abs((int) (Math.random() * 100)));
          } catch (InterruptedException e) {
            e.printStackTrace();
          }

          String name = Thread.currentThread().getName();
          if (amr.compareAndSet(INIT_TEXT, name, amr.isMarked(), !amr.isMarked())) {
            System.out.println(Thread.currentThread().getName() + " 修改了對象!");
            System.out.println("新的對象為:" + amr.getReference());
          }
        }
      });
    }

    executorService.shutdown();
    executorService.awaitTermination(3, TimeUnit.SECONDS);
  }

}

AtomicStampedReference 使用一個整型值做為版本號,每次更新前先比較版本號,如果一致,才進行修改。通過這種策略,可以根本上解決 ABA 問題。

public class AtomicStampedReferenceDemo {

  private final static String INIT_REF = "pool-1-thread-3";

  private final static AtomicStampedReference<String> asr = new AtomicStampedReference<>(INIT_REF, 0);

  public static void main(String[] args) throws InterruptedException {

    System.out.println("初始對象為:" + asr.getReference());

    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 3; i++) {
      executorService.execute(new MyThread());
    }

    executorService.shutdown();
    executorService.awaitTermination(3, TimeUnit.SECONDS);
  }

  static class MyThread implements Runnable {

    @Override
    public void run() {
      try {
        Thread.sleep(Math.abs((int) (Math.random() * 100)));
      } catch (InterruptedException e) {
        e.printStackTrace();
      }

      final int stamp = asr.getStamp();
      if (asr.compareAndSet(INIT_REF, Thread.currentThread().getName(), stamp, stamp + 1)) {
        System.out.println(Thread.currentThread().getName() + " 修改了對象!");
        System.out.println("新的對象為:" + asr.getReference());
      }
    }

  }

}

四、數(shù)組類型

Java 提供了以下針對數(shù)組的原子類:

  • AtomicIntegerArray - 整形數(shù)組原子類
  • AtomicLongArray - 長整型數(shù)組原子類
  • AtomicReferenceArray - 引用類型數(shù)組原子類

已經(jīng)有了針對基本類型和引用類型的原子類,為什么還要提供針對數(shù)組的原子類呢?

數(shù)組類型的原子類為 數(shù)組元素 提供了 volatile 類型的訪問語義,這是普通數(shù)組所不具備的特性——volatile 類型的數(shù)組僅在數(shù)組引用上具有 volatile 語義。

示例:AtomicIntegerArray 使用示例(AtomicLongArray 、AtomicReferenceArray 使用方式也類似)

public class AtomicIntegerArrayDemo {

  private static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(10);

  public static void main(final String[] arguments) throws InterruptedException {

    System.out.println("Init Values: ");
    for (int i = 0; i < atomicIntegerArray.length(); i++) {
      atomicIntegerArray.set(i, i);
      System.out.print(atomicIntegerArray.get(i) + " ");
    }
    System.out.println();

    Thread t1 = new Thread(new Increment());
    Thread t2 = new Thread(new Compare());
    t1.start();
    t2.start();

    t1.join();
    t2.join();

    System.out.println("Final Values: ");
    for (int i = 0; i < atomicIntegerArray.length(); i++) {
      System.out.print(atomicIntegerArray.get(i) + " ");
    }
    System.out.println();
  }

  static class Increment implements Runnable {

    @Override
    public void run() {

      for (int i = 0; i < atomicIntegerArray.length(); i++) {
        int value = atomicIntegerArray.incrementAndGet(i);
        System.out.println(Thread.currentThread().getName() + ", index = " + i + ", value = " + value);
      }
    }

  }

  static class Compare implements Runnable {

    @Override
    public void run() {
      for (int i = 0; i < atomicIntegerArray.length(); i++) {
        boolean swapped = atomicIntegerArray.compareAndSet(i, 2, 3);
        if (swapped) {
          System.out.println(Thread.currentThread().getName() + " swapped, index = " + i + ", value = 3");
        }
      }
    }

  }
}

五、屬性更新器類型

更新器類支持基于反射機制的更新字段值的原子操作。

  • AtomicIntegerFieldUpdater - 整型字段的原子更新器。
  • AtomicLongFieldUpdater - 長整型字段的原子更新器。
  • AtomicReferenceFieldUpdater - 原子更新引用類型里的字段。

這些類的使用有一定限制:

因為對象的屬性修改類型原子類都是抽象類,所以每次使用都必須使用靜態(tài)方法 newUpdater() 創(chuàng)建一個更新器,并且需要設(shè)置想要更新的類和屬性。

  • 字段必須是 volatile 類型的;
  • 不能作用于靜態(tài)變量(static);
  • 不能作用于常量(final);
public class AtomicReferenceFieldUpdaterDemo {

 static User user = new User("begin");

 static AtomicReferenceFieldUpdater<User, String> updater =
  AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "name");

 public static void main(String[] args) {
  ExecutorService executorService = Executors.newFixedThreadPool(3);
  for (int i = 0; i < 5; i++) {
   executorService.execute(new MyThread());
  }
  executorService.shutdown();
 }

 static class MyThread implements Runnable {

  @Override
  public void run() {
   if (updater.compareAndSet(user, "begin", "end")) {
    try {
     TimeUnit.SECONDS.sleep(1);
    } catch (InterruptedException e) {
     e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName() + " 已修改 name = " + user.getName());
   } else {
    System.out.println(Thread.currentThread().getName() + " 已被其他線程修改");
   }
  }

 }

 static class User {

  volatile String name;

  public User(String name) {
   this.name = name;
  }

  public String getName() {
   return name;
  }

  public User setName(String name) {
   this.name = name;
   return this;
  }

 }

}

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • 淺談Mybatis樂觀鎖插件

    淺談Mybatis樂觀鎖插件

    這篇文章主要介紹了淺談Mybatis樂觀鎖插件,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-12-12
  • JVM中四種GC算法案例詳解

    JVM中四種GC算法案例詳解

    這篇文章主要介紹了JVM中四種GC算法案例詳解,本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細內(nèi)容,需要的朋友可以參考下
    2021-09-09
  • SpringBoot使用ResponseBodyEmitter處理流式日志和進度條

    SpringBoot使用ResponseBodyEmitter處理流式日志和進度條

    這篇文章主要為大家詳細介紹了SpringBoot如何使用ResponseBodyEmitter處理流式日志和進度條,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2025-02-02
  • java中equals和等號(==)的區(qū)別淺談

    java中equals和等號(==)的區(qū)別淺談

    java中equals和等號(==)的區(qū)別淺談,需要的朋友可以參考一下
    2013-05-05
  • IDEA的Mybatis Generator駝峰配置問題

    IDEA的Mybatis Generator駝峰配置問題

    這篇文章主要介紹了IDEA的Mybatis Generator駝峰配置問題,本文給大家介紹的非常詳細,對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2020-11-11
  • FeignClient如何共享Header及踩坑過程記錄

    FeignClient如何共享Header及踩坑過程記錄

    這篇文章主要介紹了FeignClient如何共享Header及踩坑過程記錄,
    2022-03-03
  • Spring MVC如何設(shè)置響應(yīng)

    Spring MVC如何設(shè)置響應(yīng)

    本文介紹了如何在Spring框架中設(shè)置響應(yīng),并通過不同的注解返回靜態(tài)頁面、HTML片段和JSON數(shù)據(jù),此外,還講解了如何設(shè)置響應(yīng)的狀態(tài)碼和Header
    2025-01-01
  • JAVA獲取CLASSPATH路徑的方法詳解

    JAVA獲取CLASSPATH路徑的方法詳解

    這篇文章主要介紹了Java 中獲取類路徑 classpath 的方法,非常不錯,具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • java生成圖片驗證碼功能

    java生成圖片驗證碼功能

    最近在用ssm框架做一個管理系統(tǒng),做到登錄驗證時,需要實現(xiàn)圖片驗證碼功能,今天小編給大家分享基于java制作的圖片驗證碼功能,感興趣的朋友一起看看吧
    2019-12-12
  • Java自定義類加載器實現(xiàn)類隔離詳解

    Java自定義類加載器實現(xiàn)類隔離詳解

    由于每種組件的不同版本所依賴的jar包不同,我們可以借鑒tomcat的實現(xiàn)方式,通過自定義類加載器打破雙親委派機制來實現(xiàn)類隔離,從而達到操作多組件多版本的目的。本文就來和大家詳細聊聊實現(xiàn)方法
    2023-03-03

最新評論

论坛| 汝阳县| 都昌县| 长白| 定安县| 苗栗县| 永宁县| 乌拉特前旗| 永州市| 江北区| 阜阳市| 马鞍山市| 株洲市| 公主岭市| 清徐县| 神池县| 五峰| 富锦市| 凌源市| 孝义市| 故城县| 申扎县| 松滋市| 阿合奇县| 元朗区| 南通市| 会泽县| 大连市| 弋阳县| 丹巴县| 岳普湖县| 灵寿县| 锦州市| 新巴尔虎右旗| 晋城| 南靖县| 丰都县| 阳原县| 大庆市| 桑植县| 湘潭市|