前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >JDK8的CAS实现学习笔记

JDK8的CAS实现学习笔记

作者头像
itliusir
发布2018-05-21 17:03:24
7290
发布2018-05-21 17:03:24
举报
文章被收录于专栏:刘君君刘君君

摘要:CAS全称为compare and swap,是原子操作的一种,可用于在多线程编程中实现不被打断的数据交换操作,从而避免多线程同时改写某一数据时由于执行顺序不确定性以及中断的不可预知性产生的数据不一致问题。 该操作通过将内存中的值与指定数据进行比较,当数值一样时将内存中的数据替换为新的值。 –from Wikipedia

正文:

在使用上,通常会记录下某块内存中的旧值,通过对旧值进行一系列的操作后得到新值,然后通过CAS操作将新值与旧值进行交换。如果这块内存的值在这期间内没被修改过,则旧值会与内存中的数据相同,这时CAS操作将会成功执行 使内存中的数据变为新值。如果内存中的值在这期间内被修改过,则一般来说旧值会与内存中的数据不同,这时CAS操作将会失败,新值将不会被写入内存。

CAS的实现

接下来我们去看CAS在java中的实现,sun.misc.Unsafe提供了compareAndSwap系列函数。

代码语言:javascript
复制
/**
    * Atomically update Java variable to <tt>x</tt> if it is currently
    * holding <tt>expected</tt>.
    * @return <tt>true</tt> if successful
    */
   public final native boolean compareAndSwapObject(Object o, long offset,
                                                    Object expected,
                                                    Object x);

   /**
    * Atomically update Java variable to <tt>x</tt> if it is currently
    * holding <tt>expected</tt>.
    * @return <tt>true</tt> if successful
    */
   public final native boolean compareAndSwapInt(Object o, long offset,
                                                 int expected,
                                                 int x);

   /**
    * Atomically update Java variable to <tt>x</tt> if it is currently
    * holding <tt>expected</tt>.
    * @return <tt>true</tt> if successful
    */
   public final native boolean compareAndSwapLong(Object o, long offset,
                                                  long expected,
                                                  long x);

可以看到native发现这是一个本地方法调用,可以去查看对应的OpenJDK中调用代码atomic_linux_x86.inline.hpp / atomic_windows_x86.inline.hpp

代码语言:javascript
复制
//linux(int 类型)
inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) {
  	int mp = os::is_MP();
  	__asm__ volatile (LOCK_IF_MP(%4) "cmpxchgl %1,(%3)"
                    : "=a" (exchange_value)
                    : "r" (exchange_value), "a" (compare_value), "r" (dest), "r" (mp)
                    : "cc", "memory");
  	return exchange_value;
}

//windows(int 类型)
inline jint Atomic::cmpxchg(jint exchange_value, volatile jint* dest, jint compare_value) {
 		return (*os::atomic_cmpxchg_func)(exchange_value, dest, compare_value);
}

可以看到其实现方式是基于硬件平台的汇编指令cmpxchg指令完成的,JVM只是封装了汇编调用。以linux x86处理器为例子,int mp = os::is_MP()中的MP是multiprocessor,即多处理器,当遇到是多处理器的情况下加上LOCK。cmpxchgl指的应该是compare and exchange指令。

CAS在Java中的使用

代码语言:javascript
复制
	public class UnsafeTest {

		private static Unsafe unsafe;
	
		static {
			try {
				// 通过反射获取rt.jar下的Unsafe类
				Field field = Unsafe.class.getDeclaredField("theUnsafe");
				field.setAccessible(true);
				unsafe = (Unsafe) field.get(null);
			} catch (Exception e) {
				System.out.println("Get Unsafe instance occur error" + e);
			}
		}
	
		public static void main(String[] args) throws Exception {
			Class clazz = Target.class;
			Field[] fields = clazz.getDeclaredFields();
			Target target = new Target();
			Field intFiled = clazz.getDeclaredField("intParam");
			Field longFiled = clazz.getDeclaredField("longParam");
			Field strFiled = clazz.getDeclaredField("strParam");
			Field strFiled2 = clazz.getDeclaredField("strParam2");
	
			// intParam
			System.out.print(unsafe.compareAndSwapInt(target, 12, 3, 10) + ":");
			System.out.println((Integer) intFiled.get(target));
			// longParam
			System.out.print(unsafe.compareAndSwapLong(target, 16, 1l, 2l) + ":");
			System.out.println((Long) longFiled.get(target));
			// strParam
			System.out.print(unsafe.compareAndSwapObject(target, 24, null, "5")
					+ ":");
			System.out.println((String) strFiled.get(target));
			// strParam2
			System.out.print(unsafe.compareAndSwapObject(target, 28, null, "6")
					+ ":");
			System.out.println((String) strFiled2.get(target));
	
		}
	}
	
class Target {
	int intParam = 3;
	long longParam = 1l;
	String strParam;
	String strParam2;
}

如代码所示,compareAndSwapXx方法会根据第二个参数”偏移量”去拿偏移量这么多的属性的值和第三个参数对比,如果相同则将该属性值替换为第四个参数。该偏移量是指某个字段相对Java对象的起始位置的偏移量,可以通过unsafe.objectFieldOffset(param)去获取对应属性的偏移量。

顺便介绍个查看对象的属性位置分布的一个小工具:jol

使用Demo:

首先引用jol-core包

代码语言:javascript
复制
<!-- https://mvnrepository.com/artifact/org.openjdk.jol/jol-core -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.9</version>
</dependency>

然后在项目里简单使用下:

代码语言:javascript
复制
public class TestOffset {
	public static void main(String[] args) {
		out.println(VM.current().details());
		out.println(ClassLayout.parseClass(Throwable.class).toPrintable());
	}
}

结果如下,根据偏移量界面化的显示属性分布的位置:

代码语言:javascript
复制
# Running 64-bit HotSpot VM.
# Using compressed oop with 3-bit shift.
# Using compressed klass with 3-bit shift.
# Objects are 8 bytes aligned.
# Field sizes by type: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]
# Array element sizes: 4, 1, 1, 2, 2, 4, 4, 8, 8 [bytes]

java.lang.Throwable object internals:
 OFFSET  SIZE                            TYPE DESCRIPTION                               VALUE
      0    12                                 (object header)                           N/A
     12     4                                 (alignment/padding gap)                  
     16     4                java.lang.String Throwable.detailMessage                   N/A
     20     4             java.lang.Throwable Throwable.cause                           N/A
     24     4   java.lang.StackTraceElement[] Throwable.stackTrace                      N/A
     28     4                  java.util.List Throwable.suppressedExceptions            N/A
Instance size: 32 bytes
Space losses: 4 bytes internal + 0 bytes external = 4 bytes total

CAS存在的ABA问题

CAS普遍存在的一个问题就是ABA问题,即是当线程一将变量A修改为B,之后又修改为A,线程二去对比A发现没变化就会判断出错。目前很多都是使用加上版本号来解决,加个version字段,每次修改就++,每次判断时候多判断下版本号是否变化来确定某变量是否被修改。

下班啦,暂且到这里,祝大家十一玩的开心~~~

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2017-09-30,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 正文:
  • CAS的实现
  • CAS在Java中的使用
    • 使用Demo:
    • CAS存在的ABA问题
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档