CAS无锁类的使用

本文详细介绍了Java并发包中Atomic类的使用与原理,包括AtomicInteger、AtomicReference、AtomicStampedReference等,探讨了它们如何解决线程安全问题,以及在实际应用中的案例。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转自: https://blog.youkuaiyun.com/netcobol/article/details/79785443

1. AtomicInteger

API

public final int get() //取得当前值

public final void set(int newValue) //设置当前值

public final int getAndSet(int newValue) //设置新值,并返回旧值
public final boolean compareAndSet(int expect, int updated)//如果当前值为expect,则设置为u
public final int getAndIncrement() //当前值加1,返回旧值
public final int getAndDecrement() //当前值减1,返回旧值
public final int getAndAdd(int delta) //当前值增加delta,返回旧值
public final int incrementAndGet() //当前值加1,返回新值
public final int decrementAndGet() //当前值减1,返回新值

public final int addAndGet(int delta) //当前值增加delta,返回新值


方法实现

  1.     public final int getAndUpdate(IntUnaryOperator updateFunction) {
  2.         int prev, next;
  3.         do {
  4.             prev = get();
  5.             next = updateFunction.applyAsInt(prev);
  6.         } while (!compareAndSet(prev, next));
  7.         return prev;
  8.     }
  9.     public final boolean compareAndSet(int expect, int update) {
  10.         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
  11.     }
  12.     private static final Unsafe unsafe = Unsafe.getUnsafe();
  13.     private static final long valueOffset;
  14.  
  15.     static {
  16.         try {
  17.             valueOffset = unsafe.objectFieldOffset
  18.                 (AtomicInteger.class.getDeclaredField("value"));
  19.         } catch (Exception ex) { throw new Error(ex); }
  20.     }
  21.     private volatile int value;

2. Unsafe类

概述:非安全的操作,比如:
根据偏移量设置值
park()
底层的CAS操作

非公开API,在不同版本的JDK中, 可能有较大差异

主要方法

//获得给定对象偏移量上的int值
public native int getInt(Object o, long offset);
//设置给定对象偏移量上的int值
public native void putInt(Object o, long offset, int x);
//获得字段在对象中的偏移量
public native long objectFieldOffset(Field f);
//设置给定对象的int值,使用volatile语义
public native void putIntVolatile(Object o, long offset, int x);
//获得给定对象对象的int值,使用volatile语义
public native int getIntVolatile(Object o, long offset);
//和putIntVolatile()一样,但是它要求被操作字段就是volatile类型的

public native void putOrderedInt(Object o, long offset, int x);


3. AtomicReference

概述:对引用进行修改

是一个模板类,抽象化了数据类型

主要方法:

get()

set(V)
compareAndSet()

getAndSet(V)

