java5以后的java.util.concurrent包是世界级并发大师Doug Lea的做品,里面主要实现了html
今天咱们主要介绍atomic包下相关内容。java
#CASc++
atomic包下的类主要基于现代主流 CPU 都支持的一种指令,Compare and Swap(CAS),这个指令能为多线程编程带来更好的性能。引用《Java Concurrency in Practice》里的一段描述:算法
在这里,CAS 指的是现代 CPU 普遍支持的一种对内存中的共享数据进行操做的一种特殊指令。这个指令会对内存中的共享数据作原子的读写操做。简单介绍一下这个指令的操做过程:首先,CPU 会将内存中将要被更改的数据与指望的值作比较。而后,当这两个值相等时,CPU 才会将内存中的数值替换为新的值。不然便不作操做。最后,CPU 会将旧的数值返回。这一系列的操做是原子的。它们虽然看似复杂,但倒是 Java 5 并发机制优于原有锁机制的根本。简单来讲,CAS 的含义是“我认为原有的值应该是什么,若是是,则将原有的值更新为新值,不然不作修改,并告诉我原来的值是多少”。spring
#AtomicInteger编程
private volatile int value;
AtomicInteger里面只包含一个字段,用来记录当前值,定义为volatile是为了知足可见性。tomcat
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
一开始定义了static变量Unsafe,AtomicInteger里面的方法都是对unsafe里面安全
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
方法的封装。 咱们来看原子性的i++,多线程
public final int getAndIncrement() { for (;;) { int current = get(); int next = current + 1; if (compareAndSet(current, next)) return current; } }
在一个无限循环里面,首先获取当前值,用当前值+1,而后调用并发
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
unsafe.compareAndSwapInt(this, valueOffset, expect, update)的含义是把this对象里面valueOffset(在一开始static代码里面获取)这个位置(即value值)跟expect比较,若是相等,则修改成update,返回true;若是不相等,说明在获取到current以后有其余线程修改过value的值,则从新来一遍,一直到修改为功为止。这里就能够看出,理论上来讲,这个方法是有可能永远不能返回的,实际而言,当并发冲突很严重,反复compareAndSet(current, next)失败,有可能也须要花费不少时间。
AtomicInteger里面的其余方法,基本相似;其余类包括AtomicLong,AtomicReference等也是基本对Unsafe里面compareAndSet的一个封装。
#Unsafe
前面能够看到Unsafe类在实现atomic的重要性。为何有Unsafe这个class呢,基本缘由是Java不容许代码直接操做内存,好处是更安全,通常不会出现内存泄露,由于有JVM的GC;坏处是有些底层调用执行不了。个人理解是,Unsafe就是这个java安全围城通向好比c++这个不安全外围的一道门,因此叫Unsafe嘛。Unsafe里面基本都是native,即经过JNI调用c/c++等代码。大部分是直接内存操做,以及后面会讲到的挂起唤醒线程等,包括park和unpark。
前面到
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
这个方法就不是java代码了,若是想看实现的话,须要下载OpenJDK源码,里面是c++代码调用汇编代码,blabla。我不建议你们再往下继续了,缘由有几个,一是咱们用java等高级语言的目的就是为了不纠结复杂的底层细节,站在更高层的角度思考问题,并且java里面还有更多的问题等待你去解决,更多的知识能够学习呢!若是你说你已经把java彻底掌握了,包括把jdk源码,tomcat、spring,xxxxx源码都看过了,实在没得看了,那我会说,多陪陪家人吧~除非你是JVM开发工程师,哦,那很差意思,大神,当我啥都没说。。。。为了完整性,我贴几个参考连接http://www.blogjava.net/mstar/archive/2013/04/24/398351.html, http://zl198751.iteye.com/blog/1848575.
那么若是获取Unsafe呢?Unsafe有一个static方法能够获取Unsafe实例,以下
public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(2); if(var0.getClassLoader() != null) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } }
但是你若是在本身代码里使用,能够编译经过,可是运行时候报错。由于里面限制了调用getUnsafe()这个方法的类必须是启动类加载器Bootstrap Loader。因此若是想在本身代码里面调用Unsafe的话(强烈建议不要这样子作),能够用Java的反射来实现:
static class UnsafeSupport { private static Unsafe unsafe; static { Field field; try { // 由反编译Unsafe类得到的信息 field = Unsafe.class.getDeclaredField("theUnsafe"); field.setAccessible(true); // 获取静态属性,Unsafe在启动JVM时随rt.jar装载 unsafe = (Unsafe) field.get(null); } catch (Exception e) { e.printStackTrace(); } } public static Unsafe getInstance() { // return Unsafe.getUnsafe();//没有用,只能native获取,不然会抛异常 return unsafe; } }
获取到了Unsafe的实例以后,你照样能够本身实现Atomic类,再说一遍,强烈建议不要这样作!!!
#CAS优势
Compare and Set 是一个非阻塞的算法,这是它的优点。由于使用的是CPU支持的指令,提供了比原有的并发机制更好的性能和伸缩性。能够认为通常状况下性能更好,而且也更容易使用(这才是关键啊)。
#CAS缺点
##ABA问题
CAS操做容易致使ABA问题,也就是在作a++之间,a可能被多个线程修改过了,只不过回到了最初的值,这时CAS会认为a的值没有变。a在外面逛了一圈回来,你能保证它没有作任何坏事,不能!!也许它讨闲,把b的值减了一下,把c的值加了一下等等。解决ABA问题的方法有不少,能够考虑增长一个修改计数(版本号),只有修改计数不变的且a值不变的状况下才作a++,atomic包下有AtomicStampedReference类作这个事情,这和事务原子性处理有点相似!
##循环时间长开销大
我在本身的电脑上用100个线程去修改一个共享变量,发现用AtomicInteger就比synchronized慢,可是都很快!因此仍是那个建议,不要过早优化,不要纠结究竟是1ms仍是2ms,除非测试以后发现确实是性能瓶颈,而后再仔细看一下,是否是代码的使用有问题,要相信,能写到JDK里的代码,通常都不会有问题。通常不到一天几千万上亿的PV,应该是没啥问题的。并且JVM对synchronized作了不少优化,包括锁去除(Lock Elimination),轻量级锁,偏向锁等,因此写代码的时候首先仍是主要考虑代码正确、清晰、可维护。
##只能保证一个共享变量的原子操做
若是并发约束条件涉及到两个变量,就不能用两个原子变量来达到总体的原子性,仍是得用同步。固然你也能够用一个变通的方法,定义一个class,里面包含约束条件涉及到的变量,而后用AtomicReference来实现原子性。
#总结
atomic包下的类好比AtomicInteger实现原子性的方法主要是依靠现代主流 CPU 都支持的CAS指令,它是经过Unsafe类的native方法调用的。通常而言性能比用锁同步要好,可是都已经很好了,通常而言不会遇到性能问题,关键仍是看它的语义是否知足使用要求,以及是否可让代码更清新。
Refers