zoukankan      html  css  js  c++  java
  • 编程语言java-并发(锁)

    文章转载自http://www.importnew.com/22078.html

    悲观锁和乐观锁

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

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

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

    Java中CAS的实现

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

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

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

    AtomicInteger的实现

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

  • 相关阅读:
    对象的创建
    Java运行时数据区域
    Java内存模型
    LinkedList小练习及相关算法
    面试题之矩阵与转置矩阵相乘
    快速排序
    垃圾收集器
    java垃圾收集相关问题
    Win7下安装Centos7双系统出错:No valid bootloader target device found.
    Scanner类的方法
  • 原文地址:https://www.cnblogs.com/loveyixiang/p/6028960.html
Copyright © 2011-2022 走看看