天天看点

Java并发Concurrent包的原子类原子变量类原子数组类原子更新类

Java 并发包 Concurrent 的包结构共可分为五个部分:

- 原子类

- 锁

- collection并发集合框架

- excutor线程池

- 同步工具

本文介绍各种原子类。

主要有原子变量类,原子数组类,原子更新类。

原子变量类

原子变量类位于 java.util.concurrent.atomic 包下。

AtomicInteger 可以用原子方式更新的 int 值。

AtomicBoolean 可以用原子方式更新的 boolean 值。

AtomicLong 可以用原子方式更新的 long 值。

AtomicReference 可以用原子方式更新的对象引用。

以 AtomicInteger 为例,其它原子类的原理都一样:

int get()

获取当前值。

void set(int newValue)

设置为给定值。

void lazySet(int newValue)

最后设置为给定值。

int getAndSet(int newValue)

以原子方式设置为给定值,并返回旧值。

这个相当于线程安全的下边的代码:

int temp = i;
    i = newValue;
    return temp;
           

boolean compareAndSet(int expect, int update)

如果当前值等于预期值,则以原子方式将该值设置为给定的更新值。

boolean weakCompareAndSet(int expect, int update)

如果当前值等于预期值,则以原子方式将该值设置为给定的更新值。

int getAndIncrement()

以原子方式将当前值加 1。返回旧值。

相当于线程安全的 i++ 操作。

int getAndDecrement()

以原子方式将当前值减1。返回旧值。

相当于线程安全的 i– 操作。

int getAndAdd(int delta)

以原子方式将给定值与当前值相加,返回以前的旧值。

这个相当于线程安全的下边的代码:

int temp = i;
    i += delta;
    return temp;
           

int incrementAndGet()

以原子方式将当前值加 1。返回新值。

相当于线程安全的 ++i 操作。

int decrementAndGet()

以原子方式将当前值减 1。返回新值。

相当于线程安全的 –i 操作。

int addAndGet(int delta)

以原子方式将给定值与当前值相加,返回新值。

这个相当于线程安全的 i += delta 操作。

测试,起 10万 个线程,每个线程将变量自增一次,等所有线程执行完毕,然后查看运行结果:

public class TestAtomic {

    private static AtomicInteger a = new AtomicInteger();

    public static void main(String[] args) throws InterruptedException {
        final int threadSize = ;
        Thread[] ts = new Thread[threadSize];
        for (int i = ; i < threadSize; i++) {
            ts[i] = new Thread() {
                public void run() {
                    a.incrementAndGet();
                }
            };
        }
        for(Thread t:ts) {
            t.start();
        }
        for(Thread t:ts) {
            t.join();
        }
        System.out.println(a);
    }

}
           

运行结果是:

100000
           

可以用普通的 int 类型来做一次比较,修改程序如下:

使用 a++ 操作。

public class TestAtomic {

    private static int a = ;

    public static void main(String[] args) throws InterruptedException {
        final int threadSize = ;
        Thread[] ts = new Thread[threadSize];
        for (int i = ; i < threadSize; i++) {
            ts[i] = new Thread() {
                public void run() {
                    a++;
                }
            };
        }
        for(Thread t:ts) {
            t.start();
        }
        for(Thread t:ts) {
            t.join();
        }
        System.out.println(a);
    }

}
           

运行结果,每次运行结果都不相同,但一般只会比 10万 小一些:

99991
           

原因就是 i++ 不是原子性的,产生了竞态条件。

关于竞态条件可以参考: Java中竞态条件和使用synchronized关键字同步

http://blog.csdn.net/yx0628/article/details/79087110

原子数组类

以 AtomicIntegerArray 为例,其它的 AtomicLongArray , AtomicReferenceArray 都类似。

下面的方法,可以参考 AtomicInteger 的相应方法:http://blog.csdn.net/yx0628/article/details/79420960

int get(int i)

获取位置 i 的当前值。

void set(int i, int newValue)

将位置 i 的元素设置为给定值。

void lazySet(int i, int newValue)

最后将位置 i 的元素设置为给定值。

int getAndSet(int i, int newValue)

将位置 i 的元素以原子方式设置为给定值,并返回旧值。

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

如果当前值等于预期值,则以原子方式将位置 i 的元素设置为给定的更新值。

boolean weakCompareAndSet(int i, int expect, int update)

如果当前值等于预期值,则以原子方式将位置 i 的元素设置为给定的更新值。

int getAndIncrement(int i)

以原子方式将索引 i 的元素加 1。

