在多线程操作中经常会出现多个线程对一个共享变量的并发修改,为了保证此操作的正确性,最初的时候可以通过synchronized关键字操作。JDK1.5之后,可以通过java.util.concurrent.atomic包中的原子操作类来操作。
此包中提供的原子操作类可以分为4类:
- 基本类型:AtomicInteger、 AtomicLong、 AtomicBoolean。
- 数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray。
- 引用类型:AtomicReference、AtomicStampedReference【带有引用版本号的】、 AtomicMarkableReference【标记节点】。
- 对象的属性修改类型:AtomicIntegerFieldFupdater、 AtomicLongFieldUpdater、AtomicReferenceFieldUpdater。
原子操作类最大的特点是可以进行线程安全更新,即帮助用户使用一种更为简单的共享数据的线程同步处理操作,所以通过源代码,我们可以发现,在这些原子类数据保存属性上都使用类volatile关键字进行声明,这样就可以防止由于数据缓存所造成的数据更新不一致的问题。
1-1、【基本类型】使用AtomicLong进行原子性操作的例子:
package com.mydemo;
import java.util.concurrent.atomic.AtomicLong;
public class JUCDemo {
public static void main(String[] args) {
// 实例化原子操作类
AtomicLong atomicLong = new AtomicLong(100l);
// 增加数据并取得
atomicLong.addAndGet(200);
// 先获取而后再自增
long current = atomicLong.getAndIncrement();
// 自增前的内容
System.out.println(current);
// 自增后的内容
System.out.println(atomicLong.get());
}
}
运行结果:
300
301
1-2、【基本类型】利用多线程操作的例子:
package com.mydemo;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicLong;
public class JUCDemo {
public static void main(String[] args) {
// 实例化原子操作类
AtomicLong atomicLong = new AtomicLong(100);
for (int i = 0; i < 10; i++) {
new Thread(
() -> {
// 增加数据并取得
atomicLong.addAndGet(200);
}
).start();
}
// 休眠2秒,等待执行结果
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 自增后的内容
System.out.println(atomicLong.get());
}
}
运行结果:
2100
1-3、【基本类型】判断并设置新内容的例子:
package com.mydemo;
import java.util.concurrent.atomic.AtomicLong;
public class JUCDemo {
public static void main(String[] args) {
// 实例化原子操作类
AtomicLong atomicLong = new AtomicLong(100l);
// 内容相同,返回true
System.out.println(atomicLong.compareAndSet(100l, 300l));
// 取出数据
System.out.println(atomicLong.get());
}
}
运行结果:
true
300
2-1、【数组类型】使用AtomicReferenceArray类操作的例子:
package com.mydemo;
import java.util.concurrent.atomic.AtomicReferenceArray;
public class JUCDemo {
public static void main(String[] args) {
String infos[] = new String[]{
"www.baidu.com",
"www.google.com",
"www.alibaba.com"
};
AtomicReferenceArray<String> atomicReferenceArray = new AtomicReferenceArray<>(infos);
/**
* 在使用compareAndSet()方法进行比较时,是通过“==”方式实现的比较操作
*
* 原因:
* 由底层C语言实现的。
* 该类操作是基于地址指针的形式处理的,所以只允许通过“==”进行地址判断
*/
System.out.println(atomicReferenceArray.compareAndSet(0, "www.baidu123.com", "www.test.com"));
System.out.println(atomicReferenceArray.compareAndSet(1, "www.google.com", "www.test.com"));
// 输出
System.out.println(atomicReferenceArray.get(0));
System.out.println(atomicReferenceArray.get(1));
}
}
运行结果:
false
true
www.baidu.com
www.test.com
3-1、【引用类型】使用AtomicReference类操作引用数据的例子:
package com.mydemo;
import java.util.concurrent.atomic.AtomicReference;
public class JUCDemo {
public static void main(String[] args) {
// 实例化Member实例
Member memberA = new Member("张三", 18);
Member memberB = new Member("李四", 20);
AtomicReference<Member> atomicReference = new AtomicReference<>(memberA);
// 修改当前保存数据
atomicReference.compareAndSet(memberA, memberB);
// 输出当前数据内容
System.out.println(atomicReference);
}
}
class Member {
private String name;
private int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Member{" +
"姓名:'" + name + '\'' +
", 年龄:" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
运行结果:
Member{姓名:'李四', 年龄:20}
3-2、【引用类型】使用AtomicStampedReference类进行引用原子性操作的例子:
package com.mydemo;
import java.util.concurrent.atomic.AtomicStampedReference;
public class JUCDemo {
public static void main(String[] args) {
// 实例化Member实例
Member memberA = new Member("张三", 18);
Member memberB = new Member("李四", 20);
// 由于AtomicStampedReference需要提供版本号,所以在初始化时定义版本号为1
AtomicStampedReference<Member> atomicStampedReference =
new AtomicStampedReference<>(memberA, 1);
// 在进行CAS操作时除了要设置替换内容外,
// 也需要设置正确的版本号,否则无法替换。
atomicStampedReference.compareAndSet(memberA, memberB, 1, 2);
// 输出当前数据内容
System.out.println(atomicStampedReference.getReference());
// 获取版本号
System.out.println(atomicStampedReference.getStamp());
}
}
class Member {
private String name;
private int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Member{" +
"姓名:'" + name + '\'' +
", 年龄:" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
运行结果:
Member{姓名:'李四', 年龄:20}
2
3-3、【引用类型】使用AtomicMarkableReference类进行标记原子性操作的例子:
package com.mydemo;
import java.util.concurrent.atomic.AtomicMarkableReference;
public class JUCDemo {
public static void main(String[] args) {
// 实例化Member实例
Member memberA = new Member("张三", 18);
Member memberB = new Member("李四", 20);
// 由于AtomicMarkableReference需要提供标记位才可以进行判断
AtomicMarkableReference<Member> atomicMarkableReference =
new AtomicMarkableReference<>(memberA, true);
// 在进行CAS操作时除了要设置替换内容外,
// 也需要设置标记号,否则无法替换。
atomicMarkableReference.compareAndSet(memberA, memberB, true, false);
// 输出当前数据内容
System.out.println(atomicMarkableReference.getReference());
}
}
class Member {
private String name;
private int age;
public Member(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public String toString() {
return "Member{" +
"姓名:'" + name + '\'' +
", 年龄:" + age +
'}';
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
运行结果:
Member{姓名:'李四', 年龄:20}
4-1、【对象的属性修改类型】实现属性操作的例子:
package com.mydemo;
import java.util.concurrent.atomic.AtomicLongFieldUpdater;
public class JUCDemo {
public static void main(String[] args) {
Book book = new Book(1001, "java从入门到放弃");
book.setId(2003);
System.out.println(book);
}
}
class Book {
/**
* 必须使用volatile定义
* 否则将会出现IllegalArgumentException异常
*/
// private long id;
private volatile long id;
private String title;
public Book(long id, String title) {
this.id = id;
this.title = title;
}
@Override
public String toString() {
return "Book{" +
"id:" + id +
", 名称:'" + title + '\'' +
'}';
}
public long getId() {
return id;
}
public void setId(long id) {
// this.id = id;
AtomicLongFieldUpdater<Book> atomicLongFieldUpdater =
AtomicLongFieldUpdater.newUpdater(Book.class, "id");
atomicLongFieldUpdater.compareAndSet(this, this.id, id);
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
}
运行结果:
Book{id:2003, 名称:'java从入门到放弃'}
4-2、【对象的属性修改类型】并发计算的例子:
package com.mydemo;
import java.util.concurrent.atomic.DoubleAccumulator;
public class JUCDemo {
public static void main(String[] args) {
/**
* 原子性的累加器只适合于进行基础的数据统计,
* 并不适用于其他更加细粒度的操作。
*/
// 累加器
DoubleAccumulator doubleAccumulator = new DoubleAccumulator(
(x, y) -> x + y, 1.1
);
// 原始内容
System.out.println(doubleAccumulator.doubleValue());
// 加法计算
doubleAccumulator.accumulate(20);
// 获取数据
System.out.println(doubleAccumulator.get());
}
}
运行结果:
1.1
21.1
4-3、【对象的属性修改类型】使用加法器计算的例子:
package com.mydemo;
import java.util.concurrent.atomic.DoubleAdder;
public class JUCDemo {
public static void main(String[] args) {
// 定义加法器
DoubleAdder doubleAdder = new DoubleAdder();
// 数据执行加法
doubleAdder.add(10);
doubleAdder.add(20);
doubleAdder.add(30);
// 数据累加
System.out.println(doubleAdder.sum());
}
}
运行结果:
60.0