zoukankan      html  css  js  c++  java
  • AtomicIntegerFieldUpdater可以保证对象属性的原子性

    AtomicIntegerFieldUpdater测试类

    package com.dwz.atomicApi;
    
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    public class AtomicIntegerFieldUpdaterTest {
        public static void main(String[] args) {
            AtomicIntegerFieldUpdater<TestMe> updater = AtomicIntegerFieldUpdater.newUpdater(TestMe.class, "i");
            TestMe me = new TestMe();
            
            for(int i = 0; i < 2; i++) {
                new Thread() {
                    public void run() {
                        for(int i = 0; i < 20; i++) {
                            int v = updater.getAndIncrement(me);
                            System.out.println(Thread.currentThread().getName() + "=>" + v);
                        }
                    }
                }.start();
            }
        }
        
        static class TestMe {
            volatile int i;
        }
    }

    测试结果正常

    有几种情况会导致AtomicIntegerFieldUpdater失败

    情况一:不能访问同包类中的私有变量

    目标类

    package concurrency;
    
    public class TestMe {
        private volatile int i;
    }

    AtomicIntegerFieldUpdater测试类

    package concurrency;
    
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    import org.junit.Test;
    
    public class FailedAtomicIntegerFieldUpdaterTest {
        
        @Test(expected = RuntimeException.class)
        public void testPrivateFieldAccessError() {
            AtomicIntegerFieldUpdater<TestMe> updater = AtomicIntegerFieldUpdater.newUpdater(TestMe.class, "i");
            TestMe me = new TestMe();
            updater.compareAndSet(me, 0, 1);
        }
    }

    不加@Test(expected = RuntimeException.class)会出错

    java.lang.RuntimeException: java.lang.IllegalAccessException: Class concurrency.FailedAtomicIntegerFieldUpdaterTest can not access a member of class concurrency.TestMe with modifiers "private volatile"
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:405)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdater.java:88)
        at concurrency.FailedAtomicIntegerFieldUpdaterTest.testPrivateFieldAccessError(FailedAtomicIntegerFieldUpdaterTest.java:11)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
    Caused by: java.lang.IllegalAccessException: Class concurrency.FailedAtomicIntegerFieldUpdaterTest can not access a member of class concurrency.TestMe with modifiers "private volatile"
        at sun.reflect.Reflection.ensureMemberAccess(Reflection.java:102)
        at sun.reflect.misc.ReflectUtil.ensureMemberAccess(ReflectUtil.java:103)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:394)
        ... 25 more

    意思是不能访问同包类中的私有变量

    情况二:访问的对象不能是null

    目标类

    package concurrency;
    
    public class TestMe {
        volatile int i;
    }

    测试类

    package concurrency;
    
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    import org.junit.Test;
    
    public class FailedAtomicIntegerFieldUpdaterTest {
        @Test
        public void testTargetObjectIsNull() {
            AtomicIntegerFieldUpdater<TestMe> updater = AtomicIntegerFieldUpdater.newUpdater(TestMe.class, "i");
            updater.compareAndSet(null, 0, 1);
        }
    }

    测试结果:

    java.lang.ClassCastException
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.throwAccessCheckException(AtomicIntegerFieldUpdater.java:475)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.accessCheck(AtomicIntegerFieldUpdater.java:466)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.compareAndSet(AtomicIntegerFieldUpdater.java:488)
        at concurrency.FailedAtomicIntegerFieldUpdaterTest.testTargetObjectIsNull(FailedAtomicIntegerFieldUpdaterTest.java:19)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)

    情况三:目标对象的字段名称错误

    目标类

    package concurrency;
    
    public class TestMe {
        volatile int i;
    }

    测试类

    package concurrency;
    
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    
    import org.junit.Test;
    
    public class FailedAtomicIntegerFieldUpdaterTest {
        @Test
        public void testFieldNameInvalid() {
            AtomicIntegerFieldUpdater<TestMe> updater = AtomicIntegerFieldUpdater.newUpdater(TestMe.class, "i1");
            TestMe me = new TestMe();
            updater.compareAndSet(me, 0, 1);
        }
    }

    测试结果

    java.lang.RuntimeException: java.lang.NoSuchFieldException: i1
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:403)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater.newUpdater(AtomicIntegerFieldUpdater.java:88)
        at concurrency.FailedAtomicIntegerFieldUpdaterTest.testFieldNameInvalid(FailedAtomicIntegerFieldUpdaterTest.java:24)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:498)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:89)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:41)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:541)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:763)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:463)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:209)
    Caused by: java.lang.NoSuchFieldException: i1
        at java.lang.Class.getDeclaredField(Class.java:2070)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl$1.run(AtomicIntegerFieldUpdater.java:390)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl$1.run(AtomicIntegerFieldUpdater.java:388)
        at java.security.AccessController.doPrivileged(Native Method)
        at java.util.concurrent.atomic.AtomicIntegerFieldUpdater$AtomicIntegerFieldUpdaterImpl.<init>(AtomicIntegerFieldUpdater.java:387)
        ... 25 more

     情况四:目标对象的字段类型调用错误

    测试类

    package concurrency;
    
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
    
    import org.junit.Test;
    
    public class FailedAtomicIntegerFieldUpdaterTest {
        @Test
        public void testFieldTypeInvalid() {
            AtomicReferenceFieldUpdater<TestMe2, Long> updater = AtomicReferenceFieldUpdater.newUpdater(TestMe2.class, Long.class, "i");
            TestMe2 me = new TestMe2();
            updater.compareAndSet(me, 0L, 1L);
        }
        
        static class TestMe2 {
            volatile Integer i;
        }
    }

    测试结果

    java.lang.ClassCastException

    情况五:目标对象字段没有用volatile修饰

    package concurrency;
    
    import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
    import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
    
    import org.junit.Test;
    
    public class FailedAtomicIntegerFieldUpdaterTest {
        static class TestMe2 {
            Integer i;
        }
        
        @Test
        public void testFieldIsNotVolatile() {
            AtomicReferenceFieldUpdater<TestMe2, Integer> updater = AtomicReferenceFieldUpdater.newUpdater(TestMe2.class, Integer.class, "i");
            TestMe2 me = new TestMe2();
            updater.compareAndSet(me, 0, 1);
        }
    }

    测试结果:

    java.lang.IllegalArgumentException: Must be volatile type

     总结:

    使用AtomicXXXFieldUpdater的原因

    a.想让类的属性操作具有原子性:
    1.必须是volatile修饰
    2.非private,protected(如果是当前类也可以)
    3.类型必须一致
    b.不想使用锁(包括显示锁和重量级锁synchronized)
    c.大量需要原子类型修饰的对象,相对耗费内存,AtomicXXXFieldUpdater至少可以省一半内存

  • 相关阅读:
    win 下 docker 环境配置
    【译】PHP 内核 — 字符串管理
    Elasticsearch和Lucene的关系
    如何写出高性的SQL语句?
    Java 8
    Cause: java.sql.SQLSyntaxErrorException: ORA-01719: OR 或 IN 操作数中不允许外部联接运算符 (+)
    sql 使用 in 后数据量太大报错
    java8-求最小值(8中方法)
    Oracle 强制索引
    为什么新安装eclipse idea等环境,初次运行java程序,会弹出windows防火墙信息
  • 原文地址:https://www.cnblogs.com/zheaven/p/13157887.html
Copyright © 2011-2022 走看看