专注于 JetBrains IDEA 全家桶,永久激活,教程
持续更新 PyCharm,IDEA,WebStorm,PhpStorm,DataGrip,RubyMine,CLion,AppCode 永久激活教程

Java多线程系列之CAS及延伸

CAS

CAS(Compare and swap || Compare and exchange)比较交换,属于乐观锁的一种实现,当操作失败时并不会被操作系统挂起,而是会再次发起请求直到成功,CAS是天生免疫死锁的

看jdk中一个使用CAS的栗子

/**
 * @Author tan
 **/
public class Test {
    # 自增 AtomicInteger是jdk1.5出现的基于CAS的原子变量类
    public static void main(String[] args) {
        AtomicInteger i = new AtomicInteger();
        i.incrementAndGet();
    }
}

利用IDE查看源码

# Unsafe.class
 public final int getAndAddInt(Object var1, long var2, int var4) {
        int var5;
        do {
            var5 = this.getIntVolatile(var1, var2);
        } while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));

        return var5;
    }

主要是代码中的compareAndSwapInt,compareAndSwapInt其实是一个native的实现,简而言之就是java里面调c++或者其他编程语言

 public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);

介绍

CAS 是一种有名的无锁算法,CAS是一种CPU级别的指令,在大多数处理器架构,包括IA32、Space中采用的都是CAS指令。顾名思义这个算法分为两部分,比较和交换,一共有三个数据:

  • V表示准备要被更新的变量,volatile修饰保证可见性
  • E表示我们提供的 期望的值
  • N表示新值 ,准备更新V的值

56_1.png

本质实现

CAS在底层上存在一条硬件级别的汇编指令

# cmpxchg指令是非原子性的 其原子性体现在lock,lock指令在执行时
# 锁定了一个北桥信号,并不采用锁总线的信号方式
lock cmpxchg

ABA问题及其解决方案

在CAS的核心算法中,通过死循环不断获取最新的E。如果在此之间,V被修改了两次,但是最终值还是修改成了旧值V,这个时候,就不好判断这个共享变量是否已经被修改过。为了防止这种不当写入导致的不确定问题,原子操作类提供了一个带有时间戳的原子操作类 AtomicStampedReferenc 看网上一个实例代码:

public class ABA {
    private static AtomicInteger atomicInt = new AtomicInteger(100);
    private static AtomicStampedReference<Integer> atomicStampedRef = 
            new AtomicStampedReference<Integer>(100, 0);

    public static void main(String[] args) throws InterruptedException {
        Thread intT1 = new Thread(new Runnable() {
            @Override
            public void run() {
                atomicInt.compareAndSet(100, 101);
                atomicInt.compareAndSet(101, 100);
            }
        });
        Thread intT2 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                boolean c3 = atomicInt.compareAndSet(100, 101);
                System.out.println(c3);        //true
            }
        });
        intT1.start();
        intT2.start();
        intT1.join();
        intT2.join();
        Thread refT1 = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    TimeUnit.SECONDS.sleep(1);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                atomicStampedRef.compareAndSet(100, 101, 
                        atomicStampedRef.getStamp(), atomicStampedRef.getStamp()+1);
                atomicStampedRef.compareAndSet(101, 100, 
                        atomicStampedRef.getStamp(), atomicStampedRef.getStamp()+1);
            }
        });
        Thread refT2 = new Thread(new Runnable() {
            @Override
            public void run() {
                int stamp = atomicStampedRef.getStamp();
                System.out.println("before sleep : stamp = " + stamp);    // stamp = 0
                try {
                    TimeUnit.SECONDS.sleep(2);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("after sleep : stamp = " + atomicStampedRef.getStamp());//stamp = 1
                boolean c3 = atomicStampedRef.compareAndSet(100, 101, stamp, stamp+1);
                System.out.println(c3);        //false
            }
        });

        refT1.start();
        refT2.start();
    }
    # 输出如下:
    true
    before sleep : stamp = 0
    after sleep : stamp = 2
    false

查看对象内存布局小工具

Jot

  #  https://mvnrepository.com/artifact/org.openjdk.jol/jol-core
        <dependency>
            <groupId>org.openjdk.jol</groupId>
            <artifactId>jol-core</artifactId>
            <version>0.10</version>
        </dependency>

        Object o = new Object();
        System.out.println(ClassLayout.parseInstance(o).toPrintable());
        System.out.println("-----------------------------------------");
        o = "谭婧杰";
        System.out.println(ClassLayout.parseInstance(o).toPrintable());

56_2.png

对象在内存中的存储布局

56_3.png

java -XX:+PrintCommandLineFlags -version
# initialHeapSize -> 起始堆大小
# MaxHeapSize -> 最大堆大小
# useCompressedClassPointers - > 压缩指针
# useComressedOops -> 普通对象指针

56_4.png

按照上面的例子,给o加锁

synchronized (o) {
    System.out.println(ClassLayout.parseInstance(o).toPrintable());
}

56_5.png

由图可知,关于锁的信息一般都是记录在对象头里

Synchroized

Synchroized加锁

1、 修饰实例方法,作用于当前实例加锁,进入同步代码前 要获得当前实例的锁
2、 静态方法,作用于当前类对象加锁,进入同步代码前要 获得当前类对象的锁
3、 修饰代码块,指定加锁对象,对给定对象加锁,进入同 步代码库前要获得给定对象的锁。

4种锁状态,级别由低到高依次为:无锁状态、偏向锁状态、轻量级锁状态、重量级锁状态。这几个状态会随着竞争情况逐渐升级

文章永久链接:https://tech.souyunku.com/19690

未经允许不得转载:搜云库技术团队 » Java多线程系列之CAS及延伸

JetBrains 全家桶,激活、破解、教程

提供 JetBrains 全家桶激活码、注册码、破解补丁下载及详细激活教程,支持 IntelliJ IDEA、PyCharm、WebStorm 等工具的永久激活。无论是破解教程,还是最新激活码,均可免费获得,帮助开发者解决常见激活问题,确保轻松破解并快速使用 JetBrains 软件。获取免费的破解补丁和激活码,快速解决激活难题,全面覆盖 2024/2025 版本!

联系我们联系我们