并发编程艺术-锁类型以及底层原理(代码片段)

CoderQiangJJ CoderQiangJJ     2022-10-30     384

关键词:

本文地址

Java并发编程艺术-并发机制的底层原理实现

1.Volatile

定义:

Java编程语言允许线程访问共享变量,为了确保共享变量能被准确和一致的更新,线程应该确保通过排他锁单独获得这个变量。

volatile借助Java内存模型保证所有线程能够看到最新的值。(内存可见性)

实现原理:

将带有volatile变量操作的Java代码转换成汇编代码后,可以看到多了个lock前缀指令(X86平台CPU指令)。这个lock指令是关键,在多核处理器下实现两个重要操作:

1.将当前处理器缓存行的数据写回到系统内存。

2.这个写回内存的操作会使其他处理器里缓存该内存地址的数据失效

如果了解计算机组成原理,可以知道CPU为了提高处理速度,不和内存直接进行交互,而是使用Cache(高速缓存,通过缓存数据交互速度和内存不是一个数量级,而同时Cache的存储容量也很小)。
从内存将数据读到缓存后,CPU进行一系列数据操作,而操作完成时间是不可知的。而JVM对带有volatile变量进行写操作时,会发送Lock前缀指令,将数据从缓存行写入到内存。写入内存还不够,因为其他线程的缓存行中数据还是旧的,Lock指令可以让其他CPU通过监听在总线上的数据,检查自己的缓存数据是否过期,如果缓存行的地址和总线上的地址相同,则将缓存行失效,下次该线程对这个数据操作时,会重新从内存中读取,更新到缓存行。

2.Synchronized

Synchronized也是经常用到的,它给人的印象一般是"重量级锁"。在JDK1.6后,对Synchronized进行了一系列优化,引入了偏向锁和轻量级锁,对锁的存储结构和升级过程。有效减少获得锁和释放锁带来的性能消耗。

Synchronized同步基础:

1.普通同步方法,锁是当前实例对象。 public synchronized void test()...

2.静态同步方法,锁是当前类的Class对象。public static synchronized void test(...)

3.对于同步方法块,锁是Synchronized括号中里配置的对象。synchronized(instance)...

用javap反编译class文件,可以看到Synchronized用的是monitorenter和monitorexit实现加锁。一个monitorenter必须要有monitorexit与之对应,所以同步方法会在异常处和方法返回处加入monitorexit指令。

         3: monitorenter  //注意此处,进入同步方法
         4: aload_0
         5: dup
         6: getfield      #2             // Field i:I
         9: iconst_1
        10: iadd
        11: putfield      #2            // Field i:I
        14: aload_1
        15: monitorexit   //注意此处,退出同步方法

3.Java对象头

Synchronized用到的锁存在Java对象头里,若对象非数组类型,用32bit存储(2个字宽,32虚拟机一个字宽为4字节,一个字节8bit)
MarkWord存储和锁相关的信息:

锁有四个等级: 无锁->偏向锁->轻量级锁->重量级锁。如果存在竞争,就会不断升级,但不会降级。

1.偏向锁

多数情况下,锁不会存在竞争,而是同一个线程多次获得。当某个线程访问同步块代码时,会将锁对象和栈帧中的锁记里存储锁偏向的线程ID,以后线程在进入和退出同步块时不需要进行CAS操作来加锁和解锁,只需简单比对一下对象头中的MarkWord里的线程ID,如果一致则表示线程获得锁。若不一致,再继续测试偏向锁的标识是否为1:如果没有设置(无锁状态),用CAS(Compare and Swap)竞争锁;如果设置了,尝试使用CAS将对象头的偏向锁指向当前线程。

当有另一个线程尝试竞争锁时,持有偏向锁的线程才会释放锁。需要等待全局安全点(在这个时间点上没有字节码正在执行),它会首先暂停拥有偏向锁的线程,然后检查持有偏向锁的线程是否活着,如果线程不处于活动状态,则将对象头设置成无锁状态,如果线程仍然活着,拥有偏向锁的栈会被执行,遍历偏向对象的锁记录,栈中的锁记录和对象头的Mark Word,要么重新偏向于其他线程,要么恢复到无锁或者标记对象不适合作为偏向锁,最后唤醒暂停的线程。

Java 6,7默认开启偏向锁,可以通过JVM的参数-XX:-UsebiasedLocking=false关闭

2.轻量级锁

