beaninfo详解源码解析 java_Java并发包专题:AtomicIntegerFieldUpdater源码解析

前两篇文章我介绍了AtomicInteger和AtomicIntegerArray,现在总结一下两个类的特点。

1:AtomicInteger是对单个变量进行原子操作的。2:AtomicIntegerArray是对数组中的元素进行原子操作的,而不是数组本身。

本篇文章介绍第三类原子操作:AtomicIntegerFieldUpdater.

1:关于private/protected/public访问控制符2:AtomicIntegerFieldUpdater的一些限制3:AtomicIntegerFieldUpdater实例4:AtomicIntegerFieldUpdater源码解析

一、关于private/protected/public访问控制符

由于下面要用到Java这个知识点,所以我们在复习一下关于Java的访问控制符,在Java中主要定义三个访问控制符,还有一个默认的(没有被任何访问控制符修饰default),一共四个访问控制符

1:private:私有的,只能自己能访问,其他都访问不到2:默认的(default,如果没有被任何访问控制符修饰):同一类或者同一个包中能够访问到,其他访问不到。3:protected:同一个类、子类、同一个包能放到,其他访问不到。4:public:所有类都能访问到。

他们的访问范围如图:

91887b728c7626ba46f5d9d08355c2a2.png

通过一个表格更加清晰的看一下:

8c7b0ce306b2d0c361435a64e1e76890.png

这个知识点大家都非常熟悉了,理解了访问控制符,AtomicIntegerFieldUpdater的一些知识点就好理解了。

二、AtomicIntegerFieldUpdater的一些限制

接下来的文章内容如果没有特殊说明,AtomicIntegerFieldUpdater更新的字段简称为被操作的字段。

1:被操作的字段必须被volatile修饰2:被操作的字段必须是int类型,不能是包装类型,如Integer3:被操作的字段只能是实例变量,不能是类变量,就是不能被static修饰4:被操作的字段只能是可变变量,不能被final修饰,其实final和volatile不能同时存在,否则编译不通过。5:如果调用方无法直接访问到被操作字段,则会抛出异常

关于第5点就是用到了上面所说的访问控制权限:

1:如果调用方和被操作字段所在的类是同一类,那么被操作字段可以被private/defalut/protected/public修饰。2:如果调用方和被操作字段所在的类是同一个包,那么被操作字段可以被defalut/protected/public修饰3:如果调用方和被操作字段所在的类属于子类,那么被操作字段可以被protected/public修饰4:如果调用方和被操作字段所在的类不在同一个包,也不属于子类关系,那么只能被public修饰。

三、AtomicIntegerFieldUpdater实例

上面提出了它的限制后,下面我们通过实例去一个一个的证明:

1:首先证明AtomicIntegerFieldUpdater可以原子更新一个对象中被volatile修饰的int类型的字段

下面的实例调用对象AtomicIntegerFieldUpdater和被操作字段所在的类在同一个包中

调用对象:AtomicIntegerFieldUpdater

