目录
1.JUC包下的原子类
使用原子类比使用锁的的效率高,因为这些原子类都是使用非阻塞的CAS算法实现的。具体是使用Unsafe类提供的方法实现的。
总结起来就是一句话,如果在当前的var1类中,内存偏移地址为var2的变量,当前值等于var6,就给他加var4。
原子类中value设计成volatile的原因是,因为多个线程之间这个value必须要共享的才行,因为Unsafe调用CAS算法的时候要把value当初参数传进去。
2.Unsafe类
Unsafe类提供了硬件级别的原子操作,Unsafe类中的方法都是native方法。
Unsafe类:Unsafe提供的API大致可分为内存操作、CAS、Class相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等几类.
3.Unsafe类的简单使用的问题
我们也写一个类似的AtomicLong的类来实现自增的功能。
启动直接报错:
跟着这个错误我们可以找到,当加载我们自定义的这个类的时候,使用的是我们的应用程序类加载器AppClassLoador。而这里做了一个校验类加载器是为了只能让引导类加载器来加载这个Unsafe类。我们在深入理解Java虚拟机第七章里面说过,如果想把一个类交给引导类来加载,则需要使用null去替代ClassLoader.
这么做的目的是Unsafe类可以直接操作内存,如果让应用程序类加载器来加载,势必不安全。
4.那么我们怎么使用这个Unsafe类呢?
我一开始想的是使用引导类加载器来加载自定义的这个类,利用类加载的传到规则不就可以使用启动类来加载这个Unsafe类吗,但是试了几次没有成功,换一种方式,利用反射,利用反射去获得这个实例。
我们可以观察到 Unsafe将获取到的这个实例放到了这个属性中,我们就可以利用反射拿到这个实例
再次启动之后从0变成了1
看完这个例子之后我们再来总结一下CAS算法,就是通过变量在实例中的内存偏移量拿到这个变量在内存中此时此刻的值,如果与之前拿到这个变量的旧值相等,说明没有线程对他做修改,可以修改成我们期待的新值。
JDK8以后的优化
在竞争比较激烈的情况下,AtomicLong 还是又性能的问题,因为CAS一次只能有一个线程能修改成功,其他的线程会进入自旋状态。
JDK8提供了高性能的LongAdder类,他就是解决高并发下性能问题的,他维护了一个基值变量base,和一个Cell数组,当高并发时,会有多个Cell变量来分担线程的竞争压力。最后的值等于基值base加上各个cell的值。
Cell的结构:Cell结构里面也是用到了validate和CAS,用来保证当前线程跟新是被分配Cell中value的原子性
LongAccumulator类
可以自定去定义这个双目运算的接口