(1)加锁

锁记录存储在栈桢,会将对象头的MarkWord复制到锁记录。线程在执行同步块时,会尝试用CAS将对象头的MarkWord替换为指向锁记录的指针,若成功,获得锁;失败表示其他线程竞争锁,当前线程尝试使用自旋获取锁。

(2)解锁

类似于加锁反向操作,会将锁记录复制会对象头的MarkWord。若成功,表示操作过程中没有竞争发生;若失败,存在竞争,锁会膨胀成重量级锁。

如下图:

当膨胀到重量级锁时,不会再通过自选获得锁(自旋时线程处于活动状态,会消耗CPU),而是将线程阻塞,获得锁的线程执行完后会释放重量级锁,此时唤醒因为锁阻塞的线程,进行新一轮的竞争。

3.其他锁概念

自旋锁:
自旋锁是采用让当前线程不停地的在循环体内执行实现的,当循环的条件被其他线程改变时 才能进入临界区。

public class SpinLock 

  private AtomicReference<Thread> sign =new AtomicReference<>();

  public void lock()
    Thread current = Thread.currentThread();
    while(!sign .compareAndSet(null, current))
    
  

  public void unlock ()
    Thread current = Thread.currentThread();
    sign .compareAndSet(current, null);
  

使用了CAS原子操作,lock函数将owner设置为当前线程,并且预测原来的值为空。unlock函数将owner设置为null,并且预测值为当前线程。

当有第二个线程调用lock操作时由于owner值不为空,导致循环一直被执行,直至第一个线程调用unlock函数将owner设置为null,第二个线程才能进入临界区。

由于自旋锁只是将当前线程不停地执行循环体,不进行线程状态的改变,所以响应速度更快。但当线程数不停增加时,性能下降明显,因为每个线程都需要执行,占用CPU时间。如果线程竞争不激烈,并且保持锁的时间段。适合使用自旋锁。


锁的优缺点对比:

优点 缺点 适用场景
偏向锁 加锁和解锁不需要额外的消耗,和执行非同步方法比仅存在纳秒级的差距 如果线程间存在锁竞争,会带来额外的锁撤销的消耗 适用于只有一个线程访问同步块场景
轻量级锁 竞争的线程不会阻塞,提高了程序的响应速度 如果始终得不到锁竞争的线程使用自旋会消耗CPU 追求响应时间,锁占用时间很短
重量级锁 线程竞争不使用自旋,不会消耗CPU 线程阻塞,响应时间缓慢 追求吞吐量,锁占用时间较长

并发编程面试(代码片段)

1.说说synchronized关键字的底层原理是什么?synchronized底层的原理,是跟jvm指令和monitor有关系的如果我们用到了synchronized关键字,在底层编译后的JVM指令中,会有monitorenter和monitorexit两个指令加锁执行monitorenter指令,释放锁执行moni... 查看详情

java高并发编程实战4,synchronized与lock底层原理(代码片段)

目录一、synchronized底层原理二、反编译synchronized方法1、定义一个最简单的synchronized方法2、通过```javap-cSynchronizedTest.class```进行反编译:3、代码分析三、偏向锁四、Lock源码分析1、Lock锁的方法如下2、下面分... 查看详情

java高并发编程实战4,synchronized与lock底层原理(代码片段)

目录一、synchronized底层原理二、反编译synchronized方法1、定义一个最简单的synchronized方法2、通过```javap-cSynchronizedTest.class```进行反编译:3、代码分析三、偏向锁四、Lock源码分析1、Lock锁的方法如下2、下面分... 查看详情

[并发编程的艺术]02-java并发机制的底层实现原理

...节码,最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令.一、volatile的应用  在多处理器开发中保证共享变量的"可见性",可见性的意思是:当一个线程修改一个共享变量时,另外一个线程能够... 查看详情

java并发编程艺术系列-二java并发机制底层原理

二、Java并发机制底层原理volatilesynchronized原子操作2.1volatile原理与应用2.1.1特点轻量级的synchronized共享变量的“可见性”(定义):如果一个字段被声明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的不会引... 查看详情

java并发编程艺术学习第二章java并发机制的底层实现原理学习记录volatile