package com.stqsht.juc.atomic;public class AtomicIntegerFieldUpdaterTest {//定义一个线程池 private static ThreadPoolExecutor pool; static { pool = new ThreadPoolExecutor(5, 10, 1, TimeUnit.SECONDS, new ArrayBlockingQueue<>(1000)); } public static void main(String[] args) throws InterruptedException { Obj obj = new Obj(0, 0); AtomicIntegerFieldUpdater afu = AtomicIntegerFieldUpdater.newUpdater(Obj.class, "age"); for (int i = 0; i < 5; i++) { pool.execute(() -> { for (int j = 0; j < 10000; j++) { obj.setId(obj.getId() + 1); afu.incrementAndGet(obj); } }); } pool.awaitTermination(5, TimeUnit.SECONDS); System.out.println("id=" + obj.getId()); System.out.println("age=" + obj.getAge()); pool.shutdown(); }}

被操作字段所在的类Obj中有两个字段,一个字段没有被volatile修饰,一个字段被volatile修饰

package com.stqsht.juc.atomic;class Obj { private int id; volatile int age; public Obj(int id, int age) { this.id = id; this.age = age; }//省略getter和setter方法}

通过运行多次后,得出一个结论,每一次id的结果都不相同,而age得到的结果相同且是正确的,运行结果如下:

287b5ae6aa49c8877efe6b84e7399511.png
70e0221a6e2ed891d086308f464fbe8b.png

从上述案例可以证明,AtomicIntegerFieldUpdater可以原子的更新一个对象中的字段。

2:如果被操作的字段没有关键字volatile,则会抛出异常:Must be volatile type

class Obj { private int id; int age; public Obj(int id, int age) { this.id = id; this.age = age; }//setter和getter省略}

上面的被修饰的字段age并没有关键字volatile,运行代码结果如下:

2610c96ff49cf47c81fc6c27464e976e.png

从运行结果可以看出,如果被操作的字段没有被volatile修饰,那么它直接抛出异常,因为多线程下volatile具有可见性和有序性。

3:被操作的字段只能是int类型,不能是包装类型Integer

class Obj { private int id; volatile Integer age; public Obj(int id, int age) { this.id = id; this.age = age; }//setter和getter省略}

上面的被操作字段是一个包装类Integer,运行结果如下图:

3069352e9500f3e30940f92a9842e961.png

4:被操作的字段只能是实例变量,不能是类变量,也就是被操作字段不能被static修饰

class Obj { private int id; static volatile int age; public Obj(int id, int age) { this.id = id; this.age = age; }//setter和getter省略}

运行结果如下图:

7a16b75fee0581e3b91dfa46664f3e86.png

5:被操作的字段只能是可变变量,不能被final修饰,其实final和volatile不能同时存在,否则编译不通过。

55868c80d9f13220fac4a534bc454481.png

6:如果调用方无法直接访问到被操作字段,则会抛出异常

6.1:如果调用方和被操作字段所在的类是同一个类,被操作字段可以被private/defalut/protected/public修饰

public class User { public static void main(String[] args) { AtomicIntegerFieldUpdaterai = AtomicIntegerFieldUpdater.newUpdater(User.class,"age"); User user = new User(0); ai.incrementAndGet(user); System.out.println(user.getAge()); }//可以被private/defalut/protected/public修饰 private volatile int age; public User(int age) { this.age = age; }//省略setter和gettter方法}

大家可以亲自测试以下上面的demo,无论成员变量age被什么访问控制符修饰,都是可以的。

6.2:如果调用方和被操作字段所在的类在同一个包中,被操作字段可以被defalut/protected/public修饰,用private则会抛出异常

调用方:Caller

package com.stqsht.juc.other;import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;//调用方public class Caller { public static void main(String[] args) { User user = new User(0); AtomicIntegerFieldUpdater ai = AtomicIntegerFieldUpdater.newUpdater(User.class,"age"); ai.incrementAndGet(user); System.out.println(user.getAge()); }}

被操作字段所在的类:User

package com.stqsht.juc.other;//调用方public class User {//可以被default/protected/public修饰 volatile int age; public User(int age) { this.age = age; } //省略setter和getter方法}

如果上面的例子如果被private修饰,则会抛出如图的异常:

6dcdbf5c8048d3489ccb5e8b73dcbed8.png

6.3:如果调用方和被操作字段所在的类属于子类关系,被操作字段可以被protected/public修饰

调用方:com.stqsht.juc.other.Caller

package com.stqsht.juc.other;import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;import com.stqsht.juc.common.User;//调用方public class Caller extends User { public static void main(String[] args) { Caller user = new Caller(0); AtomicIntegerFieldUpdater ai = AtomicIntegerFieldUpdater.newUpdater(User.class,"age"); ai.incrementAndGet(user); System.out.println(user.getAge()); } public Caller(int age) { super(age); }}

被操作字段所在的类:com.stqsht.juc.common.User

package com.stqsht.juc.common;//被操作字段所在的类,可以被protect/public修饰public class User { volatile int age; public User(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }}

如果上面的例子被private/defalut修饰,则会抛出以下异常。

6b681ac64adfcea805361552d12b144a.png
e9a52ffd4841e69b0ef2d9a2c0508411.png

6.4:如果调用方和被操作字段所在的类不在同一个包,也不属于子类关系,那么只能被public修饰。

调用方:com.stqsht.juc.other.Caller

package com.stqsht.juc.other;import com.stqsht.juc.common.User;import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;//调用方public class Caller { public static void main(String[] args) { User user = new User(0); AtomicIntegerFieldUpdater ai = AtomicIntegerFieldUpdater.newUpdater(User.class, "age"); ai.incrementAndGet(user); System.out.println(user.getAge()); }}

被操作字段所在的类:com.stqsht.juc.common.User

package com.stqsht.juc.common;public class User { protected volatile int age; public User(int age) { this.age = age; } public int getAge() { return age; } public void setAge(int age) { this.age = age; }}

如果上面的代码被private/defalut/protected修饰,则会抛出如下异常

946684d74b5a55c1ee915d7c9a620485.png
726ffa116478ddfce531141e1350b9fe.png
890e148958a7dca5d272cbc404d3be80.png

上面列出了AtomicIntegerFieldUpdater的一些限制,我们知道了怎么使用AtomicIntegerFieldUpdaterle ,那从源码角度,它是怎么有这些限制呢,我们进入它的源码一探究竟。

四、AtomicIntegerFieldUpdater源码解析

当我们跟进源码中看到它是一个抽象类,它只有一个方法能够获取一个实例对象。

public abstract class AtomicIntegerFieldUpdater {  @CallerSensitive public static  AtomicIntegerFieldUpdater newUpdater(Class tclass, String fieldName) { return new AtomicIntegerFieldUpdaterImpl (tclass, fieldName, Reflection.getCallerClass()); }//省略其他代码}参数1:tclass:实例的Class对象,例如上面例子User.class参数2:fieldName:被操作的字段的名称,例如上面例子成员变量age

通过一个newUpdater()方法获取一个实例对象,从这个方法中可以看出他的实现类是AtomicIntegerFieldUpdaterImpl,这个实现类在它的内部,我们一起跟进它的实现类。

private static final class AtomicIntegerFieldUpdaterImplextends AtomicIntegerFieldUpdater {//原子操作封装对象private static final sun.misc.Unsafe U = sun.misc.Unsafe.getUnsafe();private final long offset;//调用方的Class对象,如上面例子Caller.classprivate final Class> cclass;//被操作字段所在的类的Class对象,如上面例子User.classprivate final Class tclass;

上面是子类的几个重要的成员变量,下面我们看一下它的核心构造函数

//参数一:tclass:被操作字段所在类的Class对象//参数二:被操作字段的名称//参数三:调用方的Class对象AtomicIntegerFieldUpdaterImpl(final Class tclass, final String fieldName, final Class> caller) { final Field field; final int modifiers; try {//通过反射获取被操作字段 field = AccessController.doPrivileged( new PrivilegedExceptionAction() { public Field run() throws NoSuchFieldException { return tclass.getDeclaredField(fieldName); } });//获取被操作字段修饰符 modifiers = field.getModifiers();//判断访问控制符的权限 sun.reflect.misc.ReflectUtil.ensureMemberAccess( caller, tclass, null, modifiers); ClassLoader cl = tclass.getClassLoader(); ClassLoader ccl = caller.getClassLoader(); if ((ccl != null) && (ccl != cl) && ((cl == null) || !isAncestor(cl, ccl))) { sun.reflect.misc.ReflectUtil.checkPackageAccess(tclass); } } catch (PrivilegedActionException pae) { throw new RuntimeException(pae.getException()); } catch (Exception ex) {//$5:如果调用方不能直接访问被操作字段,则抛出异常 throw new RuntimeException(ex); } if (field.getType() != int.class)//$2:如果不是int类型,则会抛出异常 throw new IllegalArgumentException("Must be integer type"); if (!Modifier.isVolatile(modifiers))//$1:如果没有被volatile修饰,则抛出异常 throw new IllegalArgumentException("Must be volatile type");  this.cclass = (Modifier.isProtected(modifiers) && tclass.isAssignableFrom(caller) && !isSamePackage(tclass, caller)) ? caller : tclass; this.tclass = tclass;//$3:objectFieldOffset()方法的作用:获取某个字段相对Java对象的起始地址的偏移地址 this.offset = U.objectFieldOffset(field);}

这个构造函数涵盖了AtomicIntegerFieldUpdater的所有限制,所以从源码角度也证明了上面总结的限制。这个构造函数的流程图如下:

da4985aae8409e1d459b4f51738607c4.png

AtomicIntegerFieldUpdater的核心方法和AtomicInteger类似,功能也是一样的:原子更新一个被volatile修饰的变量,这里就不在介绍了,如果不太明白这些方法的用法,请进入我的今日头条主页查看

接下来介绍几个特殊的方法:判断调用方和被操作字段所在的类是否在同一个包,是否是子类关系。

第一个方法:isAncestor:判断是否属于子类关系

private static boolean isAncestor(ClassLoader first, ClassLoader second) { ClassLoader acl = first; do { acl = acl.getParent(); if (second == acl) {//属于子类,返回true return true; } } while (acl != null); return false;}

第二个方法:isSamePackage:判断是否在同一个包

private static boolean isSamePackage(Class> class1, Class> class2) { return class1.getClassLoader() == class2.getClassLoader() && Objects.equals(getPackageName(class1), getPackageName(class2));}private static String getPackageName(Class> cls) { String cn = cls.getName(); int dot = cn.lastIndexOf('.'); return (dot != -1) ? cn.substring(0, dot) : "";}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值