专业的JAVA编程教程与资源

网站首页 > java教程 正文

Java实现CAS乐观锁、自旋锁(java自旋锁怎么实现)

temp10 2025-06-23 21:45:48 java教程 2 ℃ 0 评论

介绍CAS操作前,我们先简单看一下乐观锁 与 悲观锁这两个常见的锁概念。

悲观锁:

Java实现CAS乐观锁、自旋锁(java自旋锁怎么实现)

从Java多线程角度,存在着“可见性、原子性、有序性”三个问题,悲观锁就是假设在实际情况中存在着多线程对同一共享的竞争,所以在操作前先占有共享资源(悲观态度)。因此,悲观锁是阻塞,独占的,存在着频繁的线程上下文切换,对资源消耗较大。synchronized就是悲观锁的一种实现。

乐观锁:

  如名一样,每次操作都认为不会发生冲突,尝试执行,并检测结果是否正确。如果正确则执行成功,否则说明发生了冲突,回退再重新尝试。乐观锁的过程可以分为两步:冲突检测 和 数据更新。在Java多线程中乐观锁一个常见实现即:CAS操作。

CAS

CAS,(Compare-And-Swap,比较和替换)。其具有三个操作数:内存地址V,旧的预期值A,新的预期值B。当V中的值和A相同时,则用新值B替换V中的值,否则不执行更新。(PS:上述的操作是原子性的,因为过程是:要么执行更新,要么不更新)

新建一个接口,统一加锁入口:

public interface MyCAS {
    void doSomething();
}

加锁工具类:

import java.util.concurrent.atomic.AtomicReference;

public class MyCASUtil {
    //封装为原子类
    private static final AtomicReference<Thread> atomicThread = new AtomicReference<Thread>();

    //执行类
    public static void doSomethingCAS(MyCAS myCAS) {
        lock();
        myCAS.doSomething();
        unLock();
    }

    //前置方法 加锁
    private static void lock() {
        //获取当前线程
        Thread thread = Thread.currentThread();
        while (!atomicThread.compareAndSet(null, thread)){
            //稍微一等 节省cpu开销
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    //后置方法  解锁
    private static void unLock() {
        Thread thread = Thread.currentThread();
        atomicThread.compareAndSet(thread, null);
    }
}

其中的compareAndSet()方法是一个原子操作, 当current值符合expect时,用next替换了current。如果不符合,则在循环中不断尝试,直到成功为止。

在这个例子中我们要怎么使用呢? 我们可以新建一个类实现MyCAS这个接口,把需要加锁的代码放在方法doSomething中,使用方法举例:

public class MyCASTest implements MyCAS {

    private int val;

    MyCASTest(int val) {
        this.val = val;
    }

    public void doSomething() {
        System.out.println(val);
        try {
            Thread.sleep(100);
            val++;
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

    }
}

测试:

public class MainTest {
    public static void main(String[] args) {
        final MyCAS casTest = new MyCASTest(0);

        ExecutorService executor = newFixedThreadPool(10);
        for (int i = 0; i < 100;i++) {
            executor.execute(new Runnable() {
                public void run() {
              	    //加锁的情况
                    MyCASUtil.doSomethingCAS(casTest);
                    //不加锁的情况
                    //casTest.doSomething();
                }
            });
        }
        executor.shutdown();
    }
}

CAS存在的问题

1. ABA问题

 ABA问题值,内存地址V的值是A,线程one从V中取出了A,此时线程two也从V中取出了A,同时将A修改为B,但是又因为一些原因修改为A。而此时线程one仍看到V中的值为A,认为没有发生变化,此为ABA问题。解决ABA问题一种方式是通过版本号(version)。每次执行数据修改时,都需要带上版本号,如:1A,2B,3A。通过比较版本号可知是否有发生过操作,也就解决了ABA问题。在我们这个例子中并不存在ABA问题。  

2. 未知的等待时长

  因为CAS采取失败重试的策略,所以不确定会发生多少次循环重试。如果在竞争激烈的环境下,其重试次数可能大幅增加。此时效率也就降低了。

Tags:

本文暂时没有评论,来添加一个吧(●'◡'●)

欢迎 发表评论:

最近发表
标签列表