【多线程】线程安全之原子性CAS实现

本文深入探讨了原子性操作的概念,特别是在多线程环境下的重要性,并详细解析了CAS(Compare and Swap)机制的原理与实现。通过Java中的Unsafe类,展示了如何在代码层面应用CAS,确保线程安全。

线程安全之原子性CAS实现

一、概念

  • 原子性操作是针对访问共享变量的操作而言的,且是从该操作的执行线程以外的线程来描述的,即多线程环境下。
  • 原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割而只执行其中的一部分(不可中断性),即强调其不可分割性。
  • 这种“不可分割”是指访问(读、写)某个共享变量的操作从其执行线程以外的任何线程来看,该操作要么已经执行结束要么尚未发生。
    java中实现原子性有两种方式:锁、CAS机制。

二、CAS机制

CAS即compare and swap,比较和交换的意思。CAS操作需要两个操作数值:旧值A(操作前查看到的值)和新值B(要修改为的值),CAS在操作前先比较下旧值A有没有发生变化,没有变化再去交换为新值B,发生变化则不去交换。
我们可以通过java中sun.misc.Unsafe类提供的API来实现CAS操作,但使用Unsafe对象并不简单,首先jdk底层很多类中会用到,我们先看下是怎么用的。

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 6214790243416807050L;

    // setup to use Unsafe.compareAndSwapInt for updates
    private static final Unsafe unsafe = Unsafe.getUnsafe();

这里使用了Unsafe的静态方法getUnsafe,但我们尝试在程序中这么获取下发现会抛异常:

java.lang.SecurityException: Unsafe
	at sun.misc.Unsafe.getUnsafe(Unknown Source)
	at com.wps.thread.CASTest.<clinit>(CASTest.java:18)
Exception in thread "main" java.lang.NullPointerException
	at com.wps.thread.CASTest.main(CASTest.java:31)

查看这个方法的源码我们会发现它只有由主类加载器加载的类才能调用这个方法:

public static Unsafe getUnsafe() {
        Class<?> caller = Reflection.getCallerClass();
        if (!VM.isSystemDomainLoader(caller.getClassLoader()))
            throw new SecurityException("Unsafe");
        return theUnsafe;
    }

但提供了一个theUnsafe实列,我们可以通过反射拿到这个实例:

public final class Unsafe {
    private Unsafe() {}

    private static final Unsafe theUnsafe = new Unsafe();

实现代码:

public class CASTest {
	static Unsafe unsafe;
	private int value=11;
	static long valueOffSet;
	static {
		try {
			//获取Unsafe实例对象
			Field field=Unsafe.class.getDeclaredField("theUnsafe");
			field.setAccessible(true);
			try {
				unsafe=(Unsafe) field.get(Unsafe.class);
				valueOffSet=unsafe.objectFieldOffset(CASTest.class.getDeclaredField("value"));//拿到value属性在内存中的地址(偏移量)
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		CASTest cTest=new CASTest();
		int oldv=unsafe.getIntVolatile(cTest, valueOffSet);
		System.out.println("修改前的值:"+oldv);
		boolean result=unsafe.compareAndSwapInt(cTest, valueOffSet, 11, 22);
		int newV=unsafe.getIntVolatile(cTest, valueOffSet);
		System.out.println("修改操作:"+result+"  当前值:"+newV);
	}
}

输出结果:
修改前的值:11
修改操作:true 当前值:22

我们尝试修改下预期的旧值:

public class CASTest {
	static Unsafe unsafe;
	private int value=11;
	static long valueOffSet;
	static {
		try {
			//获取Unsafe实例对象
			Field field=Unsafe.class.getDeclaredField("theUnsafe");
			field.setAccessible(true);
			try {
				unsafe=(Unsafe) field.get(Unsafe.class);
				valueOffSet=unsafe.objectFieldOffset(CASTest.class.getDeclaredField("value"));//拿到value属性在内存中的地址(偏移量)
			} catch (IllegalArgumentException e) {
				e.printStackTrace();
			} catch (IllegalAccessException e) {
				e.printStackTrace();
			}
		} catch (NoSuchFieldException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		}
	}
	public static void main(String[] args) {
		CASTest cTest=new CASTest();
		int oldv=unsafe.getIntVolatile(cTest, valueOffSet);
		System.out.println("修改前的值:"+oldv);
		boolean result=unsafe.compareAndSwapInt(cTest, valueOffSet, 1, 22);
		int newV=unsafe.getIntVolatile(cTest, valueOffSet);
		System.out.println("修改操作:"+result+"  当前值:"+newV);
	}
}

输出结果:
修改前的值:11
修改操作:false 当前值:11

Unsafe 是从硬件层面实现原子性的,API基本都是native的,功能非常强大,但操作不好容易出BUG,因此正常情况下我们不会使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值