本文首发于一世流云的专栏: https://segmentfault.com/blog...
在java.util.concurrent.atomic
包中,由三个比较特殊的原子类:AtomicIntegerFieldUpdater
、AtomicLongFieldUpdater
、AtomicReferenceFieldUpdater
。
经过名称能够看到,这几类的功能大体相同,只是针对的类型有所不一样。java
所谓AtomicXXXFieldUpdater,就是能够以一种线程安全的方式操做非线程安全对象的某些字段。光这么说有点难理解,咱们经过一个例子来看下。segmentfault
假设有一个公司帐户Account,100我的同时往里面存钱1块钱,那么正常状况下,最终帐户的总金额应该是100。
先来看下线程不安全的方式:安全
帐户类:并发
class Account { private volatile int money; Account(int initial) { this.money = initial; } public void increMoney() { money++; } public int getMoney() { return money; } @Override public String toString() { return "Account{" + "money=" + money + '}'; } }
调用类:框架
public class FieldUpdaterTest { public static void main(String[] args) throws InterruptedException { Account account = new Account(0); // 初始金额0 List<Thread> list = new ArrayList<>(); for (int i = 0; i < 100; i++) { Thread t = new Thread(new Task(account)); list.add(t); t.start(); } for (Thread t : list) { // 等待全部线程执行完成 t.join(); } System.out.println(account.toString()); } private static class Task implements Runnable { private Account account; Task(Account account) { this.account = account; } @Override public void run() { account.increMoney(); // 增长帐户金额 } } }
上述未对Account作并发控制,最终帐户金额极可能小于100。
按照以前学习的atomic框架,能够将Account类的int类型字段改成AtomicInteger,或者在Task任务类中,将全部涉及到共享变量的地方都加锁访问。ide
那么,还有没有其它解决方式?学习
本章开头咱们讲到,AtomicXXXFieldUpdater能够以一种线程安全的方式操做非线程安全对象的某些字段。
这里,Account就是非线程安全对象,money就是须要操做的字段。this
咱们来对上述代码进行改造:
帐户类Account改造:atom
class Account { private volatile int money; private static final AtomicIntegerFieldUpdater<Account> updater = AtomicIntegerFieldUpdater.newUpdater(Account.class, "money"); // 引入AtomicIntegerFieldUpdater Account(int initial) { this.money = initial; } public void increMoney() { updater.incrementAndGet(this); // 经过AtomicIntegerFieldUpdater操做字段 } public int getMoney() { return money; } @Override public String toString() { return "Account{" + "money=" + money + '}'; } }
调用方,并未作任何改变:spa
public class FieldUpdaterTest { public static void main(String[] args) throws InterruptedException { Account account = new Account(0); List<Thread> list = new ArrayList<>(); for (int i = 0; i < 100; i++) { Thread t = new Thread(new Task(account)); list.add(t); t.start(); } for (Thread t : list) { t.join(); } System.out.println(account.toString()); } private static class Task implements Runnable { private Account account; Task(Account account) { this.account = account; } @Override public void run() { account.increMoney(); } } }
上述代码,不管执行多少次,最终结果都是“100”,由于这回是线程安全的。
对比下改造,能够发现,AtomicIntegerFiledUpdater的引入,使得咱们能够在不修改用户代码(调用方)的状况下,就能实现并发安全性。
惟一的改变之处就是Account内部的改造:
这也是AtomicXXXFieldUpdater引入的一个重要缘由,单纯从功能上来说,能用AtomicXXXFieldUpdater实现的并发控制,同步器和其它原子类都能实现,可是使用AtomicXXXFieldUpdater,符合面向对象设计的一个基本原则——开闭原则,尤为是对一些遗留代码的改造上。
另外,使用AtomicXXXFieldUpdater,不须要进行任何同步处理,单纯的使用CAS+自旋操做就能够实现同步的效果。这也是整个atomic包的设计理念之一。
AtomicIntegerFieldUpdater
、AtomicLongFieldUpdater
、AtomicReferenceFieldUpdater
这三个类大同小异,AtomicIntegerFieldUpdater只能处理int原始类型的字段,AtomicLongFieldUpdater只能处理long原始类型的字段,AtomicReferenceFieldUpdater能够处理全部引用类型的字段。
本节以AtomicReferenceFieldUpdater为例,介绍下FiledUpdater的基本原理。
AtomicReferenceFieldUpdater自己是一个抽象类,没有公开的构造器,只能经过静态方法newUpdater建立一个AtomicReferenceFieldUpdater子类对象:
newUpdater的三个入参含义以下:
入参名称 | 含义 |
---|---|
tclass | 目标对象的类型 |
vclass | 目标字段的类型 |
fieldName | 目标字段名 |
AtomicReferenceFieldUpdaterImpl是AtomicReferenceFieldUpdater的一个内部类,并继承了AtomicReferenceFieldUpdater。AtomicReferenceFieldUpdater的API,基本都是委托AtomicReferenceFieldUpdaterImpl 来实现的。
来看下AtomicReferenceFieldUpdaterImpl 对象的构造,其实就是一系列的权限检查:
经过源码,能够看到AtomicReferenceFieldUpdater的使用必须知足如下条件:
AtomicReferenceFieldUpdater中全部的方法都是基于Unsafe类操做,看下最经常使用的方法compareAndSet:
经过偏移量offset获取字段的地址,而后利用Unsafe进行CAS更新。
其它方法也大同小异,读者能够参考Oracle官方文档和JDK源码。