java.util.concurrent.atomic
i++
(基本数据类型)public class CaseDemo {
//不使用原子类(CAS)
volatile int number = 0;
//读数据
public int getNumber(){
return number;
}
//写数据,加锁,保证数据原子性
public synchronized void setNumber(){
number++;
}
}
//CAS出现以后
AtomicInteger atomicInteger = new AtomicInteger();
public int getAtomicInteger(){
return atomicInteger.get();
}
public void setAtomicInteger(){
//获取并执行i++
atomicInteger.getAndIncrement();
}
public static void main(String[] args) {
AtomicInteger atomicInteger = new AtomicInteger(5);
System.out.println(atomicInteger.compareAndSet(5, 2024) + "\t" + atomicInteger.get());
System.out.println(atomicInteger.compareAndSet(5, 2024) + "\t" + atomicInteger.get());
atomicInteger.getAndIncrement();
}
public final native boolean compareAndSwapObject(Object var1, long var2, Object var4, Object var5);
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
以上三个方法均可类比,主要对 4 个参数做简要说明
UNSAFE_ENTRY(jboolean,Unsafe_CompareAndSwapInt(
JNIEnv *env,jobject unsafe,jobject obj,jlong offset,jint e,jint x))
UnsafeWrapper("Unsafe_CompareAndSwapInt");
oop p = JNIHandles::resolve(obj);
//获取变量value在内存中的地址,根据偏移量valueOffset,计算value的地址
jint *addr = (jint *)index_oop_from_field_offset_long(p,offset);
/*
调用Atomic中的函数cmpxchg进行比较交换,参数x是要交换的值,e是要比较的值
cas成功,返回期望值e,等于e,此方法返回true
cas失败,返回内存中的value值,不等于e,此方法返回false
*/
return(jint)(Atomic::cmpxchg(x,addr,e)==e);
UNSAFE_END
JDK 提供的 CAS 机制,在汇编层级会禁止变量两侧的指令优化,然后使用 cmpxchg 指令比较并更新变量值(原子性)
(Atomic::cmpxchg(x,addr,e))==e;
调用Atomic中的函数cmpxchg进行比较交换,参数x是要交换的值,e是要比较的值
return(jint)(Atomic::cmpxchg(x,addr,e)==e);
unsigned Atomic ::cmpxchg(unsigned int exchange_value,
volatile unsigned int* dest,unsigned int compare_value)
{
assert(sizeof(unsigned int)==sizeof(jint),"more work to do")
//根据OS类型调用不同平台的重载函数,这个在预编译期间编译器会决定使用哪个平台下的重载函数
return (unsigned int)Atomic::cmpxchg((jint)exchange_value,(volatile jint*)dest,(jint)compare_value);
}
inline jint Atomic::cmpxchg (int exchange_value, volatile jint* dest,jint compare_value){
//判断是否是多核cpu
int mp os::is _MP();
_asm{
//三个move指令表示的是将后面的值移动到前面的寄存器上
mov edx, dest
mov ecx,exchange_value
mov eax, compare_value
//cpu原语级别,cpu触发
LOCK_IF_MP(mp)
//比较并交换指令
//cmpxchg:即"比较并交换"指令
//dword:全称是doubleword表示两个字,一共四个字节
//ptr:全称是pointer,与前面的dword连起来使用,表明访问的内存单元
//将eax寄存器中的值(compare_value)与[edx]双字内存单元中的值进行对比。
cmpxchg dword ptr[edx],ecx
}
}
只需要记住:CAS是靠硬件实现的从而在硬件层面提升效率,最底层还是交给硬件来保证原子性和可见性实现方式是基于硬件平台的汇编指令,在intel的CPU中(X86机器上),使用的是汇编指令cmpxchg指令。 核心思想就是:比较要更新变量的值V和预期值E(compare),相等才会将V的值设为新值N(swap)如果不相等自旋再来。
基于原子引用实现自定义原子类 example:AtomicBook,AtomicOrder,etc…
AtomicReference<User> atomicReference = new AtomicReference<>();
User z3 = new User("z3", 22);
User li4 = new User("li4", 28);
atomicReference.set(z3);
System.out.println(atomicReference.compareAndSet(z3, li4) +
"\t" + atomicReference.get().toString());
System.out.println(atomicReference.compareAndSet(z3, li4) +
"\t" + atomicReference.get().toString());
CAS 是实现自旋锁的基础,CAS 利用 CPU 指令保证了操作的原子性,以达到锁的效果,自旋,是指尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁 当线程发现锁被占用时,会不断循环判断锁的状态,直到获取这样的好处在于减少了线程上下文切换的消耗,弊端则是会消耗 CPU
/**
* description :
* 实现一个自旋锁,复习CAS
* 自旋锁的优点 : 循环比较没有类似wait的阻塞
* 通过CAS操作完成自旋锁,A线程先进来调用myLock方法将自己持有锁5秒,B随后进来后发现,
* 但前线程持有锁,因此自能通过自旋等待,知道A释放锁后B随即抢到
*
* @param args
*/
AtomicReference<Thread> atomicReference = new AtomicReference<>();
public void lock() {
Thread thread = Thread.currentThread();
System.out.println(Thread.currentThread().getName() + "\t" + "--come in");
while (!atomicReference.compareAndSet(null, thread)) {
}
}
public void unlock() {
Thread thread = Thread.currentThread();
atomicReference.compareAndSet(thread, null);
System.out.println(Thread.currentThread().getName() + "\t" + "--task over unlock");
}
public static void main(String[] args) {
SpinLockDemo spinLockDemo = new SpinLockDemo();
new Thread(() -> {
spinLockDemo.lock();
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
spinLockDemo.unlock();
}, "A").start();
//保证A先于B启动
try {
TimeUnit.MICROSECONDS.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
new Thread(() -> {
spinLockDemo.lock();
spinLockDemo.unlock();
}, "B").start();
}
版本号时间戳原子引用
public static void main(String[] args) {
Book spring = new Book(1, "spring");
AtomicStampedReference<Book> bookAtomicStampedReference = new AtomicStampedReference<>(spring, 1);
System.out.println(bookAtomicStampedReference.getReference() + "\t" + bookAtomicStampedReference.getStamp());
Book juc = new Book(1, "juc");
boolean b = bookAtomicStampedReference.compareAndSet(spring,
juc,
bookAtomicStampedReference.getStamp(),
bookAtomicStampedReference.getStamp() + 1);
System.out.println(b + "\t" + bookAtomicStampedReference.getReference() + "\t" + bookAtomicStampedReference.getStamp());
b = bookAtomicStampedReference.compareAndSet(
juc, spring,
bookAtomicStampedReference.getStamp(),
bookAtomicStampedReference.getStamp() + 1);
System.out.println(b + "\t" + bookAtomicStampedReference.getReference() + "\t" + bookAtomicStampedReference.getStamp());
}
static AtomicInteger atomicInteger = new AtomicInteger(100);
public static void main(String[] args) {
new Thread(()->{
atomicInteger.compareAndSet(100,101);
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
atomicInteger.compareAndSet(101,100);
},"t1").start();
new Thread(()->{
try {
TimeUnit.MILLISECONDS.sleep(200);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(atomicInteger.compareAndSet(100, 2024) + "\t" + atomicInteger.get());
},"t2").start();
}
static AtomicInteger atomicInteger = new AtomicInteger(100);
static AtomicStampedReference<Integer> stampedReference = new AtomicStampedReference<>(100, 1);
public static void main(String[] args) {
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "首次版本号" + stamp);
//暂停500毫秒,保证后面的t4线程初始化拿到版本与t3一致
try {
TimeUnit.MICROSECONDS.sleep(500);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
stampedReference.compareAndSet(100, 101, stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "第2次流水" + stampedReference.getStamp());
stampedReference.compareAndSet(101, 100, stampedReference.getStamp(), stampedReference.getStamp() + 1);
System.out.println(Thread.currentThread().getName() + "第3次流水" + stampedReference.getStamp());
}, "t3").start();
new Thread(() -> {
int stamp = stampedReference.getStamp();
System.out.println(Thread.currentThread().getName() + "首次版本号" + stamp);
//暂停1秒钟,等待上面的t3线程发生ABA问题
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
boolean b = stampedReference.compareAndSet(100, 2024, stamp, stamp + 1);
System.out.println(b+"\t"+stampedReference.getReference()+"\t"+stampedReference.getStamp());
}, "t4").start();