int getAndDecrement(int i)

以原子方式将索引 i 的元素减 1。

int getAndAdd(int i, int delta)

以原子方式将给定值与索引 i 的元素相加。

int incrementAndGet(int i)

以原子方式将索引 i 的元素加 1。

int decrementAndGet(int i)

以原子方式将索引 i 的元素减 1。

int addAndGet(int i, int delta)

以原子方式将给定值与索引 i 的元素相加。

原子更新类

原子更新类和原子类的方法基本类似,不过是基于反射来更新的。

AtomicIntegerFieldUpdater

基于反射的实用工具,可以对指定类的指定 volatile int 字段进行原子更新。

AtomicLongFieldUpdater

基于反射的实用工具,可以对指定类的指定 volatile long 字段进行原子更新。

AtomicReferenceFieldUpdater

基于反射的实用工具,可以对指定类的指定 volatile 字段进行原子更新。

更新类的要求:

- 字段必须是 volatile 类型, 不能是 static 或者 final 类型;

- 调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段;

- AtomicIntegerFieldUpdater 和 AtomicLongFieldUpdater 只能修改 int/long 类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdater。

附上成员修饰符访问范围:

Java并发Concurrent包的原子类原子变量类原子数组类原子更新类

下面以 AtomicReferenceFieldUpdater 的方法为例来看:

boolean compareAndSet(T obj, int expect, int update)

如果当前值等于预期值,则以原子方式将此更新器管理的给定对象的字段设置为给定的更新值。

boolean weakCompareAndSet(T obj, int expect, int update)

如果当前值等于预期值,则以原子方式将此更新器管理的给定对象的字段设置为给定的更新值。

void set(T obj, int newValue)

将此更新器管理的给定对象的字段设置为给定更新值。

void lazySet(T obj, int newValue)

最终将此更新器管理的给定对象的字段设置为给定更新值。

int get(T obj)

获取由此更新器管理的在给定对象的字段中保持的当前值。

int getAndSet(T obj, int newValue)

将此更新器管理的给定对象的字段自动设置为给定值,并返回旧值。

AtomicReferenceFieldUpdater<U,W> newUpdater(Class<U> tclass, Class<W> vclass, String fieldName)

使用给定的字段为对象创建和返回一个更新器。

一个更新器的例子

Father.java

public class Father {
    public volatile int fatherValue = ;
}
           

Son.java

public class Son extends Father{
    public volatile int publicValue = ;
    volatile int defaultValue = ;
    protected volatile int protectedValue = ;      
    private volatile int privateValue = ;
}
           

Test类:

public class TestAtomic {

    public static void main(String[] args) {
        TestAtomic test = new TestAtomic();
        test.test();
    }

    private AtomicIntegerFieldUpdater<Son> getUpdater(String fieldname){
        return AtomicIntegerFieldUpdater.newUpdater(Son.class, fieldname);
    }

    private void test() {
        Son son = new Son();
        System.out.println(getUpdater("publicValue").getAndAdd(son, ));
        System.out.println(getUpdater("defaultValue").incrementAndGet(son));
        System.out.println(getUpdater("protectedValue").decrementAndGet(son));
        System.out.println(getUpdater("privateValue").compareAndSet(son, , ));
        // 父类的public
        System.out.println(getUpdater("fatherValue").getAndAdd(son, ));
    }
}
           

如果是这样的包结构:

Java并发Concurrent包的原子类原子变量类原子数组类原子更新类

运行结果:

publicValue : 可以访问。

defaultValue : 报错。原因:java.lang.IllegalAccessException 外部包 TestAtomic 类不能访问 Son 中的变量。

protectedValue : 报错。原因:java.lang.IllegalAccessException 外部包 TestAtomic 类不能访问 Son 中的变量。

privateValue:报错。原因:java.lang.IllegalAccessException 外部包 TestAtomic 类不能访问 Son 中的变量。

fatherValue : 报错。原因:java.lang.NoSuchFieldException: fatherValue 父变量访问不到。

那么如果把包结构改成这样的:

Java并发Concurrent包的原子类原子变量类原子数组类原子更新类

运行结果:

publicValue : 可以访问。

defaultValue : 可以访问,本包可以访问。

protectedValue : 可以访问,本包可以访问。

privateValue:报错。原因:java.lang.IllegalAccessException 外部包 TestAtomic 类不能访问 Son 中的变量。

fatherValue : 报错。原因:java.lang.NoSuchFieldException: fatherValue 父变量访问不到。

通过这个例子可以说明原子更新类的变量访问限制。