博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
B8 Concurrent JDK中的乐观锁与原子类
阅读量:6703 次
发布时间:2019-06-25

本文共 5067 字,大约阅读时间需要 16 分钟。

概述

  乐观锁采用的是一种无锁的思想,总是假设最好的情况,认为一个事务在读取数据的时候,不会有别的事务对数据进行修改,只需要在修改数据的时候判断原数据数据是否已经被修改了。JDK 中 java.util.concurrent.atomic 包下提供的原子类就是基于乐观锁的思想实现的,具体的实现主要依赖于 sun.misc.Unsafe 类提供的 CAS(Compare And Swap) 算法实现。

CAS 算法

  Compare And Swap,顾名思义,先比较然后交换。CAS 算法主要的参数有三个:old(原值)、expect(期望值)、update(更新值)。首先比较 expect(期望值)与  old(原值)是否相等,若相等,则将 old(原值)更新为 update(更新值)。

  JDK 1.8 中 sun.misc.Unsafe 类提供了以下几种 CAS 算法相关的方法

  1) 最底层的方法(native method):

public final native boolean compareAndSwapObject(Object paramObject1, long paramLong, Object paramObject2, Object paramObject3);  public final native boolean compareAndSwapInt(Object paramObject, long paramLong, int paramInt1, int paramInt2);  public final native boolean compareAndSwapLong(Object paramObject, long paramLong1, long paramLong2, long paramLong3);

  首先介绍一下这四个参数:

  • 参数 1: 表示指向共享变量的引用
  • 参数 2: 表示距离共享变量内存储存开始位置的偏移量,指向该变量对象的某一具体属性。obj + offset 可以确定 old(原值)
  • 参数 3:表示 expect(期望值),用于与 old(原值)判断是否相等
  • 参数 4:表示 update(更新值),若 expect 和 old 相等,则将 old 更新为 update

  返回值:若 expect 与 old 判断相等,且将 old 更新为 update,则返回 true;否则返回 false。

  2) JDK 1.8 新增方法:

public final int getAndAddInt(Object paramObject, long paramLong, int paramInt)  {    int i;    do      i = getIntVolatile(paramObject, paramLong);    while (!compareAndSwapInt(paramObject, paramLong, i, i + paramInt));    return i;  }  public final long getAndAddLong(Object paramObject, long paramLong1, long paramLong2)  {    long l;    do      l = getLongVolatile(paramObject, paramLong1);    while (!compareAndSwapLong(paramObject, paramLong1, l, l + paramLong2));    return l;  }  public final int getAndSetInt(Object paramObject, long paramLong, int paramInt)  {    int i;    do      i = getIntVolatile(paramObject, paramLong);    while (!compareAndSwapInt(paramObject, paramLong, i, paramInt));    return i;  }  public final long getAndSetLong(Object paramObject, long paramLong1, long paramLong2)  {    long l;    do      l = getLongVolatile(paramObject, paramLong1);    while (!compareAndSwapLong(paramObject, paramLong1, l, paramLong2));    return l;  }  public final Object getAndSetObject(Object paramObject1, long paramLong, Object paramObject2)  {    Object localObject;    do      localObject = getObjectVolatile(paramObject1, paramLong);    while (!compareAndSwapObject(paramObject1, paramLong, localObject, paramObject2));    return localObject;  }

  对比 JDK 1.7 和 1.8 中原子类 AtomicInteger 的代码实现: JDK 1.8 将自旋操作封装到 sun.misc.Unsafe 类中

  JDK1.7:

public final int getAndIncrement()  {    while (true)    {      int i = get();      int j = i + 1;      if (compareAndSet(i, j))        return i;    }  }

  JDK 1.8:

public final int getAndIncrement() {        return unsafe.getAndAddInt(this, valueOffset, 1);    }

