当前位置: 首页 > java >正文

并发编程 之 Java内存模型、AQS详解:AQS设计思想、Unsafe

并发编程 之 Java内存模型、AQS详解:AQS设计思想、Unsafe

Java内存模型见上一篇

深度理解J.U.C包

CAS同时具有volatile读写的内存语义,java线程间的通信有4种方式

 volatile变量写读

 volatile变量写,CAS更新

 CAS更新volatile变量,CAS更新volatile变量

 CAS更新volatile变量,读volatile变量

通用化的实现模式

  1. 声明共享变量为volatile

  2. 使用CAS原子条件更新来实现线程间的同步

  3. 配合volatile的读写和CAS具有的volatile读写的内存语义实现线程间的通信

j.u.c包底层实现示意图

image-20241112214631046

AQS

AQS实现锁语义的逻辑

写state值,volatile特性会刷新主内存

读state值,volatile特性会使得线程从主内存读取,其他线程的修改变得可见。

锁的获取和释放都需要使得状态的变化在线程间同步

设计思想

AQS提供了3个方法来访问修改同步状态

getState(),获取当前同步状态

setState(newState),设置当前同步状态

compareAndSetState(expect,update),使用CAS设置当前状态,保证状态设置的原子性

image-20241112215607668

image-20241112215637590

Unsafe

介绍

/*** Java是一门安全的编程语言,防止程序员犯很多愚蠢的错误,它们大部分是基于内存管理的。* 但是,有一种方式可以有意的执行一些不安全、容易犯错的操作,那就是使用Unsafe类。<br/>* * Unsafe也被称为Java中的black magic,能够直接进行内存级别的操作,包含100多个方法。* JDK1.8的后期打算将其去除,但无奈很多优秀开源框架都依赖这个类。* 操作范围包含:操作对象、内存信息、屏障、原子操作、内存管理、监视器锁、线程park等。<br/>* *  即使Unsafe对应用程序很有用,但(建议)不要使用它。*/

Java是一门安全的编程语言,防止程序员犯很多内存管理的错误。Unsafe类可以有意的执行一些不安全、容易犯错的操作

 Unsafe类包含100多个方法,这些方法的操作包含:

 Info.仅返回一些低级的内存信息

 Objects.提供用于操作对象及其字段的方法

 Classes.提供用于操作类及其静态字段的方法

 Arrays.操作数组

 Synchronization.低级的同步原语

 Memory.直接内存访问方法

 Monitor锁释放获取

使用

即使Unsafe对应用程序很有用,但(建议)不要使用它。那我们在JVM之外如何使用它那?通过反射的方法

虽然Unsafe类中提供了获取unsafe的方法,但是使用时抛出异常,如下:

