是什么

  • 全称compare and swap,一个CPU原子指令,在硬件层面实现的机制,体现了乐观锁的思想。
  • JVM用C语言封装了汇编调用。Java的基础库中有很多类就是基于JNI调用C接口实现了多线程同步更新的功能。

原理

CMS有三个操作数:当前主内存变量的值V,线程本地变量预期值A,线程本地待更新值B。当需要更新变量值的时候,会先获取到内存变量值V然后很预期值A进行比较,如果相同则更新为B,如果不同,则将最新的变量值更新到预期值中再重新尝试上面的步骤,直到成功为止。

举例

以基于CAS实现的AtomicInteger 类进行讲解。

我们先写一个多线程,对同一个数据类进行分别进行加减操作10000次,正确的结果应该还是0。

没有任何多线程同步机制的代码如下:

package priv.nanjing.testCasClass;

/*
* @Author : darrenqiao
* */ //多线程争用的数据类
class Counter {
int count = 0; public int getCount() {
return count;
} public void setCount(int count) {
this.count = count;
} public void add() {
count += 1;
} public void dec() {
count -= 1;
}
} //争用数据做加操作的线程
class AddDataThread extends Thread { Counter counter; public AddDataThread(Counter counter) {
this.counter = counter;
} @Override
public void run() {
for (int i = 0; i < CasClass.LOOP; ++i) {
counter.add();
}
}
} //争用数据做减法操作的线程
class DecDataThread extends Thread { Counter counter; public DecDataThread(Counter counter) {
this.counter = counter;
} @Override
public void run() {
for (int j = 0; j < CasClass.LOOP; j++) {
counter.dec();
}
}
} public class CasClass {
final static int LOOP = 10000; public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread addThread = new AddDataThread(counter);
Thread decThread = new DecDataThread(counter);
addThread.start();
decThread.start();
addThread.join();
decThread.join();
System.out.println(counter.getCount());
} }

以下是三次执行结果,每一次都不一样。

为什么会出现这个结果呢?

因为 count += 1 / count -= 1 通过javap反编译Count.class文件,可以看出对应的字节码是三条指令

      //count += 1
5: iconst_1
6: iadd
7: putfield #12 // Field count:I //count 1= 1
5: iconst_1
6: isub
7: putfield #12 // Field count:I

所以多线程切换是,可能会造成数据更新的不同步,怎么解决呢

就是对被操作的数据加锁,可以是悲观锁,可以是乐观锁,这里使用的就是基于乐观锁实现的AtomicInteger类

package priv.nanjing.testCasClass;

import java.util.concurrent.atomic.AtomicInteger;

/*
* @Author : darrenqiao
* */ //多线程争用的数据类
class Counter {
//int count = 0;
//使用AtomicInteger代替基本数据类型
AtomicInteger count = new AtomicInteger(0); public int getCount() {
//return count;
return count.get();
} public void add() {
//count += 1;
count.addAndGet(1);
} public void dec() {
//count -= 1;
count.decrementAndGet();
}
} //争用数据做加操作的线程
class AddDataThread extends Thread { Counter counter; public AddDataThread(Counter counter) {
this.counter = counter;
} @Override
public void run() {
for (int i = 0; i < CasClass.LOOP; ++i) {
counter.add();
}
}
} //争用数据做减法操作的线程
class DecDataThread extends Thread { Counter counter; public DecDataThread(Counter counter) {
this.counter = counter;
} @Override
public void run() {
for (int j = 0; j < CasClass.LOOP; j++) {
counter.dec();
}
}
} public class CasClass {
final static int LOOP = 10000; public static void main(String[] args) throws InterruptedException {
Counter counter = new Counter();
Thread addThread = new AddDataThread(counter);
Thread decThread = new DecDataThread(counter);
addThread.start();
decThread.start();
addThread.join();
decThread.join();
System.out.println(counter.getCount());
} }

执行多次,会发现,结果只有一个:0.这样就保证了数据更新的原子性。

那么CAS有什么缺点需要注意

  • ABA问题:我内存对象从A变成B在变成A,CAS会当成没有变化,进而去更新值,实际是有变化的。
  • 循环时间开销大:一直和预期值不对的情况下,会一直循环。
  • 只能保证一个共享变量的原子操作。

