摘要:前言为了研究对原子类的实现,从类开始,分析如果对原子操作的实现。保存着基础数据,使用修饰,可以保证该值对内存可见,也是原子类实现的理论保障。使用自旋锁来处理并发问题。
前言
为了研究Java对原子类的实现,从AtomicInteger类开始,分析Java如果对原子操作的实现。
原子操作是指不会被线程调度机制打断的操作;这种操作一旦开始,就一直运行到结束,中间不会有任何上下文的切换。
注:原子操作可以是一个步骤,也可以是多个操作步骤,但是其顺序不可以被打乱,也不可以被切割只执行其中的一部分。
首先从AtomicInteger类的属性聊起:
// setup to use Unsafe.compareAndSwapInt for updates private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final long valueOffset; private volatile int value;
该类共有三个成员属性。
unsafe:该类是JDK提供的可以对内存直接操作的工具类。
valueOffset:该值保存着AtomicInteger基础数据的内存地址,方便unsafe直接对内存的操作。
value:保存着AtomicInteger基础数据,使用volatile修饰,可以保证该值对内存可见,也是原子类实现的理论保障。
再谈静态代码块(初始化)
try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
该过程实际上就是计算成员变量value的内存偏移地址,计算后,可以更直接的对内存进行操作。
了解核心方法compareAndSet(int expect,int update):
public final boolean compareAndSet(int expect, int update) { return unsafe.compareAndSwapInt(this, valueOffset, expect, update); }
在该方法中调用了unsafe提供的服务:
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
下面看看这个类在JDK中是如何实现的:
jboolean sun::misc::Unsafe::compareAndSwapInt (jobject obj, jlong offset,jint expect, jint update) { jint *addr = (jint *)((char *)obj + offset); //1 return compareAndSwap (addr, expect, update); } static inline bool compareAndSwap (volatile jlong *addr, jlong old, jlong new_val) { jboolean result = false; spinlock lock; //2 if ((result = (*addr == old))) //3 *addr = new_val; //4 return result; //5 }
通过对象地址和value的偏移量地址,来计算value的内存地址。
使用自旋锁来处理并发问题。
比较内存中的值与调用方法时调用方所期待的值。
如果3中的比较符合预期,则重置内存中的值。
如果成功置换则返回true,否则返回false;
综上所述:compareAndSet的实现依赖于两个条件:
volatile原语:保证在操作内存的值时,该值的状态为最新的。(被volatile所修饰的变量在读取值时都会从变量的地址中读取,而不是从寄存器中读取,保证数据对所有线程都是可见的)
Unsafe类:通过该类提供的功能,可以直接对内存进行操作。
了解常见操作getAndIncrement():
return unsafe.getAndAddInt(this, valueOffset, 1); }
同样使用unsafe提供的方法:
public final int getAndAddInt(Object var1, long var2, int var4) { int var5; do { var5 = this.getIntVolatile(var1, var2);//1 } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));//2 return var5; } //getIntVolatile方法native实现 jint sun::misc::Unsafe::getIntVolatile (jobject obj, jlong offset) { volatile jint *addr = (jint *) ((char *) obj + offset); //3 jint result = *addr; //4 read_barrier (); //5 return result; //6 } inline static void read_barrier(){ __asm__ __volatile__("" : : : "memory"); }
通过volatile方法获取当前内存中该对象的value值。
计算value的内存地址。
将值赋值给中间变量result。
插入读屏障,保证该屏障之前的读操作后后续的操作可见。
返回当前内存值
通过compareAndSwapInt操作对value进行+1操作,如果再执行该操作过程中,内存数据发生变更,则执行失败,但循环操作直至成功。
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/70830.html
摘要:前言今天的笔记来了解一下原子操作以及中如何实现原子操作。概念原子本意是不能被进一步分割的最小粒子,而原子操作意为不可被中断的一个或一系列操作。处理器实现原子操作处理器会保证基本内存操作的原子性。 showImg(https://segmentfault.com/img/bVVIRA?w=1242&h=536); 前言 今天的笔记来了解一下原子操作以及Java中如何实现原子操作。 概念 ...
摘要:并发教程原子变量和原文译者飞龙协议欢迎阅读我的多线程编程系列教程的第三部分。如果你能够在多线程中同时且安全地执行某个操作,而不需要关键字或上一章中的锁,那么这个操作就是原子的。当多线程的更新比读取更频繁时,这个类通常比原子数值类性能更好。 Java 8 并发教程:原子变量和 ConcurrentMap 原文:Java 8 Concurrency Tutorial: Synchroni...
摘要:本文探讨并发中的其它问题线程安全可见性活跃性等等。当闭锁到达结束状态时,门打开并允许所有线程通过。在从返回时被叫醒时,线程被放入锁池,与其他线程竞争重新获得锁。 本文探讨Java并发中的其它问题:线程安全、可见性、活跃性等等。 在行文之前,我想先推荐以下两份资料,质量很高:极客学院-Java并发编程读书笔记-《Java并发编程实战》 线程安全 《Java并发编程实战》中提到了太多的术语...
摘要:今天给大家总结一下,面试中出镜率很高的几个多线程面试题,希望对大家学习和面试都能有所帮助。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。使用可以禁止的指令重排,保证在多线程环境下也能正常运行。 下面最近发的一些并发编程的文章汇总,通过阅读这些文章大家再看大厂面试中的并发编程问题就没有那么头疼了。今天给大家总结一下,面试中出镜率很高的几个多线...
阅读 3756·2023-04-25 16:32
阅读 2107·2021-09-28 09:36
阅读 2020·2021-09-06 15:02
阅读 646·2021-09-02 15:21
阅读 902·2019-08-30 15:56
阅读 3493·2019-08-30 15:45
阅读 1696·2019-08-30 13:09
阅读 357·2019-08-29 16:05