从JDK源码角度看并发的原子性如何保证

本文深入探讨了JDK源码中的Unsafe类,揭示了其在AQS框架中的关键作用,包括如何利用CAS操作实现原子性,并介绍了获取Unsafe实例的方法及其安全性考量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

JDK源码中,在研究AQS框架时,会发现很多地方都使用了CAS操作,在并发实现中CAS操作必须具备原子性,而且是硬件级别的原子性,Java被隔离在硬件之上,明显力不从心,这时为了能直接操作操作系统层面,肯定要通过用C++编写的native本地方法来扩展实现。JDK提供了一个类来满足CAS的要求,sun.misc.Unsafe,从名字上可以大概知道它用于执行低级别、不安全的操作,AQS就是使用此类完成硬件级别的原子操作。

Unsafe是一个很强大的类,它可以分配内存、释放内存、可以定位对象某字段的位置、可以修改对象的字段值、可以使线程挂起、使线程恢复、可进行硬件级别原子的CAS操作等等,但平时我们没有这么特殊的需求去使用它,而且必须在受信任代码(一般由JVM指定)中调用此类,例如直接Unsafe unsafe = Unsafe.getUnsafe();获取一个Unsafe实例是不会成功的,因为这个类的安全性很重要,设计者对其进行了如下判断,它会检测调用它的类是否由启动类加载器Bootstrap ClassLoader(它的类加载器为null)加载,由此保证此类只能由JVM指定的类使用。

public static Unsafe getUnsafe() {  
   Class cc = sun.reflect.Reflection.getCallerClass(2);  
   if (cc.getClassLoader() != null)  
       throw new SecurityException("Unsafe");  
   return theUnsafe;  
}复制代码

当然可以通过反射绕过上面的限制,用下面的getUnsafeInstance方法可以获取Unsafe实例,这段代码演示了如何获取java对象的相对地址偏移量及使用Unsafe完成CAS操作,最终输出的是flag字段的内存偏移量及CAS操作后的值。分别为8和101。另外如果使用开发工具如Eclipse,可能会编译通不过,只要把编译错误提示关掉即可。

public class UnsafeTest {  
private int flag = 100;  
private static long offset;  
private static Unsafe unsafe = null;  
static{  
     try{  
          unsafe= getUnsafeInstance();  
          offset= unsafe.objectFieldOffset(UnsafeTest.class  
                   .getDeclaredField("flag"));  
     }catch (Exception e) {  
          e.printStackTrace();  
     }  
}  

public static void main(String[] args) throws Exception {  
     int expect = 100;  
     int update = 101;  
     UnsafeTest unsafeTest = new UnsafeTest();  
     System.out.println("unsafeTest对象的flag字段的地址偏移量为:"+offset);  
     unsafeTest.doSwap(offset,expect, update);  
     System.out.println("CAS操作后的flag值为:" +unsafeTest.getFlag());  
}  

privateboolean doSwap(long offset, int expect, int update) {  
     returnunsafe.compareAndSwapInt(this, offset, expect, update);  
}  

publicint getFlag() {  
     returnflag;  
}  

private static Unsafe getUnsafeInstance() throws SecurityException,  
          NoSuchFieldException,IllegalArgumentException,  
          IllegalAccessException{  
     Field theUnsafeInstance = Unsafe.class.getDeclaredField("theUnsafe");  
     theUnsafeInstance.setAccessible(true);  
     return (Unsafe)theUnsafeInstance.get(Unsafe.class);  
}  
}复制代码

Unsafe类让我们明白了java是如何实现对操作系统操作的,一般我们使用java是不需要在内存中处理java对象及内存地址位置的,但有的时候我们确实需要知道java对象相关的地址,于是我们使用Unsafe类,尽管java对其提供了足够的安全管理。

Java语言的设计者们极力隐藏涉及底层操作系统的相关操作,但此节我们本着对AQS框架实现的目的,不得不剖析了Unsafe类,因为AQS里面即是使用Unsafe获取对象字段的地址偏移量、相关原子操作来实现CAS操作的。

以下是广告相关阅读

========广告时间========

鄙人的新书《Tomcat内核设计剖析》已经在京东销售了,有需要的朋友可以到 item.jd.com/12185360.ht… 进行预定。感谢各位朋友。

为什么写《Tomcat内核设计剖析》

=========================

相关阅读:

从JDK源码角度看Object

谈谈Java基础数据类型

从JDK源码角度看并发锁的优化

从JDK源码角度看线程的阻塞和唤醒

从JDK源码角度看并发竞争的超时

从JDK源码角度看java并发线程的中断

从JDK源码角度看Java并发的公平性

从JDK源码角度看java并发的原子性如何保证

从JDK源码角度看Byte

从JDK源码角度看Boolean

从JDK源码角度看Short

欢迎关注:

这里写图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值