JAVA乐观锁实现-CAS的更多相关文章

  1. paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象)

    paip.提升性能----java 无锁结构(CAS, Atomic, Threadlocal, volatile, 函数式编码, 不变对象) 1     锁的缺点 2     CAS(Compare ...

  2. Java乐观锁实现之CAS操作

    介绍CAS操作前,我们先简单看一下乐观锁 与 悲观锁这两个常见的锁概念. 悲观锁: 从Java多线程角度,存在着“可见性.原子性.有序性”三个问题,悲观锁就是假设在实际情况中存在着多线程对同一共享的竞 ...

  3. java 乐观锁CAS

    乐观锁是一种思想,本身代码里并没有lock或synchronized关键字进行修饰.而是采用一种version. 即先从数据库中查询一条记录得到version值,在更新这条记录时在where条件中对这 ...

  4. 深入分析 Java 乐观锁

    前言 激烈的锁竞争,会造成线程阻塞挂起,导致系统的上下文切换,增加系统的性能开销.那有没有不阻塞线程,且保证线程安全的机制呢?--乐观锁. 乐观锁是什么? 操作共享资源时,总是很乐观,认为自己可以成功 ...

  5. Java乐观锁、悲观锁

    乐观锁 乐观锁(Optimistic Lock), 顾名思义,就是很乐观,每次去拿数据的时候都认为别人不会修改,所以不会上锁,但是在更新的时候会判断一下在此期间别人有没有去更新这个数据,可以使用版本号 ...

  6. Java乐观锁的实现原理(案例)

    简要说明: 表设计时,需要往表里加一个version字段.每次查询时,查出带有version的数据记录,更新数据时,判断数据库里对应id的记录的version是否和查出的version相同.若相同,则 ...

  7. JAVA乐观锁、悲观锁实现

    一.名词解释 1.悲观锁:认为每次对数据库的操作(查询.修改)都是不安全的,因此每次操作都会把这条数据锁掉,直到本次操作完毕释放该锁 2.乐观锁:查询数据的时候总是认为是安全的,不会锁数据:等到更新数 ...

  8. java 乐观锁 vs 悲观锁

    在数据库的锁机制中介绍过,数据库管理系统(DBMS)中的并发控制的任务是确保在多个事务同时存取数据库中同一数据时不破坏事务的隔离性和统一性以及数据库的统一性. 悲观锁其实就是 完全同步 比如 sync ...

  9. 乐观锁--CAS

    悲观锁与乐观锁的区别 悲观锁会把整个对象加锁占为已有后才去做操作,Java中的Synchronized属于悲观锁.悲观锁有一个明显的缺点就是:它不管数据存不存在竞争都加锁,随着并发量增加,且如果锁的时 ...

随机推荐

  1. how to drop multiple talbes in oracle use a sigle query

    Tool:PL/SQL developer Syntax:SELECT 'DROP TABLE ' || table_name || ';' FROM user_tables; 1.then you ...

  2. Win10笔记本显卡驱动更新升级

    对于游戏玩家来说,对显卡的关注度要高于电脑其它硬件,一般来说,显卡越好,游戏性能往往越强.不过要持续发挥显卡的最佳游戏性能,经常更新显卡驱动也是很有必要的.那么笔记本显卡驱动怎么更新?下面小编以自己的 ...

  3. Asp.Net MVC Identity 2.2.1 使用技巧(四)

    使用用户管理器之用户管理 一.建立模型 1.在Models文件夹上点右键 >添加>类     类的名称自定,我用AdminViewModels命名的. 2.更改模板自建的AdminView ...

  4. 有用的JS函数

    1. QueryString function queryString(key) { var re = new RegExp("[?&]" + key + "=( ...

  5. September 01st 2017 Week 35th Friday

    Each trauma, is another kind of maturity. 每一个创伤,都是另一种成熟. Sometimes the trauma may be too severe to h ...

  6. 51nod 1952 栈

    题目链接戳这 如果只是从尾端插入,那么问题就是基础的:求取栈内最大值的问题,这用单调栈解决即可. 但是前端也能插入,一般的单调栈已经不能满足.那么想象,如果在前端插入一个小值,相当于在栈底多加一个值罢 ...

  7. JQuery的getJSON函数跨域

    由于一开始看到“$”等类似符号就头晕,所以注定与PHP和JQuery无缘了,不过自己用JavaScript可不代表其他人也得用,这不,麻烦到了... 两个网站:A.B A站点提供了一个重要的API,由 ...

  8. 匹配IP的正则表达式

    正则表达式匹配IP 1 ((25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|\d)\.){3}(25[0-5]|2[0-4]\d|1\d\d|[1-9]\d|[1-9])  

  9. linux mint gcc 编译第一个c程序

    GCC是Linux操作系统下一个非常重要的源代码编译工具,有着许多重要的选项,支持许多不同语言的编译,如C.C++.Ada. Fortran.Objective.Perl.Python.Ruby以及J ...

  10. python各种模块的使用

    Pexpect模块:http://www.ibm.com/developerworks/cn/linux/l-cn-pexpect1/ ConfigParser模块:http://blog.china ...