处理器如何实现原子操作
处理器如何实现原子操作:首先处理器会自动保证基本的内存操作的原子性。针对简单的内存操作处理器自动保证其原子性,对于复杂的内存操作处理器提供总线锁定和缓存锁定两个机制来保证复杂内存操作的原子性。
a、第一个机制是通过总线锁保证原子性。所谓总线锁就是使用处理器提供的一个LOCK#信号,当一个处理器在总线上输出此信号时,其他处理器的请求将被阻塞住,那么该处理器可以独占使用共享内存。所以总线锁定的开销比较大。
b、使用缓存锁保证原子性。频繁使用的内存会缓存在处理器的L1,L2和L3高速缓存里,那么原子操作就可以直接在处理器内部缓存中进行,并不需要声明总线锁。在处理器中可以使用"缓存锁定"的方式来实现复杂的原子性。
所谓"缓存锁定"就是如果缓存在处理器缓存行中内存区域在LOCK操作期间被锁定,当它执行锁操作回写内存时,处理器不在总线上声言LOCK#信号,而是修改内部的内存地址,并允许它的缓存一致性机制来保证操作的原子性。
c、但是有两种情况下处理器不会使用缓存锁定。
第一种情况是:当操作的数据不能被缓存在处理器内部,或操作的数据跨多个缓存行(cache line),则处理器会调用总线锁定。
第二种情况是:有些处理器不支持缓存锁定。对于Inter486和奔腾处理器,就算锁定的内存区域在处理器的缓存行中也会调用总线锁定。
Java当中如何实现原子操作:java中可以通过锁和循环CAS的方式来实现原子操作。JVM中的CAS操作正是利用了上文中提到的处理器提供的CMPXCHG指令实现的。自旋CAS实现的基本思路就是循环进行CAS操作直到成功为止。
Atomic:在Atomic包里一共有12个类,四种原子更新方式,分别是原子更新基本类型,原子更新数组,原子更新引用和原子更新字段。Atomic包里的类基本都是使用Unsafe实现的包装类。
分类:
1、原子更新基本类型类
用于通过原子的方式更新基本类型,Atomic包提供了以下三个类:
a、AtomicBoolean:原子更新布尔类型。
b、AtomicInteger:原子更新整型。
c、AtomicLong:原子更新长整型。
这里只有boolean、int和long的基本类型的原子更新,那么byte、short、char、double和float的原子基本类型呢?
Atomic包里的类基本都是使用Unsafe实现的,Unsafe只提供了三种CAS方法,compareAndSwapObject,compareAndSwapInt和compareAndSwapLong,再看AtomicBoolean源码,发现其是先把Boolean转换成整型,再使用compareAndSwapInt进行CAS,所以原子更新其他基本类型也可以用类似的思路来实现。
2、原子更新数组类
通过原子的方式更新数组里的某个元素,Atomic包提供了以下三个类:
a、AtomicIntegerArray:原子更新整型数组里的元素。
b、AtomicLongArray:原子更新长整型数组里的元素。
c、AtomicReferenceArray:原子更新引用类型数组里的元素。
AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,其常用方法如下:
a、int addAndGet(int i, int delta):以原子方式将输入值与数组中索引i的元素相加。
b、boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。
3、原子更新引用类型
原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子的更新多个变量,就需要使用这个原子更新引用类型提供的类。Atomic包提供了以下三个类:
a、AtomicReference:原子更新引用类型。
b、AtomicReferenceFieldUpdater:原子更新引用类型里的字段。
c、AtomicMarkableReference:原子更新带有标记位的引用类型。可以原子的更新一个布尔类型的标记位和引用类型。构造方法是AtomicMarkableReference(V initialRef, boolean initialMark)
4、原子更新字段类
如果我们只需要某个类里的某个字段,那么就需要使用原子更新字段类,Atomic包提供了以下三个类:
a、AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
b、AtomicLongFieldUpdater:原子更新长整型字段的更新器。
c、AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更数据和数据的版本号,可以解决使用CAS进行原子更新时,可能出现的ABA问题。
原子更新字段类都是抽象类,每次使用都时候必须使用静态方法newUpdater创建一个更新器。原子更新类的字段的必须使用public volatile修饰符。
Unsafe应用解析
Unsafe是位于sun.misc包下的一个类,主要提供一些用于执行低级别、不安全操作的方法,如直接访问系统内存资源、自主管理内存资源等,这些方法在提升Java运行效率、增强Java语言底层资源操作能力方面起到了很大的作用。
在程序中过度、不正确使用Unsafe类会使得程序出错的概率变大,使得Java这种安全的语言变得不再“安全”,因此对Unsafe的使用一定要慎重。
获取Unsafe实例的方式:
1、从getUnsafe方法的使用限制条件出发,通过Java命令行命令-Xbootclasspath/a把调用Unsafe相关方法的类A所在jar包路径追加到默认的bootstrap路径中,使得A被引导类加载器加载,从而通过Unsafe.getUnsafe方法安全的获取Unsafe实例。
2、通过反射获取单例对象theUnsafe。
Unsafe提供的API大致可分为内存操作、CAS、Class相关、对象操作、线程调度、系统信息获取、内存屏障、数组操作等几类,下面将对其相关方法和应用场景进行详细介绍。
a、内存操作
// 分配内存
public native long allocateMemory(long var1);
// 重新分配内存
public native long reallocateMemory(long var1, long var3);
// 内存初始化
public native void setMemory(long var1, long var3, byte var5);
// 内存复制
public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7);
// 清除内存
public native void freeMemory(long var1);
b、CAS相关
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
c、线程调度
包括线程挂起、恢复、锁机制等方法。
//取消阻塞线程
public native void unpark(Object thread);
//阻塞线程
public native void park(boolean isAbsolute, long time);
//获得对象锁(可重入锁)
@Deprecated
public native void monitorEnter(Object o);
//释放对象锁
@Deprecated
public native void monitorExit(Object o);
//尝试获取对象锁
@Deprecated
public native boolean tryMonitorEnter(Object o);
d、内存屏障
//内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前
//保证在这个屏障之前的所有读操作都已经完成。
public native void loadFence();
//内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前
//保证在这个屏障之前的所有写操作都已经完成。
public native void storeFence();
//内存屏障,禁止load、store操作重排序
//保证在这个屏障之前的所有读写操作都已经完成。
public native void fullFence();
e、类加载
//defineClass方法定义一个类,用于动态地创建类。
public native Class<?> defineClass(String var1, byte[] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6);
//用于动态的创建一个匿名内部类。
public native Class<?> defineAnonymousClass(Class<?> var1, byte[] var2, Object[] var3);
//创建一个类的实例,但是不会调用这个实例的构造方法,如果这个类还未被初始化,则初始化这个类。
public native Object allocateInstance(Class<?> var1) throws InstantiationException;
//用于判断是否需要初始化一个类。
public native boolean shouldBeInitialized(Class<?> var1);
//用于保证已经初始化过一个类。
public native void ensureClassInitialized(Class<?> var1);