`

java AtomicInteger基于CAS的乐观锁实现

 
阅读更多
1. 悲观锁与乐观锁

   我们都知道,cpu是时分复用的,也就是把cpu的时间片,分配给不同的thread/process轮流执行,时间片与时间片之间,需要进行cpu切换,也就是会发生进程的切换。切换涉及到清空寄存器,缓存数据。然后重新加载新的thread所需数据。当一个线程被挂起时,加入到阻塞队列,在一定的时间或条件下,在通过notify(),notifyAll()唤醒回来。在某个资源不可用的时候,就将cpu让出,把当前等待线程切换为阻塞状态。等到资源(比如一个共享数据)可用了,那么就将线程唤醒,让他进入runnable状态等待cpu调度。这就是典型的悲观锁的实现。独占锁是一种悲观锁,synchronized就是一种独占锁,它假设最坏的情况,并且只有在确保其它线程不会造成干扰的情况下执行,会导致其它所有需要锁的线程挂起,等待持有锁的线程释放锁。

     但是,由于在进程挂起和恢复执行过程中存在着很大的开销。当一个线程正在等待锁时,它不能做任何事,所以悲观锁有很大的缺点。举个例子,如果一个线程需要某个资源,但是这个资源的占用时间很短,当线程第一次抢占这个资源时,可能这个资源被占用,如果此时挂起这个线程,可能立刻就发现资源可用,然后又需要花费很长的时间重新抢占锁,时间代价就会非常的高。

     所以就有了乐观锁的概念,他的核心思路就是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。在上面的例子中,某个线程可以不让出cpu,而是一直while循环,如果失败就重试,直到成功为止。所以,当数据争用不严重时,乐观锁效果更好。比如CAS就是一种乐观锁思想的应用。

2.java中CAS的实现

     CAS就是Compare and Swap的意思,比较并操作。很多的cpu直接支持CAS指令。CAS是项乐观锁技术,当多个线程尝试使用CAS同时更新同一个变量时,只有其中一个线程能更新变量的值,而其它线程都失败,失败的线程并不会被挂起,而是被告知这次竞争中失败,并可以再次尝试。CAS有3个操作数,内存值V,旧的预期值A,要修改的新值B。当且仅当预期值A和内存值V相同时,将内存值V修改为B,否则什么都不做。

     JDK1.5中引入了底层的支持,在int、long和对象的引用等类型上都公开了CAS的操作,并且JVM把它们编译为底层硬件提供的最有效的方法,在运行CAS的平台上,运行时把它们编译为相应的机器指令。在java.util.concurrent.atomic包下面的所有的原子变量类型中,比如AtomicInteger,都使用了这些底层的JVM支持为数字类型的引用类型提供一种高效的CAS操作。

      在CAS操作中,会出现ABA问题。就是如果V的值先由A变成B,再由B变成A,那么仍然认为是发生了变化,并需要重新执行算法中的步骤。有简单的解决方案:不是更新某个引用的值,而是更新两个值,包括一个引用和一个版本号,即使这个值由A变为B,然后为变为A,版本号也是不同的。AtomicStampedReference和AtomicMarkableReference支持在两个变量上执行原子的条件更新。AtomicStampedReference更新一个“对象-引用”二元组,通过在引用上加上“版本号”,从而避免ABA问题,AtomicMarkableReference将更新一个“对象引用-布尔值”的二元组。

3.AtomicInteger的实现

     AtomicInteger 是一个支持原子操作的 Integer 类,就是保证对AtomicInteger类型变量的增加和减少操作是原子性的,不会出现多个线程下的数据不一致问题。如果不使用 AtomicInteger,要实现一个按顺序获取的 ID,就必须在每次获取时进行加锁操作,以避免出现并发时获取到同样的 ID 的现象。



接下来通过源代码来看AtomicInteger具体是如何实现的原子操作。

首先看incrementAndGet() 方法,下面是具体的代码。

public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }


通过源码,可以知道,这个方法的做法为先获取到当前的 value 属性值,然后将 value 加 1,赋值给一个局部的 next 变量,然而,这两步都是非线程安全的,但是内部有一个死循环,不断去做compareAndSet操作,直到成功为止,也就是修改的根本在compareAndSet方法里面,compareAndSet()方法的代码如下:

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


compareAndSet()方法调用的compareAndSwapInt()方法的声明如下,是一个native方法。

  
publicfinal native boolean compareAndSwapInt(Object var1, long var2, int var4, intvar5);


   compareAndSet 传入的为执行方法时获取到的 value 属性值,next 为加 1 后的值, compareAndSet所做的为调用 Sun 的 UnSafe 的 compareAndSwapInt 方法来完成,此方法为 native 方法,compareAndSwapInt 基于的是CPU 的 CAS指令来实现的。所以基于 CAS 的操作可认为是无阻塞的,一个线程的失败或挂起不会引起其它线程也失败或挂起。并且由于 CAS 操作是 CPU 原语,所以性能比较好。

     类似的,还有decrementAndGet()方法。它和incrementAndGet()的区别是将 value 减 1,赋值给next 变量。

     AtomicInteger中还有getAndIncrement() 和getAndDecrement() 方法,他们的实现原理和上面的两个方法完全相同,区别是返回值不同,前两个方法返回的是改变之后的值,即next。而这两个方法返回的是改变之前的值,即current。还有很多的其他方法,就不列举了。

转自:http://www.mamicode.com/info-detail-862009.html


分享到:
评论
发表评论

文章已被作者锁定,不允许评论。

相关推荐

    java乐观锁

    Java的`java.util.concurrent.atomic`包下提供了很多基于CAS的原子类,如AtomicInteger、AtomicLong等。 乐观锁的优势在于减少锁的使用,避免了线程阻塞,提高了并发性能。但是,如果数据冲突频繁,乐观锁会导致...

    JAVA CAS实现原理与使用.docx

    在Java中,`java.util.concurrent.atomic`包下的原子类,如`AtomicInteger`,就是基于CAS实现的。 以`AtomicInteger`的`incrementAndGet()`方法为例,它用于实现无锁的自增操作。在循环中,线程首先获取当前值,...

    乐观锁的一种实现方式-CAS编程开发技术共4页.pdf

    CAS(Compare and Swap,比较并交换)是乐观锁的一种常见实现机制,尤其在多线程和并发编程中被广泛应用。它是一个原子操作,由硬件支持,可以无锁地完成对内存位置的更新。CAS包含三个参数:旧值、预期值和新值。当...

    乐观锁测试代码

    在Java开发中,乐观锁常见的实现方式有以下几种: 1. **版本号机制**:每个记录都有一个版本号字段,每次更新时都会将版本号加一。当尝试更新时,如果读取到的版本号与数据库中的版本号不一致,就认为数据已被其他...

    彻底理解Java中的各种锁.pdf

    在Java中,乐观锁通常是通过CAS(Compare-And-Swap)实现,例如Java中的AtomicInteger类就使用了这种方式。悲观锁则假设最坏的情况,即每次读取数据都认为会被其他线程修改,所以总是对数据进行加锁。synchronized...

    JAVA架构面试专题_面试必备之乐观锁与悲观锁.pdf

    在Java中,`java.util.concurrent.atomic`包下的原子变量类如AtomicInteger、AtomicLong等,使用了CAS算法来实现乐观锁。 **版本号机制**是乐观锁的一种实现方式,通过添加一个版本号字段来跟踪数据的修改。当读取...

    Java并发问题之乐观锁与悲观锁

    在这里,我们可以看到 AtomicInteger 使用了 CAS 操作来实现乐观锁。compareAndSet 方法就是使用 CAS 操作来更新变量的值。如果更新成功,那么返回 true,否则返回 false。 在 Java 中,java.util.concurrent 包...

    深入分析Java并发编程之CAS

    这种乐观锁策略假设并发冲突较少,可以减少锁带来的同步开销。在Java中,CAS操作是通过`Unsafe`类实现的,这个类位于`sun.misc`包下,提供了对内存的直接访问和原子操作。 `Unsafe`类是一个非常强大的工具,但同时...

    Java 多线程与并发(3-26)-Java 并发 - Java中所有的锁.pdf

    在Java中,常见的乐观锁实现是无锁编程,如使用原子类(AtomicInteger、AtomicLong等)中的CAS(Compare and Swap)算法。悲观锁则假设在读取数据时会有其他线程修改,所以在读取时即加锁,如`synchronized`关键字和...

    Java CAS基本实现原理代码实例解析

    在 Java 中,CAS 是通过乐观锁来实现的,即在读取数据时,不会加锁,而是在写回数据时,检查数据是否被修改,如果被修改,则重试写回。CAS 的优点是可以避免加锁和解锁的开销,从而提高并发性能。 在 Java 中,有...

    全面了解Java中的CAS机制

    CAS(Compare and Swap)机制是Java中的一种乐观锁思想的应用,用于解决多线程并发访问共享资源的问题。CAS机制的核心思想是,每次不加锁而是假设没有冲突而去完成某项操作,如果因为冲突失败就重试,直到成功为止。...

    java锁各种核心整理

    在Java中,乐观锁通常是通过CAS(Compare-And-Swap)操作来实现的,CAS是一种无锁的原子操作,它比较内存中的值与预期值是否相同,如果相同则更新,否则不执行任何操作,因此在多线程环境下可以保证数据的一致性。...

    基于Java+MySQL设计与实现的秒杀与抢购模型架构【100013279】

    2.MySQL加字段version实现乐观锁。 3.基于AtomicInteger的CAS机制; 4.使用Redis作为原子计数器(watch事务+decr操作),RabbitMQ作为消息队列记录用户抢购行为,MySQL做异步存储。 上述四个解决方案均使用了JMeter...

    Java中的锁分类与使用.docx

    Java中,`java.util.concurrent.atomic`包下的原子变量类如AtomicInteger、AtomicLong等利用CAS(Compare and Swap)算法实现了乐观锁。 - **悲观锁**假设并发环境下数据频繁被修改,因此在读取时就会上锁,确保...

    Java并发——无锁实现

    在Java并发编程中,无锁实现是一个高级技术,它可以让多个线程在没有使用传统锁机制(如synchronized关键字或显示锁Lock)的情况下,安全地执行对共享资源的操作。无锁机制主要依赖于硬件的原子指令,尤其是比较并...

    java中的各种锁详细介绍.docx

    Java中的乐观锁主要通过无锁编程实现,如使用`java.util.concurrent.atomic`包中的原子类,这些类利用了**CAS(Compare And Swap)**算法来保证操作的原子性。CAS操作包括三个参数:当前内存值V、预期值A和新值B。...

    Java并发编程包中atomic的实现原理示例详解

    Java并发编程包中atomic的实现原理示例详解,主要是通过volatile关键字保证可见性,CAS乐观锁保证原子性,自旋保证当次修改的最终修改成功。同时,Java 8中的LongAdder引入了分段锁的概念,提高了在高并发场景下的...

    Java 多线程与并发(8-26)-JUC原子类- CAS, Unsafe和原子类详解.pdf

    Java原子类大部分是基于CAS和volatile实现的。volatile关键字保证了变量的可见性,即一个线程对变量的修改,可以被其他线程即时看见。结合CAS,原子类能够以无锁的方式实现多线程的安全更新。 然而,CAS机制并非...

    java面试题_多线程(68题).pdf

    Java并发库中的`java.util.concurrent.atomic`包中的原子类如AtomicInteger、AtomicLong等就是基于CAS实现的。CAS在低冲突情况下具有高性能,但如果冲突频繁,会导致循环(自旋)过多,降低程序性能。 5. **乐观锁...

    Java编程cas操作全面解析

    CAS操作是一种乐观锁的思路,它相信在它修改之前,没有其它线程去修改它;而Synchronized是一种悲观锁,它认为在它修改之前,一定会有其它线程去修改它,悲观锁效率很低。 在Java编程中,CAS操作可以用来解决多线程...

Global site tag (gtag.js) - Google Analytics