在JDK1.5之前。Java主要靠synchronized这个关键字保证同步,已解决多线程下的线程不安全问题,但是这会导致锁的发生,会引发一些个性能问题。

锁主要存在一下问题

(1)在多线程竞争下,加锁、释放锁会导致比较多的上下文切换和调度延时,引起性能问题。

(2)一个线程持有锁会导致其它所有需要此锁的线程挂起。

(3)如果一个优先级高的线程等待一个优先级低的线程释放锁会导致优先级倒置,引起性能风险。

Volatile是一个不错的选择,但是前面我们已经说了,volatile不能保证原子性,因此同步还是需要用到锁。

也许大家已经听说过,锁分两种,一个叫悲观锁,一种称之为乐观锁。Synchronized就是悲观锁的一种,也称之为独占锁,加了synchronized关键字的代码基本上就只能以单线程的形式去执行了,它会导致其他需要该资源的线程挂起,直到前面的线程执行完毕释放所资源。而另外一种乐观锁是一种更高效的机制,它的原理就是每次不加锁去执行某项操作,如果发生冲突则失败并重试,直到成功为止,其实本质上不算锁,所以很多地方也称之为自旋。

乐观锁用到的主要机制就是CAS。Compare And Swap。

CAS有三个操作数,内存数据v,旧的预期数据A,要修改的数据B。每次进行数据更新时,当且仅当预期值A和内存中的数据V相同时,才将内存中的数据修改为B,否则什么也不做。

使用这种机制编写的算法也叫非阻塞算法,标准定义为一个线程的失败或者挂起不影响其他线程的失败或者挂起的算法。

现在的CPU提供了特殊的指令来自动更新共享数据,而且能检测到其他数据的干扰,因此可以通过compareAndSet来提到锁定。前面我们提到的一些原子类其实就是用的这个原理,如AtomicInteger,我们来看一下它对应的源码。

private volatile int value;

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

这里很显然采取了CAS的机制,每次从内存中读取数据都需要和+1后的数据进行一次CAS操作,如果成功返回结果,否则就失败重试,直到重试成功为止!

但是方法compareAndSet却是利用JNI来完成CPU指令的操作。

我们来看一下对应的源码

// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe(); public final boolean compareAndSet(int expect, int update) {
return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
}

整个过程就是这样子的,利用CPU的CAS机制,同时借助JNI来完成Java的非阻塞算法。基本上Java中的原子类都是使用类似的机制来保证数据的原子操作的。

尽管CAS机制使得我们可以不依赖同步,不影响和挂起其他线程实现原子性操作,能大大提升运行时的性能,但是会导致一个ABA的问题。如线程一和线程二都取出了主存中的数据为A,这时候线程二将数据修改为B,然后又修改为A,此时线程一再次去进行compareAndSet的时候仍然能够匹配成功,而实际对的数据已经发生了变更了,只不过发生了2次变化将对应的值修改为原始的数据了,并不代表实际数据没有发生变化。这时候前面提到的原子操作AtomicStampedReference/AtomicMarkableReference就很有用了。这允许一对变化的元素进行原子操作。

后续我们将继续探讨一下锁机制的实现,Java并发包中的锁是如何保证操作正确性的同时又大幅提升性能的。

