您的位置:首页 > 编程语言 > Java开发

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

2018-04-02 10:31 585 查看
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 exceptio
4000
n
                 }
              }
          };
       }.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);
   }
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  并发编程