CAS无锁类的使用

转自: https://blog.csdn.net/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、付费专栏及课程。

余额充值