注意

  JDK 中乐观锁的实现思路:使用 volatile 关键字修饰共享变量 + sun.misc.Unsafe 类提供的 CAS 算法实现。volatile 关键字保证了线程的可见性,在进行 CAS 算法 “比较” 时,保证当前线程获取到的是最新的值;CAS 算法中 Compare (比较) 和 Swap(交换)是一组原子操作,保证了线程的原子性。

CAS 与 ABA 问题

  ABA 问题指的是:一个线程将变量 A 修改为变量 B,然后又修改为变量 A,前后两个 A 可能修改了某些属性,已经发生了变更;另一个线程拿到仍然是变量 A,CAS 成功,但实际上另一线程拿到的变量 A可能已经被修改过的。

  JDK 中提供了解决 ABA 问题的具体实现:java.util.concurrent.atomic.AtomicMarkableReference<V> 和 java.util.concurrent.atomic.AtomicStampedReference<V>。

原子类 AtomicInteger 源码分析

 1). 初始化:定义了一个 volatile 关键字修饰的 int 变量 value,获取 Unsafe 对象和变量 value 的内存地址偏移量 valueOffset 

// setup to use Unsafe.compareAndSwapInt for updates    private static final Unsafe unsafe = Unsafe.getUnsafe();    private static final long valueOffset;    static {        try {            valueOffset = unsafe.objectFieldOffset                (AtomicInteger.class.getDeclaredField("value"));        } catch (Exception ex) { throw new Error(ex); }    }    private volatile int value;    /**     * Creates a new AtomicInteger with the given initial value.     *     * @param initialValue the initial value     */    public AtomicInteger(int initialValue) {        value = initialValue;    }    /**     * Creates a new AtomicInteger with initial value {@code 0}.     */    public AtomicInteger() {    }

  2). 常用方法:使用 Unsafe 类的 CAS 算法实现

public final int get() {        return value;    }    public final void set(int newValue) {        value = newValue;    }    public final int getAndSet(int newValue) {        return unsafe.getAndSetInt(this, valueOffset, newValue);    }     public final boolean compareAndSet(int expect, int update) {        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);    }    public final int getAndIncrement() {        return unsafe.getAndAddInt(this, valueOffset, 1);    }    public final int getAndDecrement() {        return unsafe.getAndAddInt(this, valueOffset, -1);    }    public final int getAndAdd(int delta) {        return unsafe.getAndAddInt(this, valueOffset, delta);    }    public final int incrementAndGet() {        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;    }    public final int decrementAndGet() {        return unsafe.getAndAddInt(this, valueOffset, -1) - 1;    }    public final int addAndGet(int delta) {        return unsafe.getAndAddInt(this, valueOffset, delta) + delta;    }

  

转载于:https://www.cnblogs.com/zlxyt/p/11102538.html

你可能感兴趣的文章
细说业务逻辑 -- 丢失的业务逻辑层
查看>>
阿里云自动快照有什么用,如何设置?
查看>>
xshell 与 putty
查看>>
Oracle用户、权限、角色管理
查看>>
2. Ext中关于Ext.QuickTips.init()的使用
查看>>
SIGTERM等信号含义【转】
查看>>
剑指offer三十六之两个链表的第一个公共结点
查看>>
hadoop之 Hadoop 2.x HA 、Federation
查看>>
spring-boot-starter-data-redis与spring-boot-starter-redis两个包的区别
查看>>
Django And Django-Rest-Framework 异常记录
查看>>
MLP(多层神经网络)介绍
查看>>
[原创]用C#实现微信“跳一跳”小游戏的自动跳跃助手
查看>>
测试日报模板
查看>>
GDALBuildVRT异构波段的支持
查看>>
Windows7-32位系统下R语言链接mySQL数据库步骤
查看>>
springboot学习笔记-5 springboot整合shiro
查看>>
复旦大学2017--2018学年第一学期(17级)高等代数I期末考试第八大题解答
查看>>
span的赋值与取值
查看>>
查看java内存情况命令
查看>>
SQL SERVER 事务和锁
查看>>