非阻塞同步机制和CAS

46 篇文章 1 订阅


非阻塞同步机制和CAS

我们知道在java 5之前同步是通过Synchronized关键字来实现的,在java 5之后,java.util.concurrent包里面添加了很多性能更加强大的同步类。这些强大的类中很多都实现了非阻塞的同步机制从而帮助其提升性能。

什么是非阻塞同步

非阻塞同步的意思是多个线程在竞争相同的数据时候不会发生阻塞,从而能够在更加细粒度的维度上进行协调,从而极大的减少线程调度的开销,从而提升效率。非阻塞算法不存在锁的机制也就不存在死锁的问题。

在基于锁的算法中,如果一个线程持有了锁,那么其他的线程将无法进行下去。使用锁虽然可以保证对资源的一致性访问,但是在挂起和恢复线程的执行过程中存在非常大的开销,如果锁上面存在着大量的竞争,那么有可能调度开销比实际工作开销还要高。

悲观锁和乐观锁

我们知道独占锁是一个悲观锁,悲观锁的意思就是假设最坏的情况,如果你不锁定该资源,那么就有其他的线程会修改该资源。悲观锁虽然可以保证任务的顺利执行,但是效率不高。

乐观锁就是假设其他的线程不会更改要处理的资源,但是我们在更新资源的时候需要判断该资源是否被别的线程所更改。如果被更改那么更新失败,我们可以重试,如果没有被更改,那么更新成功。

使用乐观锁的前提是假设大多数时间系统对资源的更新是不会产生冲突的。

乐观锁的原子性比较和更新操作,一般都是由底层的硬件支持的。

CAS

大多数的处理器都实现了一个CAS指令(compare and swap),通常来说一个CAS接收三个参数,数据的现值V,进行比较的值A,准备写入的值B。只有当V和A相等的时候,才会写入B。无论是否写入成功,都会返回V。翻译过来就是“我认为V现在的值是A,如果是那么将V的值更新为B,否则不修改V的值,并告诉我现在V的值是多少。”

这就是CAS的含义,JDK中的并发类是通过使用Unsafe类来使用CAS的,我们可以自己构建一个并发类,如下所示:

public class CasCounter {

    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    private volatile int value;

    static {
        try {
            valueOffset = unsafe.objectFieldOffset
                    (CasCounter.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    public CasCounter(int initialValue) {
        value = initialValue;
    }

    public CasCounter() {
    }

    public final int get() {
        return value;
    }

    public final void set(int newValue) {
        value = newValue;
    }

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }

}

上面的例子中,我们定义了一个原子操作compareAndSet, 它内部调用了unsafe的compareAndSwapInt方法。

看起来上面的CAS使用比直接使用锁复杂,但实际上在JVM中实现锁定时需要遍历JVM中一条非常复杂的代码路径,并可能导致操作系统级的锁定,线程挂机和上下文切换等操作。在最好的情况下,锁定需要执行一次CAS命令。

CAS的主要缺点就是需要调用者自己来处理竞争问题(重试,回退,放弃),而在锁中可以自动处理这些问题。

前面的文章我们也讲到了原子变量,原子变量的底层就是使用CAS。

本文的例子请参考https://github.com/ddean2009/learn-java-concurrency/tree/master/CAS

更多精彩内容且看:

更多内容请访问 flydean的博客

  • 4
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
### 回答1: CAS和synchronized都是用于实现多线程同步机制,但它们的实现方式和应用场景有所不同。 CAS(Compare And Swap)是一种乐观锁机制,它通过比较内存中的值和期望值是否相等来判断是否需要更新,如果相等则更新,否则不更新。CAS的优点是无锁,可以避免线程的阻塞和唤醒,适用于并发量较大的情况。 synchronized是一种悲观锁机制,它通过获取锁来保证同一时刻只有一个线程可以执行临界区代码,其他线程需要等待锁的释放才能执行。synchronized的优点是可以保证线程安全,但缺点是会造成线程的阻塞和唤醒,适用于并发量较小的情况。 因此,CAS适用于并发量较大的情况,可以提高程序的性能;而synchronized适用于并发量较小的情况,可以保证程序的正确性。 ### 回答2: CAS(Compare and Swap)和synchronized都属于并发编程中用于实现线程安全的技术,但它们有一些重要的区别。 首先,CAS是一种乐观锁技术,而synchronized是一种悲观锁技术。CAS通过对比内存中的值与期望值,根据比较结果来决定是否更新内存中的值,无锁期间没有线程阻塞。相比之下,synchronized通过获取对象的锁来保证线程的同步执行,如果无法获取锁,线程会进入阻塞状态,直到获取到锁才能继续执行。 其次,CAS是基于底层硬件支持的原子操作指令来实现的,效率更高。而synchronized是基于Java的关键字实现的,需要涉及到用户态和内核态的切换,效率相对较低。 此外,CAS操作可以针对单个变量进行,并且只有在比较结果一致的情况下才会进行更新,适合对于多个线程并发更新同一个变量的情况。而synchronized可以用于对代码块或方法进行同步,可以保护多个线程访问共享资源的一致性。 最后,CAS相对于synchronized更容易产生ABA问题。ABA问题是指在CAS操作中,如果变量的值在操作过程中被其他线程修改过多次,并且最后又变回原来的值,那么CAS操作会错误地认为没有被修改过。为了解决ABA问题,Java提供了AtomicStampedReference和AtomicMarkableReference等类。 总的来说,CAS适用于对单个变量进行高效的并发更新,而synchronized适用于保护共享资源的一致性以及对代码块或方法进行同步。根据实际需求和场景的不同,可以选择合适的技术来实现线程安全。 ### 回答3: CAS(Compare and Swap)和synchronized是两种用于实现线程安全的机制CAS是一种乐观锁机制,它使用原子操作来实现线程安全。CAS包含三个操作数:内存位置V、旧的预期值A和要更新的新值B。当执行CAS操作时,只有在内存位置的值与预期值相等时,CAS才会修改内存位置的值为新值B;否则,不做任何操作。由于CAS操作是原子的,所以能够保证只有一个线程能够成功地修改内存位置的值。但如果多个线程同时执行CAS操作,只有一个线程会成功,其他线程需要重试。 synchronized是一种悲观锁机制,它使用互斥锁来实现线程安全。在使用synchronized关键字修饰的代码块或方法中,同一时刻只有一个线程可以执行该代码块或方法。其他线程需要等待上一个线程执行完毕并释放锁后才能执行。synchronized能够保证代码块或方法的互斥访问,避免了并发访问的问题。但由于每次只能有一个线程执行,其他线程会进入阻塞状态,可能会造成性能问题。 总的来说,CAS是一种非阻塞的乐观锁机制,适用于竞争不激烈的情况,能够提高并发性能。而synchronized是一种阻塞的悲观锁机制,适用于竞争激烈的情况,能够保证线程安全,但可能降低并发性能。在实际开发中,我们需要根据具体的场景选择适合的机制来确保线程安全和并发性能。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

flydean程序那些事

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值