Archives

  • 从JVM并发看CPU内存指令重排序(Memory Reordering)

    这两天,我拜读了 Dennis Byrne 写的一片博文?Memory Barriers and JVM Concurrency (中译文?内存屏障与JVM并发)。

    文中提到:

    对主存的一次访问一般花费硬件的数百次时钟周期。处理器通过缓存(caching)能够从数量级上降低内存延迟的成本这些缓存为了性能重新排列待定内存操作的顺序。也就是说,程序的读写操作不一定会按照它要求处理器的顺序执行。

    这段话是作者对内存屏障重要性的定义。通过cache降低内存延迟,这句话很好理解。但后面那句“为了性能重排序内存操作顺序”,让没学好微机原理的我倍感疑惑。

    CPU为何要重排序内存访问指令?在哪种场景下会触发重排序?作者在文中并未提及。

    为了解答疑问,我在网上查阅了一些资料,在这里跟大家分享一下。

    Read more…

    2010/04/29 | Posted in Concurrency
  • Java偏向锁实现原理(Biased Locking)

    阅读本文的读者,需要对Java轻量级锁有一定的了解,知道lock record, mark word之类的名词。可以参考我的一篇博文:Java轻量级锁原理详解(Lightweight Locking)

    Java偏向锁(Biased Locking)是Java6引入的一项多线程优化。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。

    轻量级锁也是一种多线程优化,它与偏向锁的区别在于,轻量级锁是通过CAS来避免进入开销较大的互斥操作,而偏向锁是在无竞争场景下完全消除同步,连CAS也不执行(CAS本身仍旧是一种操作系统同步原语,始终要在JVM与OS之间来回,有一定的开销)。

    所谓的无竞争场景,举个例子,就是单线程访问带同步的资源或方法。

    Read more…

    2009/11/14 | Posted in Concurrency
  • Java轻量级锁原理详解(Lightweight Locking)

    大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意。
    原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操作系统互斥(mutex)来实现的。
    互斥是一种会导致线程挂起,并在较短的时间内又需要重新调度回原线程的,较为消耗资源的操作。

    为了优化Java的Lock机制,从Java6开始引入了轻量级锁的概念。

    轻量级锁(Lightweight Locking)本意是为了减少多线程进入互斥的几率,并不是要替代互斥。
    它利用了CPU原语Compare-And-Swap(CAS,汇编指令CMPXCHG),尝试在进入互斥前,进行补救。

    本文将详细介绍JVM如何利用CAS,实现轻量级锁。

    Read more…

    2009/11/08 | Posted in Concurrency
  • java并发编程实践笔记

    1, 保证线程安全的三种方法 :
    a, 不要跨线程访问共享变量
    b, 使共享变量是 final类型的
    c, 将共享变量的操作加上同步

    2, 一开始就将类设计成线程安全的 , 比在后期重新修复它 ,更容易 .

    3, 编写多线程程序 , 首先保证它是正确的 , 其次再考虑性能 .

    4, 无状态或只读对象永远是线程安全的 .

    5, 不要将一个共享变量裸露在多线程环境下 (无同步或不可变性保护 )

    6, 多线程环境下的延迟加载需要同步的保护 , 因为延迟加载会造成对象重复实例化

    7, 对于 volatile 声明的数值类型变量进行运算 , 往往是不安全的 (volatile 只能保证可见性 , 不能保证原子性 ).
    详见 volatile 原理与技巧中 , 脏数据问题讨论 .
    Read more…

    2008/03/07 | Posted in Concurrency
  • java 线程小结

    1, 为什么wait与notify之前必须要加synchronized?

    答案其实很简单,也是为了防止等待-通知机制出现race condition

    为什么会出现race condition ?
    答: 对象在被wait之前已经被另一线程notify , 之后的wait 会永久停止,并导致deadlock(死锁)

    理想情况:
    1, 第一个线程判断该对象是否要wait
    2, 第一个线程将对象wait
    3, 第二个线程再将对象notify

    实际情况
    1, 第一个线程判断该对象是否要wait
    2, 第二个线程将对象notify
    3, 第一个线程将对象wait

    为了防止这些情况,才需要在wait与notify之前加synchronized
    Read more…

    2008/03/07 | Posted in Concurrency
  • java内存模型详解

    内存模型 (memory model)
    内存模型描述的是程序中各变量(实例域、静态域和数组元素)之间的关系,以及在实际计算机系统中将变量存储到内存和从内存取出变量这样的低层细节.

    不同平台间的处理器架构将直接影响内存模型的结构.

    在C或C++中, 可以利用不同操作平台下的内存模型来编写并发程序. 但是, 这带给开发人员的是, 更高的学习成本.
    相比之下, java利用了自身虚拟机的优势, 使内存模型不束缚于具体的处理器架构, 真正实现了跨平台.
    (针对hotspot jvm, jrockit等不同的jvm, 内存模型也会不相同)

    内存模型的特征:
    a, Visibility 可视性 (多核,多线程间数据的共享)
    b, Ordering 有序性 (对内存进行的操作应该是有序的)

    java 内存模型 ( java memory model )
    根据Java Language Specification中的说明, jvm系统中存在一个主内存(Main Memory或Java Heap Memory),Java中所有变量都储存在主存中,对于所有线程都是共享的。

    每条线程都有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作都是在工作内存中进行,线程之间无法相互直接访问,变量传递均需要通过主存完成。
    java内存模型
    Read more…

    2007/08/14 | Posted in Concurrency
  • volatile原理与技巧

    volatile, 用更低的代价替代同步

    为什么 使用volatile比同步代价更低?
    同步的代价, 主要由其覆盖范围决定, 如果可以降低同步的覆盖范围, 则可以大幅提升程序性能.

    而volatile的覆盖范围仅仅变量级别的. 因此它的同步代价很低.

    volatile原理是什么?
    volatile的语义, 其实是告诉处理器, 不要将我放入工作内存, 请直接在主存操作我.(工作内存详见java内存模型)

    因此, 当多核或多线程在访问该变量时, 都将直接 操作 主存, 这从本质上, 做到了变量共享.

    volatile的有什么优势?
    1, 更大的程序吞吐量
    2, 更少的代码实现多线程
    3, 程序的伸缩性较好
    4, 比较好理解, 无需太高的学习成本

    volatile有什么劣势?
    1, 容易出问题
    2, 比较难设计
    Read more…

    2007/08/05 | Posted in Concurrency
Archive for the ‘Concurrency’ Category