Unsafe工具类
public class UnsafeUtils {private static final Unsafe unsafe = getUnsafe();//Unsafe工具类非常强大,特可以去修改引用类型的值,可以修改对象的属性、可以修改数组 等等public static Unsafe getUnsafe() {// 创建Unsafe对象的实例,不像Unsafe unsafe = new Unsafe();这么简单。// 构造器是私有的,静态的getUnsafe()方法,进行了类加载器检查,非系统类路径上的类使用会报SecurityException("Unsafe")异常。//Unsafe unsafe = Unsafe.getUnsafe();// 还好,能通过反射的方式来获取Unsafe类中的theUnsafe成员。// 需要忽略IDE,Forbidden reference->error 错误if(unsafe != null) {return unsafe;}try {Field singletonInstanceField = Unsafe.class.getDeclaredField("theUnsafe");singletonInstanceField.setAccessible(true);return (Unsafe) singletonInstanceField.get(null);} catch (NoSuchFieldException e) {e.printStackTrace();} catch (IllegalAccessException e) {e.printStackTrace();}return null;}
}
通过Unsafe实现i++的原子操作
public class AtomicCounterDemo {private volatile int i;private static long valueOffset;static {try {valueOffset = UnsafeUtils.getUnsafe().objectFieldOffset(AtomicCounterDemo.class.getDeclaredField("i"));} catch (NoSuchFieldException e) {e.printStackTrace();}}private int incrementAndGet(){for (;;){int current = UnsafeUtils.getUnsafe().getInt(this,valueOffset);if(UnsafeUtils.getUnsafe().compareAndSwapInt(this,valueOffset,current,current + 1)){return current + 1;}}}public static void main(String[] args) {AtomicCounterDemo atomicCounterDemo = new AtomicCounterDemo();CountDownLatch countDownLatch = new CountDownLatch(2);new Thread(() -> {for (int i=0;i<100000;i++){atomicCounterDemo.incrementAndGet();}countDownLatch.countDown();}).start();new Thread(() -> {for (int i=0;i<100000;i++){atomicCounterDemo.incrementAndGet();}countDownLatch.countDown();}).start();try {countDownLatch.await();System.out.println(atomicCounterDemo.i);} catch (InterruptedException e) {e.printStackTrace();}}
}
数组操作
/*** 大数组,java中一个数组的长度不会超过int类型的最大长度,有没有办法突破这个限制呢?通过unsafe就能做到。<br/>* 可用于数学计算,代码可操作大数组的数据。另外还可打破GC在大数组上延迟的限制。**/
public class SuperArray {// 内存空间中单元块大小,这里是一个byte块大小private final static int BYTE = 1;private long size;private long address;public SuperArray(long size) {this.size = size;// 实际上,这是堆外内存(off-heap memory)技术// 这种方式的内存分配不在堆上,且不受GC管理,必须小心Unsafe.freeMemory()的使用address = getUnsafe().allocateMemory(size * BYTE);}// 往指定的内存块中存放数据public void set(long i, byte value) {// 不执行任何边界检查,所以任何非法访问可能会导致JVM崩溃getUnsafe().putByte(address + i * BYTE, value);getUnsafe().fullFence();}// 获取数据public int get(long idx) {// 不执行任何边界检查,所以任何非法访问可能会导致JVM崩溃getUnsafe().loadFence();return getUnsafe().getByte(address + idx * BYTE);}public long size() {return size;}public void close() {// 释放内存空间getUnsafe().freeMemory(address);}public static void main(String[] args) {long superSize = (long) Integer.MAX_VALUE * 2;final SuperArray array = new SuperArray(superSize);System.out.println("Array size:" + array.size()); // 4294967294final int countSize = 100;/*int sum = 0;for (int i = 0; i < countSize; i++) {array.set((long) Integer.MAX_VALUE + i, (byte) 3);sum += array.get((long) Integer.MAX_VALUE + i );}System.out.println("Sum of 100 elements:" + sum);  // 300/**/CountDownLatch latch = new CountDownLatch(2);CountDownLatch signLatch = new CountDownLatch(1);new Thread(()->{try {signLatch.await();} catch (InterruptedException e) {e.printStackTrace();}for (int i = 0; i < countSize; i++) {array.set((long) Integer.MAX_VALUE + i, (byte) 3);}latch.countDown();}).start();new Thread(()->{try {signLatch.await();} catch (InterruptedException e) {e.printStackTrace();}int sum = 0;for (int i = 0; i < countSize; i++) {sum += array.get((long) Integer.MAX_VALUE + i );}System.out.println("Sum of 100 elements:" + sum);  // 300latch.countDown();}).start();signLatch.countDown();try {latch.await();} catch (InterruptedException e) {e.printStackTrace();}/**/// 释放对外内存array.close();}
}
拷贝
/*** 更多的玩法:http://ifeve.com/sun-misc-unsafe/* **/
public class MemoryPlayDemo {public static void main(String[] args) {//memoryInfo();//sizeOf(new MemoryPlayDemo());User user1 = new User();User user2 = (User) shallowCopy(user1);System.out.println("user1: "+user1+" user2: "+user2);//modifyFinalField();}public static void memoryInfo() {System.out.println(getUnsafe().addressSize());System.out.println(getUnsafe().pageSize());}public static final int count = 8;public static void modifyFinalField() {// final修饰的字段,编译阶段就不能修改// count = 100;// 来看看黑魔法unsafeSystem.out.println(count);Field countFild = null;try {countFild = MemoryPlayDemo.class.getField("count");long countOffset = getUnsafe().staticFieldOffset(countFild);System.out.println(getUnsafe().getInt(MemoryPlayDemo.class, countOffset));getUnsafe().putInt(MemoryPlayDemo.class, countOffset, 100);System.out.println(getUnsafe().getInt(MemoryPlayDemo.class, countOffset));System.out.println(count);} catch (NoSuchFieldException | SecurityException e) {e.printStackTrace();}}/*** 计算一个对象占用的内存有多大* @param o * @return*/public static long sizeOf(Object o) {// 除了可以通过jol工具来计算,还可以通过unsafe计算。Unsafe u = getUnsafe();HashSet<Field> fields = new HashSet<Field>();Class<?> c = o.getClass();while (c != Object.class) {for (Field f : c.getDeclaredFields()) {if ((f.getModifiers() & Modifier.STATIC) == 0) {fields.add(f);}}c = c.getSuperclass();}// get offsetlong maxSize = 0;for (Field f : fields) {long offset = u.objectFieldOffset(f);if (offset > maxSize) {maxSize = offset;}}return ((maxSize/8) + 1) * 8;   // padding}/** 为了正确内存地址使用,将有符号的int类型强制转换成无符号的long类型*/private static long normalize(int value) {if(value >= 0) return value;return (~0L >>> 32) & value;}/*** 浅拷贝* @param obj* @return*/static Object shallowCopy(Object obj) {long size = sizeOf(obj);long start = toAddress(obj);long address = getUnsafe().allocateMemory(size);getUnsafe().copyMemory(start, address, size);return fromAddress(address);}static long toAddress(Object obj) {Object[] array = new Object[] {obj};long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);return normalize(getUnsafe().getInt(array, baseOffset));}static Object fromAddress(long address) {Object[] array = new Object[] {null};long baseOffset = getUnsafe().arrayBaseOffset(Object[].class);getUnsafe().putLong(array, baseOffset, address);return array[0];}}

内存屏障

public native void loadFence();public native void storeFence();public native void fullFence();
/*** 演练Unsafe中的内存屏障的使用和效果**/
public class MemoryBarrierDemo {public static void main(String[] args) {final MemoryBarrierDemo demo = new MemoryBarrierDemo();Thread th1 = new Thread(()->{demo.run();});th1.start();LockSupport.parkNanos(TimeUnit.SECONDS.toNanos(1));Thread stopTh = new Thread(()->{demo.stop();});stopTh.start();// th1和stopTh线程间需要进行通信和同步,除了volatile、synchronized还有一种更底层的方式// 通过unsafe的内存屏障达到效果try {th1.join();stopTh.join();} catch (InterruptedException e) {e.printStackTrace();}}boolean running = true;int i = 0;// 线程1运行该方法public void run() {while(true) {if(running) {getUnsafe().loadFence();i++;}else {break;}}}// 线程2进行stoppublic void stop() {running = false;getUnsafe().fullFence();System.out.println("stop");System.out.println(i);}
}
tackTrace();}}boolean running = true;int i = 0;// 线程1运行该方法public void run() {while(true) {if(running) {getUnsafe().loadFence();i++;}else {break;}}}// 线程2进行stoppublic void stop() {running = false;getUnsafe().fullFence();System.out.println("stop");System.out.println(i);}
}
http://www.xdnf.cn/news/8179.html

