Java 并发编程 CAS

1、比较交换 CAS

与锁相比,使用比较交换会使程序看起来更加复杂一些。但由于其非阻塞性,他对死锁问题天生免疫,并且,线程间的相互影响也远远比基于锁的方式要小。使用无锁的方式完全没有锁竞争带来的系统开销,也没有线程间频繁调度带来的开销。

CAS算法过程是这样:它包含三个参数CAS(V,E,N)。 V表示要更新的变量,E表示预期值,N表示新值。 仅当V等于E时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的值。

2、无锁的线程安全整数: AtomicInteger

下面是它的一些方法:

ublic final int get()   返回当前值
public final void set(int newValue)  设置新值
public final int getAndSet(int newValue)  返回旧值  设置新值

public final boolean compareAndSet(int expect, int update)
当期望值是expect 时,更新为update
public final int getAndIncrement()
返回旧值,加一
public final int getAndDecrement()
// 返回旧值 减一
public final int getAndAdd(int delta)
// 返回旧值 加 delta
public final int incrementAndGet()
// 加一,返回新值
public final int decrementAndGet()
// 减一 ,返回新值
public final int addAndGet(int delta)
// 加delta ,返回新值

内部实现,AtomicInteger 中保存一个核心字段。

private volatile int value;

看一下 incrementAndGet()的内部实现

public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

将next 值写入的时刻,必须是当前值(从内存中获取的)与刚刚取得的期望值相同,虽然是刚刚取得,当有可能已经不等于当前值了。

看下 compareAndSet的内部实现:

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

unsafe 是一个特殊的变量,它是sun.misc.Unsafe 类型。这个compareAndSwapInt方法的内部是一个CAS原子指令来完成的。

3、ABA 问题:

CAS算法还有一个问题,就是ABA问题。就是当获得对象数据后,在准备修改为新值前,对象的值被其他新城连续修改了两次,而经过这两次修改后,对象的值又恢复为旧值。这样当前线程就无法正确判断这个对象究竟是否被修改过。

无锁的对象引用AtomicReference对于此问题就不能解决了。AtomicReference 无法解决上述问题的根本原因是对象在修过过程中,丢失了状态信息。对象值本身与状态被画上了等号。因此,我们只要能过记录对象在修改过程中的状态值,就可以很好地解决对象被反复修改导致线程无法正确判断对象状态的问题。

AtomicStampedReference正是这样解决问题的。它内部不仅维护了对象值,还维护了一个时间戳(实际上它可以使用任何一个整数来表示状态值)。当AtomicStampedReference对应的数值被修改时,除了更新数据本身外,还必须要更新时间戳。当AtomicStampedReference设置对象值时,对象值以及时间戳都必须满足期望值,写入才会成功。

AtomicStampedReference 有关时间戳的信息如下:

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


4、数组无锁 AtomicIntegerArray

JDKzhong当前可用的原子数组有:
AtomicIntegerArray AtomicLongArray AtomicReferenceArray。

以下是常用API

public final int get(int i)

public final void set(int i, int newValue)

public final int getAndSet(int i, int newValue)

public final boolean compareAndSet(int i, int expect, int update)

public final int getAndIncrement(int i)

public final int getAndDecrement(int i)

public final int getAndAdd(int i, int delta)

public final int incrementAndGet(int i)

public final int decrementAndGet(int i)

5、AtomicIntegerFieldUpdater

AtomicIntegerFieldUpdater 可以让你在不改动原有代码的基础上,让普通的变量也享受CAS操作带来的线程安全性。 根据数据类型不同,这个Updater有三种,分别是AtomicIntegerFiledUpdater、 AtomicLongFieldUpdater 和AtomicRerferenceFieldUpdater。 下面是AtomicIntegerFiledUpdater是使用。

public class AtomicIntegerFieldUpdaterDemo {

    public static class Candidate {
        int id;
        volatile int score;
    }

    public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");

    // 检查Updater是否正确工作
    public static AtomicInteger allScore = new AtomicInteger(0);

    public static void main(String args[]) throws InterruptedException {

        final Candidate can = new Candidate();
        Thread[] t = new Thread[10000];

        for (int i = 0; i < 10000; i++) {
            t[i] = new Thread() {
                public void run() {
                    if (Math.random() > 0.4) {
                        scoreUpdater.incrementAndGet(can);
                        allScore.incrementAndGet();
                    }
                }
            };

            t[i].start();
        }

        for (int i = 0; i < 10000; i++) {

            t[i].join();

        }
        System.out.println(" score:" + can.score);

        System.out.println(" allScore:" + allScore);

    }

}

AtomicIntegerFieldUpdater 有以下几个注意事项

第一: Updater只能修改它可见范围内的变量。 因为Updater使用反射得到这个变量。private属性是不可以修改的。
第二: 为了确保变量被正确的读取,他必须是volatile。
第三:由于CAS操作会通过对象实例中的偏移量直接进行赋值,因此它不支持Static字段。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值