当一个线程更新一个变量时,程序如果没有正确的同步,那么这个变量对于其他线程来说是不可见的。我们通常使用synchronized或者volatile来保证线程安全的更新共享变量。在JDK1.5中,提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一种用法简单,性能高效,线程安全地更新一个变量的方式。
Atomic包里一共提供了13个类,有4种类型的原子更新方式:原子更新基本类型、原子更新数组、原子更新引用和原子更新属性。其实现基本都是使用Unsafe实现的包装类。
1.原子更新基本类型类
- AtomicBoolean:原子更新布尔类型
- AtomicInteger:原子更新整型
- AtomicLong:原子更新长整型
以上3个类提供的方法基本一直,我们以AtomicInteger为例进行分析。
AtomicInteger常用的方法有:
- int addAndGet(int delta):以原子方式将输入的数值与实例中的值相加,并返回结果。
- boolean compareAndSet(int expect,int upate):如果输入的数值等于预期值,则以原子方式将该值设置为输入的值。
- int getAndIncrement():以原子方式将当前值加1,返回自增前的值。
- void lazySet(int newValue):最终会设置成new Value,但可能导致其他线程在之后的一小段时间内还是可以读到旧的值。
- int getAndSet(int newValue):以原子方式设置为newValue,并返回旧值。


实例一:

package com.lipeng.seventh;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 
 * 多线程更新atomicIntegerDemo
 * @author LiPeng
 *
 */
public class AtomicIntegerDemo {
    static AtomicInteger atomicInteger=new AtomicInteger();
    public static void main(String[] args) {
        Thread thread1=new Thread(new TestAction(atomicInteger));
        Thread thread2=new Thread(new TestAction(atomicInteger));
        thread1.start();
        thread2.start();
    }

}
class TestAction implements Runnable{
    AtomicInteger atomicInteger;

    public TestAction(AtomicInteger atomicInteger) {
        super();
        this.atomicInteger = atomicInteger;
    }

    @Override
    public void run() {
        for(int i=0;i<10;++i){
            System.out.println(Thread.currentThread().getName()+" exec incrementAndGet menthod[i="+i+"] and new value:"+atomicInteger.getAndIncrement());
        }
    }
}

其实现依靠我们熟悉的CAS算法:

java怎么原子操作 java原子操作有哪些_数组


在Java的基本类型中除了Atomic包中提供原子更新的基本类型外,还由char、float和double。那么这些在Atomic包中没有提供原子更新的基本类型怎么保证其原子更新呢?

从AtomicBoolean源码中我们可以得到答案:首先将Boolean转换为整型,然后使用comareAndSwapInt进行CAS,所以原子更新char、float、double同样可以以此实现。

原子更新数组

- AtomicIntegerArray:原子更新整型数组里的元素。

- AtomicLongArray:原子更新长整型数组里的元素。

- AtomicReferenceArray:原子更新引用类型数组里的元素。

【备注:】看书上说原子更新数组有4个类,除了上述3个外,还有AtomicBooleanArray类,但我在jdk5/6/7/8中都没有找到这个类的存在,只找到共12个原子操作类,而不是标题中的13个。不知道是否是书中的错误?请知情的童鞋不吝赐教。

AtomicIntegerArray类主要提供原子的方式更新数组里的整型,其常用方法如下:

- int addAndGet(int i,int delta):以原子方式将输入值与数组中索引i的元素相加。

- boolean compareAndSet(int i,int expect,int update):如果当前值等于预期值,就把索引i的元素设置成update值。

【备注】:在AtomicIntegerArray构造方法中,AtomicIntegerArray会将数组复制一份,所以当其对内数组元素进行修改时,不会影响原传入数组。

原子更新引用类型

原子更新基本类型的AtomicInteger,只能更新一个变量,如果需要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供以下3个类:

- AtomicReference:原子更新引用变量。

- AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

- AtomicMarkableReference:原子更新带有标记位的引用类型 。可以原子更新一个布尔类型的标记位和引用类型。构造方法时AtomicMarkableReference(V initialRef,boolean initialMark)。


实例一:

package com.lipeng.seventh;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
/**
 * AtomicReferenceDemo
 * @author LiPeng
 *
 */
public class AtomicReferenceDemo {
    static AtomicReference<User> atomicReference=new AtomicReference<User>();
    public static void main(String[] args) {
        User user=new User("1","zhangsan");
        //将user设置进去
        atomicReference.set(user);
        User newUser=new User("2","lisi");
        //cas更新atomicReference里user的引用
        atomicReference.compareAndSet(user, newUser);
        System.out.println(atomicReference.get().toString());
    }

    static class User{
        private String userId;
        private String userName;
        public String getUserId() {
            return userId;
        }
        public void setUserId(String userId) {
            this.userId = userId;
        }
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public User(String userId, String userName) {
            super();
            this.userId = userId;
            this.userName = userName;
        }
        @Override
        public String toString() {
            return "User [userId=" + userId + ", userName=" + userName + "]";
        }

    }
}

实例二:

package com.lipeng.seventh;

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
 * AtomicReferenceFieldUpdaterDemo
 * 原子更新User类中userName字段,此字段必须定义为public且volatile
 * @author LiPeng
 *
 */
public class AtomicReferenceFieldUpdaterDemo {
    static AtomicReferenceFieldUpdater<User, String> atomicFiled=AtomicReferenceFieldUpdater.newUpdater(User.class, String.class, "userName");
    public static void main(String[] args) {
        User user=new User("1","zhangsan");
        atomicFiled.compareAndSet(user, user.userName, "lisi");
        System.out.println(atomicFiled.get(user));
    }
    static class User{
        public String userId;
        public volatile String userName;
        public String getUserId() {
            return userId;
        }
        public void setUserId(String userId) {
            this.userId = userId;
        }
        public String getUserName() {
            return userName;
        }
        public void setUserName(String userName) {
            this.userName = userName;
        }
        public User(String userId, String userName) {
            super();
            this.userId = userId;
            this.userName = userName;
        }
        @Override
        public String toString() {
            return "User [userId=" + userId + ", userName=" + userName + "]";
        }

    }
}

原子更新字段类
如果需要原子的更新某个类里的某个字段,就需要使用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新:
- AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
- AtomicLongFiledUpdater:原子更新长整型字段的更新器。
- AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,用于原子的更新数据和数据的版本号,避免CAS的ABA问题、
想要原子的更新字段类需要调用静态方法newUpdater()创建一个更新器,并设置想要更新的类和属性。且更新类的字段必须使用public volatile修饰符。


实例一:

package com.lipeng.seventh;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
/**
 * AtomicIntegerFieldUpdaterDemo
 * 原子更新user中age字段
 * @author LiPeng
 *
 */
public class AtomicIntegerFieldUpdaterDemo {
    static AtomicIntegerFieldUpdater<User> atomicFiledUpdater=AtomicIntegerFieldUpdater.newUpdater(User.class,"age");
    public static void main(String[] args) {
        User user=new User("zhangsan",24);
        //age 自增一岁
        atomicFiledUpdater.incrementAndGet(user);
        System.out.println(atomicFiledUpdater.get(user));
    }


    static class User{
        public String name;
        public volatile int age;
        public User(String name, int age) {
            super();
            this.name = name;
            this.age = age;
        }

    }
}