相关文章:

  • 基于自动编码器的图像融合方法
  • 腾讯2025年校招笔试真题手撕(一)
  • 一图胜千言:Typora中Mermaid图表语法全解析
  • Qwen3技术报告笔记
  • 《数据结构笔记二》:顺序表
  • 【技术追踪】ADDP:通过交替去噪扩散过程学习用于图像识别和生成的通用表示(ICLR-2024)
  • Java中static关键字深度解析:从入门到高阶实战
  • 碰一碰发视频源码搭建定制化开发详解,支持OEM
  • One-shot和Zero-shot的区别以及使用场景
  • 嵌入式STM32学习——串口USART 2.3(串口发送数据控制LED灯)
  • 一文读懂GRPC
  • Django的请求和响应+template模板
  • CentOS7/Ubuntu SSH配置允许ROOT密码登录
  • LeRobot的机器人控制系统(上)
  • 无人机避障——深蓝学院浙大栅格地图以及ESDF地图内容
  • BlazeMeter录制jmeter脚本
  • 2025年系统架构师---综合知识卷
  • FreeBSD14.2因为爆内存而导致Xfce4视窗被卡,桌面变黑色,只能看到鼠标在窗体中心,鼠标无反应,键盘无反应
  • 03_基础篇-NumPy(下):深度学习中的常用操作
  • deepseek调用
  • QT ui控件setEnabled(false) 作用
  • SpringBoot系列之OpenAI API 创建智能博客评论助手
  • 人工智能培训:解锁未来职场竞争力的核心路径与课程内容解析
  • 【JAVA基础】什么情况下可以直接使用类名.方法名调用方法?
  • 【VLNs篇】05:TGS-在无地图室外环境中使用视觉语言模型进行轨迹生成和选择
  • python实现web请求与响应
  • Java中创建线程的几种方式
  • 【C++/控制台】简易五子棋游戏
  • LeetCode 257. 二叉树所有路径求解:回溯算法的深度解析与实践
  • 力扣热题——罗马数字转整数