【实战Java高并发程序设计5】让普通变量也享受原子操做

有时候,因为初期考虑不周,或者后期的需求变化,一些普通变量可能也会有线程安全的需求。若是改动不大,咱们能够简单地修改程序中每个使用或者读取这个变量的地方。但显然,这样并不符合软件设计中的一条重要原则——开闭原则。也就是系统对功能的增长应该是开发的,而对修改应该是相对保守的。并且,若是系统里使用到这个变量的地方特别多,一个一个修改也是一件使人厌烦的事情(何况不少使用场景下可能只是只读的,并没有线程安全的强烈要求,彻底能够保持原样)。segmentfault

若是你有这种困扰,在这里根本不须要担忧,由于在原子包里还有一个实用的工具类AtomicIntegerFieldUpdater。它可让你在不改动(或者极少改动)原有代码的基础上,让普通的变量也享受CAS操做带来的线程安全性,这样你能够修改极少的代码,来得到线程安全的保证。这听起来是否是让人很激动呢?数组

根据数据类型不一样,这个Updater有3种,分别是AtomicIntegerFieldUpdater、AtomicLongFieldUpdater和AtomicReferenceFieldUpdater。顾名思义,它们分别能够对int、long和普通对象就行CAS修改。安全

如今来思考这么一个场景。假设某地要进行一次选举。如今模拟这个机票场景,若是选民投了候选人一票,就记为1,不然记为0。最终的选票显然就是全部数据的简单求和。并发

01 public class AtomicIntegerFieldUpdaterDemo {
02     public static class Candidate{
03         int id;
04         volatile int score;
05     }
06     public final static AtomicIntegerFieldUpdater<Candidate> scoreUpdater 
07         = AtomicIntegerFieldUpdater.newUpdater(Candidate.class, "score");
08     //检查Updater是否工做正确
09     public static AtomicInteger allScore=new AtomicInteger(0);
10     public static void main(String[] args) throws InterruptedException {
11         final Candidate stu=new Candidate();
12         Thread[] t=new Thread[10000];
13         for(int i = 0 ; i < 10000 ; i++) {  
14             t[i]=new Thread() {  
15                 public void run() {  
16                     if(Math.random()>0.4){
17                         scoreUpdater.incrementAndGet(stu);
18                         allScore.incrementAndGet();
19                     }
20                 }  
21             };
22             t[i].start();
23         }  
24         for(int i = 0 ; i < 10000 ; i++) {  t[i].join();}
25         System.out.println("score="+stu.score);
26         System.out.println("allScore="+allScore);
27     }
28 }

上述代码模拟了这个计票场景。候选人的得票数量记录在Candidate.score中。注意,它是一个普通的volatile变量。而volatile变量并非线程安全的。第6~7行定义了dom

AtomicIntegerFieldUpdater实例,用来对Candidate.score进行写入。然后续的allScore咱们用来检查AtomicIntegerFieldUpdater的正确性。若是AtomicIntegerFieldUpdater真的保证了线程安全,那么最终Candidate.score和allScore的值必然是相等的。不然,就说明AtomicIntegerFieldUpdater根本没有确保线程安全的写入。第12~21行模拟了计票过程,这里假设有大约60%的人投同意票,而且投票是随机进行的。第17行使用Updater修改Candidate.score(这里应该是线程安全的),第18行使用AtomicInteger计数,做为参考基准。高并发

你们若是运行这段程序,不难发现,最终的Candidate.score老是和allScore绝对相等。这说明AtomicIntegerFieldUpdater很好地保证了Candidate.score的线程安全。工具


虽然AtomicIntegerFieldUpdater很好用,可是仍是有几个注意事项:线程

第一,Updater只能修改它可见范围内的变量。由于Updater使用反射获得这个变量。若是变量不可见,就会出错。好比若是score申明为private,就是不可行的。设计

第二,为了确保变量被正确的读取,它必须是volatile类型的。若是咱们原有代码中未申明这个类型,那么简单得申明一下就行,这不会引发什么问题。指针

第三,因为CAS操做会经过对象实例中的偏移量直接进行赋值,所以,它不支持static字段(Unsafe. objectFieldOffset()不支持静态变量)。

好了,经过AtomicIntegerFieldUpdater,是否是让咱们能够更加为所欲为得对系统关键数据进行线程安全地保护呢?

【实战Java高并发程序设计1】Java中的指针:Unsafe类
【实战Java高并发程序设计2】无锁的对象引用:AtomicReference
【实战Java高并发程序设计 3】带有时间戳的对象引用:AtomicStampedReference
【实战Java高并发程序设计 4】数组也能无锁AtomicIntegerArray

相关文章
相关标签/搜索