章节介绍  这一章节主要学习java并发机制的底层实现原理。主要学习volatile、synchronized和原子操作的实现原理。Java中的大部分容器和框架都依赖于此。  Java代码==经过编译==》Java字节码==通过类加载器==》JVM(jvm执行字节码... 查看详情

java并发编程的艺术,读书笔记第六章concurrenthashmap以及并发容器的介绍

ConcurrentHashMap的原理将数据一段一段的存储然后给每一段数据分配一把锁,当线程访问数据的一段时,为每段分配一把锁,同时其他段的数据可以被其他线程数据访问2)concurrentHashMap的结构concurrentHashMap由segament数组和hashentry数组... 查看详情

《java并发编程的艺术》之atomicx(代码片段)

其中主要是了解下AtomicReference以及AtomicXUpdaterAtomicReference是支持对象引用原子更新的类,仅仅是支持引用,如果要让对象内的字段支持原子更新,就一定要使用到AtomicXUpdater。字段更新类需要特别注意,字段必须是publicvolatile类型... 查看详情

java并发编程回忆提纲总结(代码片段)

参考资料:《Java并发编程的艺术》用于快速回忆,详情请看书籍原文1.一些概念1.1上下文切换定义:任务从保存到再加载的过程减少上下文切换无锁并发编程:将临界资源划分给不同线程各自访问CAS算法最少线程原则:不创建无... 查看详情

java并发编程:synchronized底层优化(偏向锁轻量级锁)

Java并发编程系列:Java并发编程:核心理论 Java并发编程:Synchronized及其实现原理Java并发编程:Synchronized底层优化(轻量级锁、偏向锁)Java并发编程:线程间的协作(wait/notify/sleep/yield/join)Java并发编程:volatile的使用及其原理... 查看详情

synchronize底层实现原理以及相关的优化(代码片段)

...首先来说下synchronize和Lock的区别:两者都是锁,用来控制并发冲突,区别在于Lock是个接口,提供的功能更加丰富,除了这个外,他们还有如下区别:synchronize自动释放锁,而Lock必须手动释放,并且代码中出现异常会导致unlock代... 查看详情

java并发编程:java并发机制的底层实现原理(代码片段)

...最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令。2、volatile应用2.1、简介在多线程并发编程中synchronized和volatile都扮演着重要的角色,volatile 查看详情

java并发编程:java并发机制的底层实现原理(代码片段)

...最终需要转化为汇编指令在CPU上执行,Java中所使用的并发机制依赖于JVM的实现和CPU的指令。2、volatile应用2.1、简介在多线程并发编程中synchronized和volatile都扮演着重要的角色,volatile 查看详情

并发编程线程可见性的底层原理(代码片段)

目录一、一段代码引发的思考二、从硬件层面了解可见性本质1.CPU高速缓存2.缓存一致性3.缓存一致性协议4.指令重排序5.CPU层面的内存屏障三、软件层面JMM1.JMM内存模型2.JMM是如何解决可见性问题的3.JMM是如何解决有序性问题的四.H... 查看详情

java并发编程的艺术(5-10)学习总结(代码片段)

本文参考学习Java并发编程的艺术第5章Java中的锁5.1Lock接口synchronized没有的特性尝试非阻塞获取锁能够中断获取锁超时获取锁5.2队列同步器队列同步器AbstractQueuedSynchronizer用来构建锁,或者其它同步组件。用一个int成员变量表... 查看详情

java并发编程:thread与runnable的底层原理(代码片段)

一、Thread类与Runnable接口的渊源本篇是Java并发编程系列第二章,本章主要讲解Thread类与Runnable接口。(1)Runnable接口的内容详解在了解Thread类与Runnable接口之前,我觉得有必要先单独对Runnable接口做一个简略的概述... 查看详情

java并发编程的艺术--原子操作类和并发工具类(第七八章)(代码片段)

...子更新字段类常用类1.4.2、AtomicIntegerFieldUpdater的使用2、并发工具类2.1、CountDownLatch2.1.1、简介2.1.2、CountDownLatch的使用2.1.3、应用场景2.2、CyclicBarrier2.2.1、简介2.2.2、CyclicBarrier的使用2.2.3、应用场景2.2.4、CyclicBarrier和CountDownLatch的区... 查看详情

并发编程之显式锁原理(代码片段)

Synchronized关键字结合对象的监视器,JVM为我们提供了一种『内置锁』的语义,这种锁很简便,不需要我们关心加锁和释放锁的过程,我们只需要告诉虚拟机哪些代码块需要加锁即可,其他的细节会由编译器和虚拟机自己实现。可... 查看详情