使用用例:

  1. static AtomicReference<Integer> money = new AtomicReference<Integer>();
  2. money.set(19);
  3. for(int i=0; i<3; i++) {
  4. new Thread() {
  5. public void run() {
  6. while(true) {
  7. while(true) {
  8. Integer m = money.get();
  9. if(m<20) {
  10. if(money.compareAndSet(m, m+20)) {
  11. System.out.println("余额小于20元,充值成功,余额:"+ money.get() + "元");
  12. break;
  13. } else {
  14. break;
  15. }
  16. }
  17. }
  18. }
  19. };
  20. }.start();
  21. new Thread() {
  22. public void run() {
  23. for (int j = 0; j < 100; j++) {
  24. while(true) {
  25. Integer m = money.get();
  26. if(m>10) {
  27. System.out.println("大于10元");
  28. if(money.compareAndSet(m, m-10)) {
  29. System.out.println("成功消费10元,余额:" + money.get());
  30. break;
  31. }
  32. } else {
  33. System.out.println("没有足够金额");
  34. break;
  35. }
  36. }
  37. try {
  38. Thread.sleep(100);
  39. } catch (InterruptedException e) {
  40. }
  41. }
  42. };
  43. }.start();
4. AtomicStampedReference

a. ABA问题

b. 主要方法

//比较设置参数依次为:期望值写入新值期望时间戳新时间戳
public boolean compareAndSet(V expectedReference,V newReference,int expectedStamp,int newStamp)
//获得当前对象引用
public V getReference()
//获得当前时间戳
public int getStamp()
//设置当前对象引用和时间戳

public void set(V newReference, int newStamp)

c. 使用用例

  1.  //比较设置参数依次为:期望值  写入新值  期望时间戳  新时间戳
  2. public boolean compareAndSet(V   expectedReference,
  3.                                  V   newReference,
  4.                                  int expectedStamp,
  5.                                  int newStamp)
  6. //获得当前对象引用
  7. public V getReference()
  8. //获得当前时间戳
  9. public int getStamp()
  10. //设置当前对象引用和时间戳
  11. public void set(V newReference, int newStamp)
  12.       使用AtomicStampedReference来修正那个贵宾卡充值的问题:
  13. public class AtomicStampedReferenceDemo {
  14. static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19,0);
  15.    public static void main(String[] args) {
  16.        for(int i=0; i<3; i++) {
  17.           final int timestamp = money.getStamp();
  18.           new Thread() {
  19.               public void run() {
  20.                  while(true) {
  21.                      while(true) {
  22.                         Integer m = money.getReference();
  23.                         if(m<20) {
  24.                             if(money.compareAndSet(m, m+20,timestamp,timestamp+1)) {
  25.                                System.out.println("余额小于20元,充值成功,余额:"+ money.getReference() + "元");
  26.                                break;
  27.                             } else {
  28.                                break;
  29.                             }
  30.                         }
  31.                      }
  32.                  }
  33.               };
  34.           }.start();
  35.        }
  36.        new Thread() {
  37.           public void run() {
  38.               for (int j = 0; j < 100; j++) {
  39.                  while(true) {
  40.                      int timestamp = money.getStamp();
  41.                      Integer m = money.getReference();
  42.                      if(m>10) {
  43.                         System.out.println("大于10元");
  44.                         if(money.compareAndSet(m, m-10,timestamp,timestamp+1)) {
  45.                             System.out.println("成功消费10元,余额:" + money.getReference());
  46.                             break;
  47.                         }
  48.                      } else {
  49.                         System.out.println("没有足够金额");
  50.                         break;
  51.                      }
  52.                  }
  53.                  try {
  54.                      Thread.sleep(100);
  55.                  } catch (InterruptedException e) {
  56.                      // TODO: handle exception
  57.                  }
  58.               }
  59.           };
  60.        }.start();
  61.    }
  62. }

5. AtomicIntegerArray

  1. //获得数组第i个下标的元素
  2. public final int get(int i)
  3. //获得数组的长度
  4. public final int length()
  5. //将数组第i个下标设置为newValue,并返回旧的值
  6. public final int getAndSet(int i, int newValue)
  7. //进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true
  8. public final boolean compareAndSet(int i, int expect, int update)
  9. //将第i个下标的元素加1
  10. public final int getAndIncrement(int i)
  11. //将第i个下标的元素减1
  12. public final int getAndDecrement(int i)
  13. //将第i个下标的元素增加delta(delta可以是负数)
  14. public final int getAndAdd(int i, int delta)

6. AtomicIntegerFieldUpdater

主要方法:

AtomicIntegerFieldUpdater.newUpdater()
incrementAndGet()

1.
Updater只能修改它可见范围内的变量。因为Updater使用反射得到这个变量。如果变量不可见,就会出错。比如如果score申明为private,就是不可行的。
2.
为了确保变量被正确的读取,它必须是volatile类型的。如果我们原有代码中未申明这个类型,那么简单得申明一下就行,这不会引起什么问题。
6

3. 由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此,它不支持static字段(Unsafe.objectFieldOffset()不支持静态变量)。

使用用例:

  1. public class AtomicIntegerFieldUpdaterDemo {
  2.    public static class Candidate {
  3.        int id;
  4.        volatile int score;
  5.    }
  6.    public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater
  7.        = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
  8.    public static AtomicInteger allScore = new AtomicInteger(0);
  9.    public static void main(String[] args) throws InterruptedException {
  10.        final Candidate stu = new Candidate();
  11.        Thread[] t = new Thread[10000];
  12.        for(int i=0; i<10000; i++) {
  13.           t[i] = new Thread() {
  14.               @Override
  15.               public void run() {
  16.                  if(Math.random() > 0.4) {
  17.                      scoreUpdater.incrementAndGet(stu);
  18.                      allScore.incrementAndGet();
  19.                  }
  20.               }
  21.           };
  22.           t[i].start();
  23.        }
  24.        for(int i=0; i<10000; i++) {t[i].join();}
  25.        System.out.println("score=" + stu.score);
  26.        System.out.println("allScore=" + allScore);
  27.    }
  28. }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值