Java高并发程序-Chapter5 锁的优化及注意事项(第三十三讲)无锁 - 无锁类的使用

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,返回新值


方法实现

    public final int getAndUpdate(IntUnaryOperator updateFunction) {
        int prev, next;
        do {
            prev = get();
            next = updateFunction.applyAsInt(prev);
        } while (!compareAndSet(prev, next));
        return prev;
    }

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

    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;

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)

使用用例:

static AtomicReference<Integer> money = new AtomicReference<Integer>();  

money.set(19); 

for(int i=0; i<3; i++) {  
    new Thread() {  
        public void run() {  
            while(true) {  
                while(true) {  
                    Integer m = money.get();  
                    if(m<20) {  
                        if(money.compareAndSet(m, m+20)) {  
                            System.out.println("余额小于20元,充值成功,余额:"+ money.get() + "元");  
                            break;  
                        } else {  
                            break;  
                        }  
                    }  
                }  
            }  
        };  
    }.start();  




new Thread() {  
    public void run() {  
        for (int j = 0; j < 100; j++) {  
            while(true) {  
                Integer m = money.get();  
                if(m>10) {  
                    System.out.println("大于10元");  
                    if(money.compareAndSet(m, m-10)) {  
                        System.out.println("成功消费10元,余额:" + money.get());  
                        break;  
                    }  
                } else {  
                    System.out.println("没有足够金额");  
                    break;  
                }  
            }  
            try {  
                Thread.sleep(100);  
            } catch (InterruptedException e) {  
  
            }  
        }  
    };  
}.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. 使用用例

 //比较设置参数依次为:期望值  写入新值  期望时间戳  新时间戳
public boolean compareAndSet(V   expectedReference,
                                 V   newReference,
                                 int expectedStamp,
                                 int newStamp)
//获得当前对象引用
public V getReference()
//获得当前时间戳
public int getStamp()
//设置当前对象引用和时间戳
public void set(V newReference, int newStamp) 
      使用AtomicStampedReference来修正那个贵宾卡充值的问题:
public class AtomicStampedReferenceDemo {
static AtomicStampedReference<Integer> money = new AtomicStampedReference<Integer>(19,0);
   public static void main(String[] args) {
       for(int i=0; i<3; i++) {
          final int timestamp = money.getStamp();
          new Thread() {
              public void run() {
                 while(true) {
                     while(true) {
                        Integer m = money.getReference();
                        if(m<20) {
                            if(money.compareAndSet(m, m+20,timestamp,timestamp+1)) {
                               System.out.println("余额小于20元,充值成功,余额:"+ money.getReference() + "元");
                               break;
                            } else {
                               break;
                            }
                        }
                     }
                 }
              };
          }.start();
       }
       new Thread() {
          public void run() {
              for (int j = 0; j < 100; j++) {
                 while(true) {
                     int timestamp = money.getStamp();
                     Integer m = money.getReference();
                     if(m>10) {
                        System.out.println("大于10元");
                        if(money.compareAndSet(m, m-10,timestamp,timestamp+1)) {
                            System.out.println("成功消费10元,余额:" + money.getReference());
                            break;
                        }
                     } else {
                        System.out.println("没有足够金额");
                        break;
                     }
                 }
                 try {
                     Thread.sleep(100);
                 } catch (InterruptedException e) {
                     // TODO: handle exception
                 }
              }
          };
       }.start();
   }
}

5. AtomicIntegerArray

//获得数组第i个下标的元素
public final int get(int i)
//获得数组的长度
public final int length()
//将数组第i个下标设置为newValue,并返回旧的值
public final int getAndSet(int i, int newValue)
//进行CAS操作,如果第i个下标的元素等于expect,则设置为update,设置成功返回true
public final boolean compareAndSet(int i, int expect, int update)
//将第i个下标的元素加1
public final int getAndIncrement(int i)
//将第i个下标的元素减1
public final int getAndDecrement(int i)
//将第i个下标的元素增加delta(delta可以是负数)
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()不支持静态变量)。

使用用例:

public class AtomicIntegerFieldUpdaterDemo {
   public static class Candidate {
       int id;
       volatile int score;
   }
   public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater 
       = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
   public static AtomicInteger allScore = new AtomicInteger(0);
   public static void main(String[] args) throws InterruptedException {
       final Candidate stu = new Candidate();
       Thread[] t = new Thread[10000];
       for(int i=0; i<10000; i++) {
          t[i] = new Thread() {
              @Override
              public void run() {
                 if(Math.random() > 0.4) {
                     scoreUpdater.incrementAndGet(stu);
                     allScore.incrementAndGet();
                 }
              }
          };
          t[i].start();
       }
       for(int i=0; i<10000; i++) {t[i].join();}
       System.out.println("score=" + stu.score);
       System.out.println("allScore=" + allScore);
   }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值