并发之Atomic类与Unsafe类
-
AtomicInteger:整形原子类
-
AtomicLong:长整型原子类
-
AtomicBoolean:布尔型原子类
-
AtomicIntegerArray:整形数组原子类
-
AtomicLongArray:长整形数组原子类
-
AtomicReferenceArray:引用类型数组原子类
-
AtomicReference:引用类型原子类
-
AtomicStampedReference:原子更新引用类型里的字段原子类
-
AtomicMarkableReference :原子更新带有标记位的引用类型
-
AtomicIntegerFieldUpdater:原子更新整形字段的更新器
-
AtomicLongFieldUpdater:原子更新长整形字段的更新器
-
AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于解决原子的更新数据和数据的版本号,可以解决使用 CAS 进行原子更新时可能出现的 ABA 问题。
1 2 3 4 5 6 7 | public final int get() //获取当前的值 public final int getAndSet( int newValue) //获取当前的值,并设置新的值 public final int getAndIncrement() //获取当前的值,并自增 public final int getAndDecrement() //获取当前的值,并自减 public final int getAndAdd( int delta) //获取当前的值,并加上预期的值 boolean compareAndSet( int expect, int update) //如果输入的数值等于预期值,则以原子方式将该值设置为输入值(update) public final void lazySet( int newValue) //最终设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。 |
AtomicInteger使用:使用AtomicInteger之后名不对increment方法加锁也会保证线程安全。
1 2 3 4 5 6 7 8 9 10 | class AtomicIntegerTest { private AtomicInteger count = new AtomicInteger(); //使用AtomicInteger之后,不需要对该方法加锁,也可以实现线程安全。 public void increment() { count.incrementAndGet(); } public int getCount() { return count.get(); } } |
3.1、AtomicInteger实现原理
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | //setup to use Unsafe.compareAndSwapInt for updates(更新操作时提供“比较并替换”的作用) private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger. class .getDeclaredField( "value" )); } catch (Exception ex) { throw new Error(ex); } } private volatile int value; |
AtomicInteger 类主要利用 CAS (compare and swap) + volatile 和 native 方法来保证原子操作,从而避免 synchronized 的高开销,执行效率大为提升。CAS的原理是拿期望的值和原本的一个值作比较,如果相同则更新成新的值。UnSafe 类的 objectFieldOffset() 方法是一个本地方法,这个方法是用来拿到“原来的值”的内存地址,返回值是valueOffset。另外 value 是一个volatile变量,在内存中可见,因此 JVM 可以保证任何时刻任何线程总能拿到该变量的最新值。
4、数组类型原子类介绍
-
AtomicIntegerArray:整形数组原子类
-
AtomicLongArray:长整形数组原子类
-
AtomicReferenceArray:引用类型数组原子类
1 2 3 4 5 6 7 | public final int get( int i) //获取 index=i 位置元素的值 public final int getAndSet( int i, int newValue) //返回 index=i 位置的当前的值,并将其设置为新值:newValue public final int getAndIncrement( int i) //获取 index=i 位置元素的值,并让该位置的元素自增 public final int getAndDecrement( int i) //获取 index=i 位置元素的值,并让该位置的元素自减 public final int getAndAdd( int delta) //获取 index=i 位置元素的值,并加上预期的值 boolean compareAndSet( int expect, int update) //如果输入的数值等于预期值,则以原子方式将 index=i 位置的元素值设置为输入值(update) public final void lazySet( int i, int newValue) //最终 将index=i 位置的元素设置为newValue,使用 lazySet 设置之后可能导致其他线程在之后的一小段时间内还是可以读到旧的值。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | public class AtomicIntegerArrayTest { public static void main(String[] args) { // TODO Auto-generated method stub int temvalue = 0 ; int [] nums = { 1 , 2 , 3 , 4 , 5 , 6 }; AtomicIntegerArray i = new AtomicIntegerArray(nums); for ( int j = 0 ; j < nums.length; j++) { System.out.print(i.get(j) + " " ); } temvalue = i.getAndSet( 0 , 2 ); System.out.println( "\ntemvalue:" + temvalue + "; i:" + i); temvalue = i.getAndIncrement( 0 ); System.out.println( "temvalue:" + temvalue + "; i:" + i); temvalue = i.getAndAdd( 0 , 5 ); System.out.println( "temvalue:" + temvalue + "; i:" + i); } } 输出: 1 2 3 4 5 6 temvalue: 1 ; i:[ 2 , 2 , 3 , 4 , 5 , 6 ] temvalue: 2 ; i:[ 3 , 2 , 3 , 4 , 5 , 6 ] temvalue: 3 ; i:[ 8 , 2 , 3 , 4 , 5 , 6 ] |
5、引用类型原子类
-
AtomicReference:引用类型原子类
-
AtomicStampedReference:原子更新引用类型里的字段原子类
-
AtomicMarkableReference :原子更新带有标记位的引用类型
1 2 3 4 5 6 7 8 9 | AtomicReference() // 使用 null 初始值创建新的 AtomicReference。 AtomicReference(V initialValue) // 使用给定的初始值创建新的 AtomicReference。 boolean compareAndSet(V expect, V update) //如果当前值==预期值,则以原子方式将该值设置为给定的更新值。 V get() // 获取当前值。 V getAndSet(V newValue) //以原子方式设置为给定值,并返回旧值。 void lazySet(V newValue) //最终设置为给定值。 void set(V newValue) // 设置为给定值。 String toString() // 返回当前值的字符串表示形式。 boolean weakCompareAndSet(V expect, V update) // 如果当前值 == 预期值,则以原子方式将该值设置为给定的更新值。 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class Worker{ private String name; private int age; public Worker(String name, int age) { this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public int getAge() { return age; } public void setAge( int age) { this .age = age; } } public class TestAtomicReference { public static void main(String[] args) { AtomicReference<Worker> atomicReference = new AtomicReference<Worker>(); Worker worker1 = new Worker( "zhangsan" , 30 ); atomicReference.set(worker1); Worker worker2 = new Worker( "lisi" , 35 ); atomicReference.compareAndSet(worker1,worker2); System.out.print(atomicReference.get().getAge() + " " ); System.out.println(atomicReference.get().getName()); } } 输出: 35 lisi |
6、对象的属性修改类型原子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | // 受保护的无操作构造方法,供子类使用。 protected AtomicIntFieldUpdater() // 以原子方式将给定值添加到此更新器管理的给定对象的字段的当前值。 int addAndGet(T obj, int delta) // 如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段设置为给定的更新值。 abstract boolean compareAndSet(T obj, int expect, int update) // 以原子方式将此更新器管理的给定对象字段当前值减 1。 int decrementAndGet(T obj) // 获取此更新器管理的在给定对象的字段中保持的当前值。 abstract int get(T obj) // 以原子方式将给定值添加到此更新器管理的给定对象的字段的当前值。 int getAndAdd(T obj, int delta) // 以原子方式将此更新器管理的给定对象字段当前值减1。 int getAndDecrement(T obj) // 以原子方式将此更新器管理的给定对象字段的当前值加1。 int getAndIncrement(T obj) // 将此更新器管理的给定对象的字段以原子方式设置为给定值,并返回旧值。 int getAndSet(T obj, int newValue) // 以原子方式将此更新器管理的给定对象字段当前值加 1。 int incrementAndGet(T obj) // 最后将此更新器管理的给定对象的字段设置为给定更新值。 abstract void lazySet(T obj, int newValue) // 为对象创建并返回一个具有给定字段的更新器。 static <U> AtomicIntFieldUpdater<U> newUpdater(Class<U> tclass, String fieldName) // 将此更新器管理的给定对象的字段设置为给定更新值。 abstract void set(T obj, int newValue) // 如果当前值 == 预期值,则以原子方式将此更新器所管理的给定对象的字段设置为给定的更新值。 abstract boolean weakCompareAndSet(T obj, int expect, int update) |
测试例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 | class Workernew { private String name; public volatile int age; public Workernew(String name, int age) { this .name = name; this .age = age; } public String getName() { return name; } public void setName(String name) { this .name = name; } public int getAge() { return age; } public void setAge( int age) { this .age = age; } } public class TestAtomicIntegerFieldUpdater { public static void main(String[] args) { AtomicIntegerFieldUpdater<Workernew> atomicIntegerFieldUpdater = AtomicIntegerFieldUpdater.newUpdater(Workernew. class , "age" ); Workernew worker = new Workernew( "wangwu" , 30 ); System.out.println(atomicIntegerFieldUpdater.getAndIncrement(worker)); System.out.println(atomicIntegerFieldUpdater.get(worker)); } } 输出: 30 31 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | public final class Unsafe { private static final Unsafe theUnsafe; public static final int INVALID_FIELD_OFFSET = - 1 ; private static native void registerNatives(); // 构造函数是private的,不允许外部实例化 private Unsafe(){ } @CallerSensitive public static Unsafe getUnsafe(){ Class var0 = Reflection.getCallerClass(); // 仅在引导类加载器`BootstrapClassLoader`加载时才合法 if (!VM.isSystemDomainLoader(var0.getClassLoader())){ throw new SecurityException( "Unsafe" ); } else { return theUnsafe; } } } |
那如若想使用这个类,该如何获取其实例?有如下两个可行方案。
1 2 3 4 5 6 7 8 9 10 11 12 | public Unsafe getUnsafe() throws IllegalAccessException { try { Field unsafeField = Unsafe. class .getDeclaredField( "theUnsafe" ); unsafeField.setAccessible( true ); Unsafe unsafe = (Unsafe) unsafeField.get( null ); return unsafe; } catch (Exception e){ log.error(e.getMessage(), e); return null ; } } |
7.1 Unsafe的主要功能
1 2 | public native int getInt(Object var1, long var2); //用于从对象的指定偏移地址处读取一个int public native void putInt(Object var1, long var2, int var4); //用于在对象指定偏移地址处写入一个int |
Unsafe还可以直接在一个地址上读写:
1 2 | public native byte getByte( long var1); //从指定内存地址处开始读取一个byte public native void putByte( long var1, byte var3); //用于从指定内存地址写入一个byte |
1 2 | public native int getIntVolatile(Object var1, long var2); //用于在对象指定偏移地址处volatile读取一个int public native void putIntVolatile(Object var1, long var2, int var4); //用于在对象指定偏移地址处volatile写入一个int |
volatile读写相对普通读写是更加昂贵的,因为需要保证可见性和有序性,而与volatile写入相比putOrderedXX写入代价相对较低,putOrderedXX写入不保证可见性,但是保证有序性,所谓有序性,就是保证指令不会重排序。
1 2 3 | public native void putOrderedObject(Object var1, long var2, Object var4); public native void putOrderedInt(Object var1, long var2, int var4); public native void putOrderedLong(Object var1, long var2, long var4); |
7.5、直接内存操作
Java不可以直接对内存进行操作,对象内存的分配和回收都是由JVM实现的。但是Unsafe为Java中提供了直接操作内存的能力。Unsafe类中提供的3个本地方法allocateMemory、reallocateMemory、freeMemory分别用于分配内存,扩充内存和释放内存。
1 2 3 4 5 6 7 8 9 10 | // 分配内存,相对于C++上的malloc函数 public native long allocateMemory( long var1); // 重新分配内存 public native long reallocateMemory( long var1, long var3); // 内存初始化 public native void setMemory( long var1, long var3, byte var5); // 内存拷贝 public native void copyMemory(Object var1, long var2, Object var4, long var5, long var7); // 清除内存 public native void freeMemory( long var1); |
通常,我们在Java中创建的对象都处于堆内内存(heap)中,堆内内存是由JVM所管控的Java进程内存,并且它们遵循JVM的内存管理机制,JVM会采用垃圾回收机制统一管理堆内存。与之相对的是堆外内存,存在于JVM管控之外的内存区域,Java中对堆外内存的操作,依赖于Unsafe提供的操作堆外内存的native方法。
1 2 3 4 5 6 7 | public native long staticFieldOffset(Field var1); public native long objectFieldOffset(Field var1); public native Object staticFieldBase(Field var1); //返回数组中第一个元素的偏移地址 public native int arrayBaseOffset(Class<?> var1); //返回数组中一个元素占用的大小 public native int arrayIndexScale(Class<?> var1); |
staticFieldOffset方法用于获取静态属性Field在对象中的偏移量,读写静态属性时必须获取其偏移量。objectFieldOffset方法用于获取非静态属性Field在对象实例中的偏移量,读写对象的非静态属性时会用到这个偏移量。staticFieldBase方法用于返回Field所在的对象。arrayBaseOffset方法用于返回数组中第一个元素实际地址相对整个数组对象的地址的偏移量。arrayIndexScale方法用于计算数组中第一个元素所占用的内存空间。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | public class LockSupport { // 唤醒线程 public static void unpark(Thread thread){ if (thread != null ) unsafe.unpark(thread); } // 挂起线程 public static void park(Object blocker) { Thread t = Thread.currentThread(); setBlocker(t, blocker); // 通过Unsafe的putObject方法设置阻塞阻塞当前线程的blocker UNSAFE.park( false , 0L); // 通过Unsafe的park方法来阻塞当前线程,注意此方法将当前线程阻塞后,当前线程就不会继续往下走了,直到其他线程unpark此线程 setBlocker(t, null ); // 清除blocker } public static void parkNanos(Object blocker, long nanos){ if (nanos > 0 ){ Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park( false , nanos); setBlocker(t, null ); } } public static void parkUntil(Object blocker, long deadline) { Thread t = Thread.currentThread(); setBlocker(t, blocker); unsafe.park( true , deadline); setBlocker(t, null ); } public static void park(){ unsafe.park( false , 0L); } public static void parkNanos( long nanos){ if (nanos > 0 ) unsafe.park( false , nanos); } public static void parkUntil( long deadline){ unsafe.park( true , deadline); } } |
park方法和unpark方法相信看过LockSupport类的都不会陌生,这两个方法主要用来挂起和唤醒线程。LockSupport中的park和unpark方法正是通过Unsafe来实现的,Unsafe中的unpark和park如下:
1 2 3 4 5 | public native void unpark(Object var1); public native void park( boolean var1, long var2); public native void monitorEnter(Object var1); public native void monitorExit(Object var1); public native boolean tryMonitorEnter(Object var1); |
如上源码说明中,方法park、unpark即可实现线程的挂起与恢复,将一个线程进行挂起是通过park方法实现的,调用park方法后,线程将一直阻塞直到超时或者中断等条件出现;unpark可以终止一个挂起的线程,使其恢复正常。
1 2 3 4 5 | public native Class<?> defineClass(String var1, byte [] var2, int var3, int var4, ClassLoader var5, ProtectionDomain var6); public native Class<?> defineAnonymousClass(Class<?> var1, byte [] var2, Object[] var3); public native Object allocateInstance(Class<?> var1) throws InstantiationException; public native boolean shouldBeInitialized(Class<?> var1); public native void ensureClassInitialized(Class<?> var1); |
defineClass方法定义一个类,用于动态地创建类。
1 2 3 4 5 6 | //内存屏障,禁止load操作重排序。屏障前的load操作不能被重排序到屏障后,屏障后的load操作不能被重排序到屏障前,保证在这个屏障之前的所有读操作都已经完成。 public native void loadFence(); //内存屏障,禁止store操作重排序。屏障前的store操作不能被重排序到屏障后,屏障后的store操作不能被重排序到屏障前,保证在这个屏障之前的所有写操作都已经完成。 public native void storeFence(); //内存屏障,禁止load、store操作重排序,保证在这个屏障之前的所有读写操作都已经完成。 public native void fullFence(); |
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
· 字符编码:从基础到乱码解决
· 提示词工程——AI应用必不可少的技术