Java并发编程

来源:互联网 发布:淘宝网拉杆箱价格图片 编辑:程序博客网 时间:2024/06/16 21:51

Java代码在编译后会变成Java字节码, 字节码被ClassLoader加载到JVM中, JVM执行字节码, 最终要转化为汇编指令在CPU上执行,
Java中所使用的并发机制依赖于JVM的实现和CPU的指令.

volatile原语

volatile是轻量级的synchronized, 它在多CPU(注意不仅仅是多核)开发中保证了共享变量的可见性.

由于volatile只保证了可见性, 无法保证原子性, 所以比synchronized的执行成本更低, 它不会引起上下文的切换和调度.

volatile实现原理

为加快CPU的处理速度, 它是不直接与内存进行通信, 而是先将系统内存的数据读到CPU内部缓存(L1, L2或其他)后再进行操作,
操作完全不知道何时会写回内存.

volatile标注的变量如果被修改了, 会引起一系列的变化:

  1. 当前操作的的CPU会强制将缓存行中的变化立即写回到内存中;
  2. 这个写回内存的操作会使在其他CPU里缓存了该内存地址的数据立即失效, 也就是其他CPU如果需要再使用这个变量,
    需要重新到内存中读取到CPU缓存中;

synchronized原语实现原理与应用

Java中的每一个对象都可以作为锁

JavaSE 1.6中, 锁一共有四种状态, 级别从低到高依次为: 无锁状态, 偏向锁, 轻量级锁, 重量级锁. 当低级的锁机制不适配的时候, 程序会自动将锁升级为高级别的锁.

偏向锁

偏向锁使用了一种等到竞争出现才释放锁的机制, 所以当其他线程尝试竞争偏向锁时, 持有偏向锁的线程才会释放锁.

轻量级锁

在竞争锁的时候, 不会阻塞, 而是使用’自旋’机制反复尝试获取锁(始终消耗CPU时间片), 直到获取锁.

重量级锁

就是JavaSE 1.6之前的锁机制

锁的优缺点对比

类别 优点 缺点 适用场景 偏向锁 和执行非同步的方法相比仅存在纳秒级的差距 如果线程间存在锁竞争, 则会带来额外的锁撤销的消耗 适用于长期只有一个线程访问同步块的场景 轻量级锁 竞争锁的线程不会阻塞, 提高了程序的响应速度 竞争的线程会自旋, 消耗CPU 追求响应时间, 且同步块执行速度非常快的场景 重量级锁 竞争锁的线程不自旋, 不会消耗CPU 竞争锁的线程会阻塞, 相应时间缓慢 追求吞吐量, 或者是同步块执行时间较长的时候

原子操作的实现原理

原子操作的定义是:

不可被中断的一个或者一系列操作

CPU如何实现原子操作

总线锁

使用处理器提供的一个LOCK#信号, 当一个处理器在总线上输出此信号时, 其他处理器的请求将被阻塞, 那么该处理器可以独占共享内存.

缓存锁

同一时刻, 我们只需要保证对某个内存地址的操作是原子性的即可

Java如何实现原子操作

Java中可以通过锁和循环CAS(Compare and Set)的方式来实现原子操作, 具体的表现形式是内置了一些类来支持原子操作: 如AtomicBoolean,
AtomicInteger, AtomicLong

CAS实现原子操作的问题

  • 循环开销大:

    自旋CAS如果长时间不成功, 会给CPU带来非常大的执行开销

  • 只能保证一个共享变量的原子操作

    如果一个类有两个以上的field时, 就需要使用锁了.

使用锁机制来实现原子操作

实际上线程操作锁的方式在底层也是使用循环CAS的方式来进行锁的获取和释放的.

0 0
原创粉丝点击