您现在的位置是:主页 > news > 专业网站设计公司/舆情分析网站免费
专业网站设计公司/舆情分析网站免费
admin2025/4/29 14:56:33【news】
简介专业网站设计公司,舆情分析网站免费,汉川建设局网站,蚌埠做企业网站线程安全 什么是线程安全呢? 当多个线程并发访问某个java对象时,无论系统如何调度这些线程,也无论这些线程将如何交替操作,这个对象都能表现出一致的、正确的行为,那么对这个对象的操作就是线程安全的。 Atomic介绍 …
线程安全
什么是线程安全呢? 当多个线程并发访问某个java对象时,无论系统如何调度这些线程,也无论这些线程将如何交替操作,这个对象都能表现出一致的、正确的行为,那么对这个对象的操作就是线程安全的。
Atomic介绍
Atomic 的中文翻译是不可中断的操作;不可中断操作就是指即使在多个线程一起执行Atomic类型操作的时候,一旦操作开始,就不会被其他线程中断。所以Atomic类指的就是具有原子操作特性的类。
atomic原子类位于JUC并发包中,文件路径为java.util.concurrent.atomic,如图:
根据操作的目标数据类型, JUC包下的原子类可以分为4类:
- 基本原子类:AtomicInteger、AtomicLong、AtomicBoolean
- 数组原子类:AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
- 原子引用类型:AtomicReference、AtomicMarkableReference
- 字段更新类型:AtomicIntegerFieldUpdater、AtomicLongFieldUpdater、AtomicReferenceFieldUpdater
CAS 原子
CAS(Compare and Swap ,比较并交换),通常指的是一种原子操作:针对一个变量,首先比较它的内存值与某个期望值是否相同,如果相同,就给它赋一个新值。
CAS是一种无锁算法,CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B;CAS的伪代码可以这样表示:
do{获得字段的期望值(oldValue)计算出需要替换的新值(newValue)
} while(!(CAS(内存地址,oldValue,newValue))
sum.misc.Unsafe 详解
并发工具、并发容器等在其底层都依赖于一个特殊类sun.misc.Unsafe,该类可以直接对内存进行相关的操作,主要提供一些用于执行低级别、不安全的底层操作,可以通过汇编指令直接进行CPU的操作,如直接访问系统内存的资源、自主管理内存资源等。 Unsafe的大量方法都是native方法。
虽然使用Unsafe的方法可以提升java运行效率、增强java语言底层资源操作能力方面起到很大的作用,但是由于Unsafe类可以像C语言一样使用指针操作内存空间,无疑增加了指针相关的问题,内存泄漏问题出现的概率,所以一般的应用开发都不会涉及此类,java官方也不建议直接在应用程序中使用此类。
Unsafe 使用了单例模式的懒汉式方法来实现代码。
private static final Unsafe theUnsafe = new Unsafe();public static Unsafe getUnsafe() {return theUnsafe;
}
完成java应用层的CAS操作主要涉及Unsafe方法的调用,具体如下:
(1) 获取Unsafe实例
Unsafe类是一个final修饰的不允许继承的最终类,而且其构造函数是private类型的方法,如下所示:
public final class Unsafe {private Unsafe() {}
}
因此我们无法在外部对Unsafe进行实例化,可以通过反射的方式自定义地 获取unsafe实例的辅助方法,代码如下:
public static Unsafe getUnsafe() {try {Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe");theUnsafe.setAccessible(true);return (Unsafe) theUnsafe.get(null);} catch (Exception e) {throw new AssertionError(e);}}
(2)调用Unsafe提供的CAS方法,这些方法主要封装了底层CPU的CAS原子操作。
Unsafe 提供的CAS方法包含4个操作数-字段所在的对象、字段内存位置、预期原值,新值;在执行Unsafe的CAS方法时,这些方法首先将内存位置的值与预期值比较,如果相匹配,那么CPU 会自动将该内存位置的值更新为新值,并返回true,如果不匹配,CPU不做任何操作,并返回false。部分源码如下:
/*
* 比价并交换的原子方法* @param o 需要操作的字段所在的对象* @param offset 需要操作的字段的偏移量(相对的,相对于对象头)* @param expected 期望值(旧的值)* @param x 更新值(新的值)* @return {@code true} 如果成功返回true*/
public final native boolean compareAndSetInt(Object o, long offset, int expected, int x);
public final native boolean compareAndSetObject(Object o, long offset, Object expected,Object x);
public final boolean compareAndSetShort(Object o, long offset, short expected,short x) ;
Unsafe的CAS操作会将第一个参数(对象的指针、地址)与第二个参数(字段偏移量)组合在一起,计算出最终的内存操作地址。
(3)调用Unsafe提供的字段偏移量方法,这些方法用于获取对象中的字段偏移量,此偏移量值需要作为参数提供给CAS操作。
Unsafe 提供的获取字段(属性)偏移量的相关操作主要如下:
/*
* 获取静态属性Field在Class对象中的偏移量,在CAS中操作静态属性时会用到这个偏移量。
*@param f 需要操作字段的反射
*@return 字段的偏移量
/
public long staticFieldOffset(Field f);
/*
* 获取非静态Field(非静态属性)在Object实例中的偏移量,在CAS中操作对象的非静态属性时会用到这个偏移量。
*@param f 需要操作字段的反射
*@return 字段的偏移量
/
public long objectFieldOffset(Field f);
##以 AtomicInteger为例
与Integer一样,AtomicInteger继承number类,作为Number类的一个子类,相比Integer类,AtomicInteger还提供了很多原子性的操作方法,
AtomicInteger主要通过CAS自旋 保障变量操作的原子性,volatile关键字保障变量的可见性,CAS+Volatile的方案实现,既保障了变量操作的线程安全性,又避免了synchronized重量级的高开销,使得java程序的执行效率大大提升。
在AtomicInteger的内部,有一个被volatile关键字修饰的的成员变量value,volatile 关键字可以保证任何线程在任何时刻总能拿到该变量的最新值,其目的在于保障变量值的线程可见性。
//内部value值,使用volatile 保证线程的可见性private volatile int value;
实际上,AtomicInteger所提供的的所有方法主要是针对该变量value进行的操作。具体操作如下:
/*
*初始化
*/
public AtomicInteger(int initialValue) {value = initialValue;}/*
* 获取当前value值
*/public final int get() {return value;}/** 设置值*/public final void set(int newValue) {value = newValue;}
在AtomicInteger 类中实例化了一个Unsafe类, AtomicInteger 中原子性的操作方法是通过CAS自旋的方式实现的, 通过调用Unsafe的方法,例如:
/*
* 原子性++
*/
public final int getAndIncrement() {return U.getAndAddInt(this, VALUE, 1);
}
/*
* 原子性---
*/
public final int getAndDecrement() {return U.getAndAddInt(this, VALUE, -1);}
接着来看下Unsafe的getAndAddInt()的源码实现如下:
public final int getAndAddInt(Object o, long offset, int delta) {int v;do {v = getIntVolatile(o, offset);} while (!weakCompareAndSetInt(o, offset, v, v + delta));return v;}
从源码可知,getAndAddInt()方法使用CAS进行无锁编程的实现的,先通过Object对象和Object对象属性的地址偏移量获取最新的地址偏移量值,然后进行CAS 操作,
public final boolean weakCompareAndSetInt(Object o, long offset,int expected,int x) {return compareAndSetInt(o, offset, expected, x);
}
public final native boolean compareAndSetInt(Object o, long offset,int expected,int x);
通过一步步代码深入,最终调用的是JNI方法, AtomicInteger源码中的主要方法都是通过CAS自旋实现的。CAS自旋的主要操作为:如果一次CAS操作失败,获取最新的value值后,再次进行CAS操作,直到成功。
总结:Atomic基础原子类是通过CAS自旋+volatile的方案实现,既保障了变量操作的线程安全性,又避免了synchronized重量级锁的高开销。