深入浅出Java并发包—CAS机制的更多相关文章

  1. 深入浅出Java并发包—锁机制(二)

    接上文<深入浅出Java并发包—锁机制(一)  >  2.Sync.FairSync.TryAcquire(公平锁) 我们直接来看代码 protected final boolean tr ...

  2. 深入浅出Java并发包—锁机制(三)

    接上文<深入浅出Java并发包—锁机制(二)>  由锁衍生的下一个对象是条件变量,这个对象的存在很大程度上是为了解决Object.wait/notify/notifyAll难以使用的问题. ...

  3. 深入浅出Java并发包—锁机制(一)

    前面我们看到了Lock和synchronized都能正常的保证数据的一致性(上文例子中执行的结果都是20000000),也看到了Lock的优势,那究竟他们是什么原理来保障的呢?今天我们就来探讨下Jav ...

  4. 深入浅出Java并发包—CountDownLauch原理分析 (转载)

    转载地址:http://yhjhappy234.blog.163.com/blog/static/3163283220135875759265/ CountDownLauch是Java并发包中的一个同 ...

  5. 深入浅出Java并发包—锁(Lock)VS同步(synchronized)

    今天我们来探讨一下Java中的锁机制.前面我们提到,在JDK1.5之前只能通过synchronized关键字来实现同步,这个前面我们已经提到是属于独占锁,性能并不高,因此JDK1.5之后开始借助JNI ...

  6. 深入浅出Java并发包—指令重排序

    前面大致提到了JDK中的一些个原子类,也提到原子类是并发的基础,更提到所谓的线程安全,其实这些类或者并发包中的这么一些类,都是为了保证系统在运行时是线程安全的,那到底怎么样才算是线程安全呢? Java ...

  7. 深入浅出 Java Concurrency (15): 锁机制 part 10 锁的一些其它问题

      主要谈谈锁的性能以及其它一些理论知识,内容主要的出处是<Java Concurrency in Practice>,结合自己的理解和实际应用对锁机制进行一个小小的总结. 首先需要强调的 ...

  8. 深入浅出 Java Concurrency (7): 锁机制 part 2 AQS

      在理解J.U.C原理以及锁机制之前,我们来介绍J.U.C框架最核心也是最复杂的一个基础类:java.util.concurrent.locks.AbstractQueuedSynchronizer ...

  9. 深入浅出 Java Concurrency (15): 锁机制 part 10 锁的一些其它问题[转]

    主要谈谈锁的性能以及其它一些理论知识,内容主要的出处是<Java Concurrency in Practice>,结合自己的理解和实际应用对锁机制进行一个小小的总结. 首先需要强调的一点 ...

随机推荐

  1. JQuery 表格 隔行换色 和鼠标滑过的样式

    $(document).ready(function () { $(".Pub_TB tbody tr:even td").css("background-color&q ...

  2. 【Qt】QDialog之屏蔽Esc键【转】

    简述 Qt中Esc键会在一些控件中默认的进行一些事件的触发,比如:QDialog,按下Esc键窗口消失.大多数情况下,我们不需要这么做,那么就需要对默认事件进行屏蔽. 简述 源码分析 事件过滤器 事件 ...

  3. System.Data.OracleClient 需要 Oracle 客户端软件 version 8.1.7 或更高版本

    说明: 执行当前 Web 请求期间,出现未经处理的异常.请检查堆栈跟踪信息,以了解有关该错误以及代码中导致错误的出处的详细信息. 异常详细信息: System.ServiceModel.FaultEx ...

  4. LANMP 如何禁止访问 .htaccess 文件

    很多朋友问我,为什么他已经在 Apache 规则里面加了禁止别人直接下载 .htaccess 文件,为什么还是可以下载? 其实这个很简单,因为 .htaccess 在 LANMP 环境下,当他作为文件 ...

  5. Elastix 禁用SSL(https),还原为 http 访问

    1.相关配置文件目录: Apache的配置文件:httpd.conf,位于:/etc/httpd/conf/httpd.conf,配置文件中包含 Include conf.d/*.conf ,引入了  ...

  6. Query execution was interrupted, max_statement_time exceeded

    版本:5.6.16 群里看见一个问题,在备份的时候,报如下错误:[root@B28-19-75 bak]# mysqldump -root -p --single-transaction --mast ...

  7. vs2010的11个调试技巧和方法

    调试是软件开发周期中很重要的一部分.它具有挑战性,同时也很让人疑惑和烦恼.总的来说,对于稍大一点的程序,调试是不可避免的.最近几年,调试工具的发展让很多调试任务变的越来越简单和省时. 这篇文章总结了可 ...

  8. Oracle RAC中的一台机器重启以后无法接入集群

          前天有个同事说有套AIX RAC的其中一台服务器重启了操作系统以后,集群资源CSSD的资源一直都在START的状态,检查日志输出有如下内容: [    CSSD][1286]clssnmv ...

  9. IOS应用开发版本控制工具之Versions使用

    Versions版本控制工具破解版(Versions.zip)下载请见本博文附件.下载后在MAC安装完以后,图标是莲花状.见下图: 双击运行如下图:    点击Repository,连接SVN服务器R ...

  10. linux第四周作业

    一.用户态内核态与中断 1.库函数把内核调用封装起来. 2.区分内核态和用户态是为了让系统更稳定.Linux里吧用户态定位3级,把内核态定位0级. 3.中断处理就是从用户态进入内核态的主要方法,系统调 ...