Atomic原子操作
在java.util.concurrent.atomic包里提供了一组原子操作类:
基本类型:AtomicInteger、AtomicLong、AtomicBoolean;
引用类型:AtomicReference、AtomicStampedRerence、AtomicMarkableReference;
数组类型:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
对象属性原子修改器:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
原子类型累加器(jdk1.8增加的类):DoubleAccumulator、DoubleAdder、LongAccumulator、LongAdder、Striped64
以AtomicInteger为例总结常用的方法 //以原子的方式将实例中的原值加1,返回的是自增前的旧值; public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } //getAndSet(int newValue):将实例中的值更新为新值,并返回旧值; public final boolean getAndSet(boolean newValue) { boolean prev; do { prev = get(); } while (!compareAndSet(prev, newValue)); return prev; } //incrementAndGet() :以原子的方式将实例中的原值进行加1操作,并返回最终相加后的结果; public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; } //addAndGet(int delta) :以原子方式将输入的数值与实例中原本的值相加,并返回最后的结果; public final int addAndGet(int delta) { return unsafe.getAndAddInt(this, valueOffset, delta) + delta;
测试 public class AtomicIntegerTest { static AtomicInteger sum = new AtomicInteger(0); public static void main(String[] args) { for (int i = 0; i < 10; i++) { Thread thread = new Thread(() -> { for (int j = 0; j < 10000; j++) { // 原子自增 CAS sum.incrementAndGet(); //TODO } }); thread.start(); } try { Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(sum.get()); }
AtomicIntegerArray为例总结常用的方法 //addAndGet(int i, int delta):以原子更新的方式将数组中索引为i的元素与输入值相加; public final int addAndGet(int i, int delta) { return getAndAdd(i, delta) + delta; } //getAndIncrement(int i):以原子更新的方式将数组中索引为i的元素自增加1; public final int getAndIncrement(int i) { return getAndAdd(i, 1); } //compareAndSet(int i, int expect, int update):将数组中索引为i的位置的元素进行更新 public final boolean compareAndSet(int i, int expect, int update) { return compareAndSetRaw(checkedByteOffset(i), expect, update);
测试 public class AtomicIntegerArrayTest { static int[] value = new int[]{ 1, 2, 3, 4, 5 }; static AtomicIntegerArray atomicIntegerArray = new AtomicIntegerArray(value); public static void main(String[] args) throws InterruptedException { //设置索引0的元素为100 atomicIntegerArray.set(0, 100); System.out.println(atomicIntegerArray.get(0)); //以原子更新的方式将数组中索引为1的元素与输入值相加 atomicIntegerArray.getAndAdd(1,5); System.out.println(atomicIntegerArray); }
AtomicReference作用是对普通对象的封装,它可以保证你在修改对象引用时的线程安全性。 public class AtomicReferenceTest { public static void main( String[] args ) { User user1 = new User("张三", 23); User user2 = new User("李四", 25); User user3 = new User("王五", 20); //初始化为 user1 AtomicReference<User> atomicReference = new AtomicReference<>(); atomicReference.set(user1); //把 user2 赋给 atomicReference atomicReference.compareAndSet(user1, user2); System.out.println(atomicReference.get()); //把 user3 赋给 atomicReference atomicReference.compareAndSet(user1, user3); System.out.println(atomicReference.get()); } } @Data @AllArgsConstructor class User { private String name; private Integer age;
对象属性原子修改器 AtomicIntegerFieldUpdater可以线程安全地更新对象中的整型变量。 public class AtomicIntegerFieldUpdaterTest { public static class Candidate { volatile int score = 0; AtomicInteger score2 = new AtomicInteger(); } public static final AtomicIntegerFieldUpdater<Candidate> scoreUpdater = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score"); public static AtomicInteger realScore = new AtomicInteger(0); public static void main(String[] args) throws InterruptedException { final Candidate candidate = new Candidate(); Thread[] t = new Thread[10000]; for (int i = 0; i < 10000; i++) { t[i] = new Thread(new Runnable() { @Override public void run() { if (Math.random() > 0.4) { candidate.score2.incrementAndGet(); scoreUpdater.incrementAndGet(candidate); realScore.incrementAndGet(); } } }); t[i].start(); } for (int i = 0; i < 10000; i++) { t[i].join(); } System.out.println("AtomicIntegerFieldUpdater Score=" + candidate.score); System.out.println("AtomicInteger Score=" + candidate.score2.get()); System.out.println("realScore=" + realScore.get()); } 对于AtomicIntegerFieldUpdater 的使用稍微有一些限制和约束,约束如下: (1)字段必须是volatile类型的,在线程之间共享变量时保证立即可见.eg:volatile int value = 3 (2)字段的描述类型(修饰符public/protected/default/private)与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不
能直接操作的,尽管子类可以访问父类的字段。 (3)只能是实例变量,不能是类变量,也就是说不能加static关键字。 (4)只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。 (5)对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。如果要修改包装类型就需要使用AtomicReferenceFieldUpdate
r。
LongAdder引入的初衷——解决高并发环境下AtomicInteger,AtomicLong的自旋瓶颈问题。 性能测试 public class LongAdderTest { public static void main(String[] args) { testAtomicLongVSLongAdder(10, 10000); System.out.println("=================="); testAtomicLongVSLongAdder(10, 200000); System.out.println("=================="); testAtomicLongVSLongAdder(100, 200000); } static void testAtomicLongVSLongAdder(final int threadCount, final int times) { try { long start = System.currentTimeMillis(); testLongAdder(threadCount, times); long end = System.currentTimeMillis() - start; System.out.println("条件>>>>>>线程数:" + threadCount + ", 单线程操作计数" + times); System.out.println("结果>>>>>>LongAdder方式增加计数" + (threadCount * times) + "次,共计耗时:" + end); long start2 = System.currentTimeMillis(); testAtomicLong(threadCount, times); long end2 = System.currentTimeMillis() - start2; System.out.println("条件>>>>>>线程数:" + threadCount + ", 单线程操作计数" + times); System.out.println("结果>>>>>>AtomicLong方式增加计数" + (threadCount * times) + "次,共计耗时:" + end2); } catch (InterruptedException e) { e.printStackTrace(); } } static void testAtomicLong(final int threadCount, final int times) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(threadCount); AtomicLong atomicLong = new AtomicLong(); for (int i = 0; i < threadCount; i++) { new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < times; j++) { atomicLong.incrementAndGet(); } countDownLatch.countDown(); } }, "my-thread" + i).start(); } countDownLatch.await(); } static void testLongAdder(final int threadCount, final int times) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(threadCount); LongAdder longAdder = new LongAdder(); for (int i = 0; i < threadCount; i++) { new Thread(new Runnable() { @Override public void run() { for (int j = 0; j < times; j++) { longAdder.add(1); } countDownLatch.countDown(); } }, "my-thread" + i).start(); } countDownLatch.await(); }
测试结果:线程数越多,并发操作数越大,LongAdder的优势越明显


浙公网安备 33010602011771号