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

《Java并发编程的艺术》第七章——Java中的13个原子操作类

2018-01-21 12:55 1321 查看
当一个线程更新一个变量时,程序如果没有正确的同步,那么这个变量对于其他线程来说是不可见的。我们通常使用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的基本类型中除了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;
}

}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: