天天看點

java并發程式設計基礎--原子類與原子更新

一、原子類介紹

1、什麼是原子類

原子類可以認為其操作都是不可分割

2、為什麼要有原子類

對多線程通路同一個變量,我們需要加鎖,而鎖是比較消耗性能的,JDk1.5之後,新增的原子操作類提供了一種用法簡單、性能高效、線程安全地更新一個變量的方式,這些類同樣位于JUC包下的atomic包下,

java并發程式設計基礎--原子類與原子更新

發展到JDk1.8,該包下共有17個類,囊括了原子更新基本類型、原子更新數組、原子更新屬性、原子更新引用,其中,jdk1.8新增的原子類有DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64

二、原子更新

1、原子更新基本類型

1)大緻可以歸為3類

      AtomicBoolean、AtomicInteger、AtomicLong  元老級的原子更新,方法幾乎一模一樣

      DoubleAdder、LongAdder  對Double、Long的原子更新性能進行優化提升

       DoubleAccumulator、LongAccumulator  支援自定義運算

2)示例

/**
 * LongAccumulator Demo
 */
public class Demo2 {

    public static void main(String[] args) {
        //輸入一個數字,如果比上一個輸入的大,則直接傳回,如果小,則傳回上一個
        LongAccumulator longAccumulator = new LongAccumulator((left, right) ->
                left * right, 0L
        );

        longAccumulator.accumulate(3L);
        System.out.println(longAccumulator.get());
        longAccumulator.accumulate(5L);
        System.out.println(longAccumulator.get());
    }

}
           

2、原子更新數組類型

1)類型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray

2)示例:

/**
 * AtomicIntegerArray Demo
 */
public class AtomicIntegerArrayDemo {

    public static void main(String[] args) {
        int[] arr = new int[]{3, 2};
        AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(arr);
        System.out.println(atomicIntegerArray.addAndGet(1, 8));

        int i = atomicIntegerArray.accumulateAndGet(0, 2, (left, right) ->
                left * right / 3
        );


        System.out.println(i);
    }

}
           

3、原子更新屬性

1)類型:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicStampedReference、AtomicReferenceFieldUpdater

2)使用上述類的時候,必須遵循以下原則

        字段必須是volatile類型的,線上程之間共享變量時保證立即可見

        字段的描述類型是與調用者與操作對象字段的關系一緻。也就是說調用者能夠直接操作對象字段,那麼就可以反射進行原子操作。

        對于父類的字段,子類是不能直接操作的,盡管子類可以通路父類的字段。

        隻能是執行個體變量,不能是類變量,也就是說不能加static關鍵字。

        隻能是可修改變量,不能使final變量,因為final的語義就是不可修改。

        對于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater隻能修改int/long類型的字段,不能修改其包裝類型(Integer/Long)。

        如果要修改包裝類型就需要使用AtomicReferenceFieldUpdater。

3)示例:修改Student類中xuhan的字段

/**
 * AtomicLongFieldUpdaterDemo
 */
public class AtomicLongFieldUpdaterDemo {

    public static void main(String[] args) {
        AtomicLongFieldUpdater<Student> longFieldUpdater = AtomicLongFieldUpdater.newUpdater(Student.class, "id");

        Student xuhan = new Student(1L, "xuhan1");
        longFieldUpdater.compareAndSet(xuhan, 1L, 100L);
        System.out.println("id="+xuhan.getId());

        AtomicReferenceFieldUpdater<Student, String> referenceFieldUpdater = AtomicReferenceFieldUpdater.newUpdater(Student.class, String.class, "name");
        referenceFieldUpdater.compareAndSet(xuhan, "虛汗", "小旭");
        System.out.println("name="+xuhan.getName());
    }
}

class Student{
    volatile long id;
    volatile String name;

    public Student(Long id, String name) {
        this.id = id;
        this.name = name;
    }

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

4、 原子更新引用

1)類型:AtomicReference:用于對引用的原子更新

        AtomicMarkableReference:帶版本戳的原子引用類型,版本戳為boolean類型。

        AtomicStampedReference:帶版本戳的原子引用類型,版本戳為int類型。

2)示例:Student類與上面的一緻

/**
 * AtomicReferenceDemo
 */
public class AtomicReferenceDemo {

    public static void main(String[] args) {
        AtomicReference<Student> studentAtomicReference = new AtomicReference<>();
        Student student = new Student(1L, "虛汗");
        Student student1 = new Student(2L, "小旭");
        studentAtomicReference.set(student);
        studentAtomicReference.compareAndSet(student, student1);
        Student student2 = studentAtomicReference.get();
        System.out.println(student2.getName());
    }
}
           

繼續閱讀