简易的小企业网站建设,申请学校网站建设申请书,wordpress积分商城,pc网站和手机网站Java 从 JDK 1.5 开始提供了 java.util.concurrent.atomic 包#xff08;以下简称Atomic包#xff09;#xff0c;这个包中的 原子操作类 提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。
因为变量的类型有很多种#xff0c;所以在 Atomic 包里一共提供了 …Java 从 JDK 1.5 开始提供了 java.util.concurrent.atomic 包以下简称Atomic包这个包中的 原子操作类 提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。
因为变量的类型有很多种所以在 Atomic 包里一共提供了 12个 类属于以下 4 种类型的原子更新方式
原子更新基本类型。
AtomicBoolean原子更新布尔类型。
AtomicInteger原子更新整型。
AtomicLong原子更新长整型。
原子更新数组。
AtomicIntegerArray原子更新整型数组里的元素。
AtomicLongArray原子更新长整型数组里的元素。
AtomicReferenceArray原子更新引用类型数组里的元素。
原子更新引用。
AtomicReference原子更新对象引用。
AtomicMarkableReference原子更新带有标记位的对象引用。
AtomicStampedReference原子更新带有版本号的对象引用。
原子更新属性字段。
AtomicIntegerFieldUpdater原子更新volatile修饰的整型的字段的更新器。
AtomicLongFieldUpdater原子更新volatile修饰的长整型字段的更新器。
AtomicReferenceFieldUpdater原子更新volatile修饰的引用类型里的字段的更新器。
Atomic 包里的类基本都是使用 Unsafe 实现的包装类。
原子更新基本类型
AtomicBoolean原子更新布尔类型。
AtomicInteger原子更新整型。
AtomicLong原子更新长整型。
以上3个类提供的方法几乎一模一样所以本节仅以 AtomicInteger 为例进行讲解。
AtomicInteger 的常用方法如下
int addAndGet(int delta)以原子方式将输入的数值与实例中的值AtomicInteger 里的 value相加并返回结果。
boolean compareAndSet(int expectint update)如果输入的数值等于预期值则以原子方式将该值设置为输入的值。
int getAndIncrement()以原子方式将当前值加1注意这里返回的是自增前的值。
void lazySet(int newValue)最终会设置成 newValue使用 lazySet 设置值后可导致其他线程在之后的一小段时间内还是可以读到旧的值。
int getAndSet(int newValue)以原子方式设置为 newValue 的值并返回旧值。
示例
public static void main(String[] args) {AtomicInteger ai new AtomicInteger(2);System.out.println(ai.get() ai.get());System.out.println(ai.addAndGet(5) ai.addAndGet(5));System.out.println(ai.get() ai.get());System.out.println(ai.compareAndSet(ai.get(), 10) ai.compareAndSet(ai.get(), 10));System.out.println(ai.get() ai.get());System.out.println(ai.getAndIncrement() ai.getAndIncrement());System.out.println(ai.get() ai.get());ai.lazySet(8);System.out.println(ai.lazySet(8));System.out.println(ai.get() ai.get());System.out.println(ai.getAndSet(5) ai.getAndSet(5));System.out.println(ai.get() ai.get());
}输出
ai.get() 2
ai.addAndGet(5) 7
ai.get() 7
ai.compareAndSet(ai.get(), 10) true
ai.get() 10
ai.getAndIncrement() 10
ai.get() 11
ai.lazySet(8)
ai.get() 8
ai.getAndSet(5) 8
ai.get() 5AtomicInteger 的 getAndIncrement()方法
public final int getAndIncrement() {for (; ; ) {int current get();int next current 1;if (compareAndSet(current, next)) {return current;}}
}
public final boolean compareAndSet(int expect, int update) {return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}for 循环体的先取得 AtomicInteger 里存储的数值
对 AtomicInteger 的当前数值进行 1 操作
关键是调用 compareAndSet 方法来进行原子更新操作该方法先检查 当前数值是否等于current 等于意味着 AtomicInteger 的值没有被其他线程修改过则将 AtomicInteger 的当前数值更新成 next的值。如果不等 compareAndSet 方法会返回 false程序会进入 for 循环重新进行 compareAndSet 操作。Atomic 包提供了 3 种基本类型的原子更新但是 Java 的基本类型里还有 char、float 和 double 等。
那么问题来了如何原子的更新其他的基本类型呢
Atomic包里的类基本都是使用 Unsafe 实现的让我们一起看一下Unsafe的源码
/*** 如果当前数值是expected则原子的将Java变量更新成x** return 如果更新成功则返回true*/
public final native boolean compareAndSwapObject(Object o, long offset,Object expected, Object x);public final native boolean compareAndSwapInt(Object o, long offset,int expected, int x);public final native boolean compareAndSwapLong(Object o, long offset,long expected, long x);综合上述代码我们可以发现 Unsafe 只提供了 3 种 CAS 方法compareAndSwapObject、compareAndSwapInt 和 compareAndSwapLong再看 AtomicBoolean 源码发现它是先把 Boolean 转换成 整型再使用 compareAndSwapInt 进行 CAS所以原子更新 char、float 和 double 变量也可以用类似的思路来实现。
原子更新数组
AtomicIntegerArray原子更新整型数组里的元素。
AtomicLongArray原子更新长整型数组里的元素。
AtomicReferenceArray原子更新引用类型数组里的元素。
以上几个类提供的方法几乎一样所以仅以 AtomicIntegerArray 为例进行介绍 AtomicIntegerArray 类主要是提供原子的方式更新数组里的整型。
常用方法如下 int addAndGet(int iint delta)以原子方式将输入值与数组中索引i的元素相加。boolean compareAndSet(int iint expectint update)如果当前值等于预期值则以原子方式将数组位置i的元素设置成update值。示例
public static void main(String[] args) {int[] value new int[]{1, 2};AtomicIntegerArray ai new AtomicIntegerArray(value);System.out.println(ai.getAndSet(0, 3));ai.getAndSet(0, 3);System.out.println(ai.get(0) ai.get(0));System.out.println(value[0] value[0]);ai.compareAndSet(1, 2, 5);System.out.println(ai.compareAndSet(1, 2, 5));System.out.println(ai.get(1) ai.get(1));
}输出
ai.getAndSet(0, 3)
ai.get(0) 3
value[0] 1
ai.compareAndSet(1,2,5)
ai.get(1) 5注意数组value通过构造方法传递进去然后AtomicIntegerArray会将当前数组复制一份所以当AtomicIntegerArray对内部的数组元素进行 修改 时不会影响传入的数组。
原子更新引用
AtomicReference原子更新对象引用。
AtomicMarkableReference原子更新带有标记位的对象引用。
AtomicStampedReference原子更新带有版本号的对象引用。该类将整数值与引用关联起来可用于原子的更新数据和数据的版本号可以解决使用 CAS 进行原子更新时可能出现的 ABA问题。
我们以AtomicReference为例进行介绍。
示例
public class AtomicReferenceTest {public static AtomicReferenceUser atomicUserRef newAtomicReferenceUser();public static void main(String[] args) {User user new User(103style, 20);atomicUserRef.set(user);System.out.println(atomicUserRef.get() atomicUserRef.get().toString());User updateUser new User(xiaoke, 22);atomicUserRef.compareAndSet(user, updateUser);System.out.println(atomicUserRef.compareAndSet(user, updateUser););System.out.println(atomicUserRef.get() atomicUserRef.get().toString());}static class User {private String name;private int age;public User(String name, int age) {this.name name;this.age age;}public String getName() {return name;}public int getAge() {return age;}Overridepublic String toString() {return name name , age age;}}
}输出
atomicUserRef.get() name103style, age20
atomicUserRef.compareAndSet(user, updateUser);
atomicUserRef.get() namexiaoke, age22原子更新属性字段
AtomicIntegerFieldUpdater原子更新volatile修饰的整型的字段的更新器。
AtomicLongFieldUpdater原子更新volatile修饰的长整型字段的更新器。
AtomicReferenceFieldUpdater原子更新volatile修饰的引用类型里的字段的更新器。
要想原子地更新字段类需要两步 因为原子更新字段类都是抽象类每次使用的时候必须使用静态方法newUpdater()创建一个更新器并且需要设置想要更新的类和属性。更新类的字段属性必须使用public volatile修饰符。我们以AstomicIntegerFieldUpdater 为例进行讲解。
示例
public class AtomicIntegerFieldUpdaterTest {public static void main(String[] args) {// 创建原子更新器,并设置需要更新的对象类和对象的属性AtomicIntegerFieldUpdaterUser a AtomicIntegerFieldUpdater.newUpdater(User.class, age);// 设置柯南的年龄是10岁User conan new User(conan, 10);// 柯南长了一岁,但是仍然会输出旧的年龄System.out.println(a.getAndIncrement(conan));// 输出柯南现在的年龄System.out.println(a.get(conan));}public static class User {public volatile int age;private String name;public User(String name, int age) {this.name name;this.age age;}public String getName() {return name;}public int getAge() {return age;}}
}输出
10
11了解更多欢迎点赞关注的哟