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

并发编程 之 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);}
}

相关文章:

  • MySQL与Redis数据同步实践与优化
  • B2160 病人排队
  • 戴尔电脑怎么开启vt_戴尔电脑新旧bios开启vt虚拟化图文教程
  • 【图像处理入门】1. 数字图像的本质:从像素到色彩模型
  • HarmonyOS 鸿蒙应用开发基础:父组件和子组件的通信方法总结
  • 设计模式-工厂模式和策略模式
  • 选择第三方软件检测机构做软件测试的三大原因
  • Qt网络编程
  • 2 卡尔曼滤波
  • 数据表格控件TeeGrid for VCL/FMX:让数据“说话”更直观!
  • 栈和队列总结
  • os:进程与线程上
  • 【Pandas】pandas DataFrame sem
  • Python训练营---Day33
  • 单一职责原则 (Single Responsibility Principle, SRP)
  • 云原生安全 SaaS :从基础到实践
  • 如何构建一个简单的AI Agent(极简指南)
  • Python训练营打卡——DAY33(2025.5.22)
  • 国产数据库:tidb专题
  • 解决androidstudio不能识别夜神模拟器的问题
  • 做景观素材有哪几个网站/google免费入口
  • 做视频网站对服务器要去/郑州seo优化外包公司
  • 东莞什么平台好做/广东seo网络培训
  • 广州机械加工/建站 seo课程
  • 购物网站建设要多少钱/电商网站建设 网站定制开发
  • 小企业网站源码/常见的网络直接营销有哪些