JDK 原子操作类详解(AtomicInteger、AtomicIntegerArray等)

本文详细介绍了Java中多线程编程的问题及解决方案,包括使用synchronized关键字、Atomic包中的原子类等方法来确保线程安全。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可能i不等于3,而是等于2。因为A和B线程在更新变量i的时候拿到的i都是1,这就是线程不安全的更新操作,通常我们会使用synchronized来解决这个问题,synchronized会保证多线程不会同时更新变量i。

[java]  view plain  copy
  1. import java.util.concurrent.CountDownLatch;  
  2.   
  3. public class UnSafeAdd {  
  4.     private static int threadCount=10;  
  5.     private static CountDownLatch countDown=new CountDownLatch(threadCount);  
  6.     private static int count=0;  
  7.     private static class Counter implements Runnable{   
  8.         @Override  
  9.         public void run() {  
  10.             for(int i=0;i<1000;i++){  
  11.                 count++;//非原子操作  
  12.             }   
  13.             countDown.countDown();  
  14.         }   
  15.     }  
  16.     public static void main(String[] args) throws InterruptedException {  
  17.         Thread[] threads=new Thread[threadCount];  
  18.         for(int i=0;i<threadCount;i++){  
  19.             threads[i]=new Thread(new Counter());  
  20.         }  
  21.         for(int i=0;i<threadCount;i++){  
  22.             threads[i].start();;  
  23.         }  
  24.         countDown.await();  
  25.         System.out.println(count);  
  26.     }  
输出:

8968

[java]  view plain  copy
  1. import java.util.concurrent.CountDownLatch;  
  2.   
  3.   
  4. public class SafeAddWithSyn {  
  5.     private static int threadCount=10;  
  6.     private static CountDownLatch countDown=new CountDownLatch(threadCount);  
  7.     private static int count=0;  
  8.     synchronized private static void addCount(){//同步方法  
  9.         count++;  
  10.     }  
  11.     private static class Counter implements Runnable{   
  12.         @Override  
  13.         public void run() {  
  14.             for(int i=0;i<1000;i++){  
  15.                 addCount();  
  16.             }   
  17.             countDown.countDown();  
  18.         }   
  19.     }  
  20.     public static void main(String[] args) throws InterruptedException {  
  21.         Thread[] threads=new Thread[threadCount];  
  22.         for(int i=0;i<threadCount;i++){  
  23.             threads[i]=new Thread(new Counter());  
  24.         }  
  25.         for(int i=0;i<threadCount;i++){  
  26.             threads[i].start();;  
  27.         }  
  28.         countDown.await();  
  29.         System.out.println(count);  
  30.     }  
  31. }  
输出:

10000

而Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。Atomic包里的类基本都是使用Unsafe实现的包装类。

[java]  view plain  copy
  1. import java.util.concurrent.CountDownLatch;  
  2. import java.util.concurrent.atomic.AtomicInteger;  
  3.    
  4. public class SafeAddWithAtomicInteger {  
  5.     private static int threadCount=10;  
  6.     private static CountDownLatch countDown=new CountDownLatch(threadCount);  
  7.     private static AtomicInteger count=new AtomicInteger(0);//原子操作类  
  8.     private static class Counter implements Runnable{   
  9.         @Override  
  10.         public void run() {  
  11.             for(int i=0;i<1000;i++){  
  12.                 count.addAndGet(1);  
  13.             }   
  14.             countDown.countDown();  
  15.         }   
  16.     }  
  17.     public static void main(String[] args) throws InterruptedException {  
  18.         Thread[] threads=new Thread[threadCount];  
  19.         for(int i=0;i<threadCount;i++){  
  20.             threads[i]=new Thread(new Counter());  
  21.         }  
  22.         for(int i=0;i<threadCount;i++){  
  23.             threads[i].start();;  
  24.         }  
  25.         countDown.await();  
  26.         System.out.println(count.get());  
  27.     }  
  28. }  
输出:

10000

原子更新基本类型

使用原子的方式更新基本类型,Atomic包提供了以下3个类。
AtomicBoolean:原子更新布尔类型。
AtomicInteger:原子更新整型。
AtomicLong:原子更新长整型。
以上3个类提供的方法几乎一模一样,所以本节仅以AtomicInteger为例进行讲解,AtomicInteger的源码如下:

[java]  view plain  copy
  1. public class AtomicInteger extends Number implements java.io.Serializable {  
  2.     private static final long serialVersionUID = 6214790243416807050L;  
  3.   
  4.     // setup to use Unsafe.compareAndSwapInt for updates 使用Unsafe类的CAS操作实现更新  
  5.     private static final Unsafe unsafe = Unsafe.getUnsafe();  
  6.     private static final long valueOffset;  
  7.     ......  
  8.     //volatile保证可见性  
  9.     private volatile int value;  
  10.     //构造器  
  11.     public AtomicInteger(int initialValue) {  
  12.         value = initialValue;  
  13.     }  
  14.     public AtomicInteger() {  
  15.     }  
  16.     ......  
  17.   
  18.     //CAS更新  
  19.     public final boolean compareAndSet(int expect, int update) {  
  20.         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);  
  21.     }  
  22.     ......  
  23.     /*以下方法都使用循环CAS更新数据*/  
  24. <pre name="code" class="java">    public final int getAndSet(int newValue) {//以原子方式设置新值  
  25.         for (;;) {  
  26.             int current = get();  
  27.             if (compareAndSet(current, newValue))  
  28.                 return current;  
  29.         }  
  30.     }  
 public final int getAndIncrement() {//以原子方式自增 for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } } ......
//以原子方式将输入值与当前值相加并返回结果 public final int addAndGet(int delta) { for (;;) {//循环 int current = get(); int next = current + delta; if (compareAndSet(current, next))//CAS return next; } } ......}
 

原子更新数组

通过原子的方式更新数组里的某个元素,Atomic包提供了以3类
AtomicIntegerArray:原子更新整型数组里的元素。
AtomicLongArray:原子更新长整型数组里的元素。
AtomicReferenceArray:原子更新引用类型数组里的元素。

[java]  view plain  copy
  1. import java.util.concurrent.CountDownLatch;  
  2.   
  3. public class AtomicIntegerArrayTest {  
  4.     private static int threadCount=1000;  
  5.     private static CountDownLatch countDown=new CountDownLatch(threadCount);  
  6.     static int[] values=new int[10];  
  7.     private static class Counter implements Runnable{   
  8.         @Override  
  9.         public void run() {   
  10.              for(int i=0;i<100;i++){  
  11.                  for(int j=0;j<10;j++){//所有元素+1  
  12.                      values[j]++;  
  13.                  }  
  14.              }  
  15.              countDown.countDown();  
  16.         }   
  17.     }   
  18.     public static void main(String[] args) throws InterruptedException{  
  19.         Thread[] threads=new Thread[threadCount];  
  20.         for(int i=0;i<threadCount;i++){  
  21.             threads[i]=new Thread(new Counter());  
  22.         }  
  23.         for(int i=0;i<threadCount;i++){  
  24.             threads[i].start();;  
  25.         }  
  26.         countDown.await();  
  27.         for(int i=0;i<10;i++){  
  28.             System.out.print(values[i]+" ");  
  29.         }  
  30.     }  
  31. }  
输出:

99997 99996 99997 99997 99996 99996 99996 99996 99996 99996 

[java]  view plain  copy
  1. import java.util.concurrent.CountDownLatch;  
  2. import java.util.concurrent.atomic.AtomicIntegerArray;  
  3.   
  4. public class AtomicIntegerArrayTest {  
  5.     private static int threadCount=1000;  
  6.     private static CountDownLatch countDown=new CountDownLatch(threadCount);  
  7.     static int[] values=new int[10];  
  8.     static AtomicIntegerArray ai=new AtomicIntegerArray(values);  
  9.     private static class Counter implements Runnable{   
  10.         @Override  
  11.         public void run() {   
  12.              for(int i=0;i<100;i++){  
  13.                  for(int j=0;j<10;j++){//所有元素+1  
  14.                      ai.getAndIncrement(j);   
  15.                  }  
  16.              }  
  17.              countDown.countDown();  
  18.         }   
  19.     }   
  20.     public static void main(String[] args) throws InterruptedException{  
  21.         Thread[] threads=new Thread[threadCount];  
  22.         for(int i=0;i<threadCount;i++){  
  23.             threads[i]=new Thread(new Counter());  
  24.         }  
  25.         for(int i=0;i<threadCount;i++){  
  26.             threads[i].start();;  
  27.         }  
  28.         countDown.await();  
  29.         for(int i=0;i<10;i++){  
  30.             System.out.print(ai.get(i)+" ");  
  31.         }  
  32.         System.out.println();  
  33.         for(int i=0;i<10;i++){  
  34.             System.out.print(values[i]+" ");  
  35.         }  
  36.     }  
  37. }  
输出:

100000 100000 100000 100000 100000 100000 100000 100000 100000 100000 
0 0 0 0 0 0 0 0 0 0 
需要注意的是,数组value通过构造方法传递进去,然后AtomicIntegerArray会将当前数组复制一份,所以当AtomicIntegerArray对内部的数组元素进行修改时,不会影响传入的数组。

[java]  view plain  copy
  1. //An {@code int} array in which elements may be updated atomically.  
  2. public class AtomicIntegerArray implements java.io.Serializable {  
  3.     ......  
  4.     private final int[] array;//存储数据的int数组  
  5.   
  6.     ......  
  7.     //构造器  
  8.     public AtomicIntegerArray(int length) {  
  9.         array = new int[length];  
  10.     }  
  11.     public AtomicIntegerArray(int[] array) {  
  12.         // Visibility guaranteed by final field guarantees  
  13.         this.array = array.clone();//这里会复制传入的int数组,因此不会改变原来的int数组  
  14.     }  
  15.   
  16.     public final int length() {  
  17.         return array.length;  
  18.     }  
  19.     ......  
  20. }  

原子更新引用

原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下3个类。
AtomicReference:原子更新引用类型。
AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
AtomicMarkableReference:原子更新带有标记位的引用类型。

[java]  view plain  copy
  1. import java.util.concurrent.CountDownLatch;  
  2. import java.util.concurrent.atomic.AtomicReference;  
  3.   
  4. public class Test {   
  5.     private static int threadCount=10;  
  6.     private static CountDownLatch countDown=new CountDownLatch(threadCount);  
  7.     public static AtomicReference<User> atomicUserRef = new AtomicReference<User>();  
  8.     private static class ReferenceUpdater implements Runnable{   
  9.         User user;  
  10.         public ReferenceUpdater(User user){  
  11.             this.user=user;  
  12.         }  
  13.         @Override  
  14.         public void run() {  
  15.             for(int i=0;i<1000;i++){   
  16.                 User oldUser=atomicUserRef.get();   
  17.                 atomicUserRef.compareAndSet(oldUser, user);  
  18.                 Thread.yield();  
  19.             }   
  20.             countDown.countDown();  
  21.         }   
  22.     }  
  23.     public static void main(String[] args) throws InterruptedException {  
  24.         Thread[] threads=new Thread[threadCount];  
  25.         for(int i=0;i<threadCount;i++){  
  26.             threads[i]=new Thread(new ReferenceUpdater(new User("name"+i,i)));  
  27.         }  
  28.         for(int i=0;i<threadCount;i++){  
  29.             threads[i].start();;  
  30.         }  
  31.         countDown.await();   
  32.         System.out.println(atomicUserRef.get().getName());  
  33.         System.out.println(atomicUserRef.get().getOld());  
  34.     }  
  35.   
  36.     static class User {  
  37.         private String name;  
  38.         private int old;  
  39.   
  40.         public User(String name, int old) {  
  41.             this.name = name;  
  42.             this.old = old;  
  43.         }  
  44.   
  45.         public String getName() {  
  46.             return name;  
  47.         }  
  48.   
  49.         public int getOld() {  
  50.             return old;  
  51.         }  
  52.     }  
  53. }  

输出:

name1
1
每次输出结果都不确定,10种情况都有可能,但是name属性和age属性是匹配的。

[java]  view plain  copy
  1. /**An object reference that may be updated atomically.*/  
  2. public class AtomicReference<V>  implements java.io.Serializable {  
  3. <pre name="code" class="java">    ......  
private static final Unsafe unsafe = Unsafe.getUnsafe();//用于CAS更新 ...... private volatile V value;//引用,volatile保证可见性 public AtomicReference(V initialValue) { value = initialValue; } public AtomicReference() { } //利用Unsafe类执行CAS操作 public final boolean compareAndSet(V expect, V update) { return unsafe.compareAndSwapObject(this, valueOffset, expect, update); } ......
//循环CAS public final V getAndSet(V newValue) { while (true) { V x = get(); if (compareAndSet(x, newValue)) return x; } }
......}
 

原子更新字段

如果需原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新。
AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
AtomicLongFieldUpdater:原子更新长整型字段的更新器。
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

要想原子地更新字段类需要两步。第一步,因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。第二步,更新类的字段(属性)必须使用public volatile修饰符。

[java]  view plain  copy
  1. import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;  
  2.    
  3. public class AtomicIntegerFieldUpdaterTest {   
  4.     // 创建原子更新器,并设置需要更新的对象类和对象的属性  
  5.     private static AtomicIntegerFieldUpdater<User> a =   
  6.             AtomicIntegerFieldUpdater.newUpdater(User.class"old");  
  7.    
  8.     public static void main(String[] args) throws InterruptedException {    
  9.         // 设置柯南的年龄是10岁  
  10.         User conan = new User("conan"10);  
  11.         // 柯南长了一岁,但是仍然会输出旧的年龄  
  12.         System.out.println(a.getAndIncrement(conan));  
  13.         // 输出柯南现在的年龄  
  14.         System.out.println(a.get(conan));  
  15.     }  
  16.   
  17.     public static class User {  
  18.         private String name;  
  19.         public volatile int old;  
  20.   
  21.         public User(String name, int old) {  
  22.             this.name = name;  
  23.             this.old = old;  
  24.         }  
  25.   
  26.         public String getName() {  
  27.             return name;  
  28.         }  
  29.   
  30.         public int getOld() {  
  31.             return old;  
  32.         }  
  33.     }  
  34. }  
输出:

10

11

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值