JMM内存模型

一、

上下文切换问题:时间片一般是几十毫秒,任务从保存到再加载的过程就是一次上下文切换,多线程竞争锁时会引起上下文切换,时间片到了会引起上下文切换

减少上下文切换:1、无锁并发线程  2、CAS算法  3、使用最少线程  4、协程

死锁问题:1、尽量避免一个线程同时获得多个锁

     2、尽量避免一个线程在锁内同时占用多个资源  

     3、尝试使用定时锁lock.tryLock(timeout)

     4、对于数据库锁,加锁和解锁必须在同一个数据库连接里

二、JVM的并发机制

1、volatile关键字:

volatile比synchronized执行成本更低,因为他不会引起线程上下文的切换和调度

被volatile修饰的共享变量进行写操作的时候会多出一条以lock为前缀的汇编指令:1、lock指令会引起会引起处理器缓冲回写到内存  2、一个处理器的缓冲回写到内存会引起其它处理器的缓冲失效

volatile关键字的作用:1、保证可见性  2、禁止指令重排序【但这两个功能的底层实现都是lock指令】

2、synchronized关键字(抛出异常自动释放锁)

所以synchronized的底层操作含义是先对对象头的锁标志位用lock cmpxchg的方式设置成“锁住“状态,释放锁时,在用lock cmpxchg的方式修改对象头的锁标志位为”释放“状态,写操作都立刻写回主内存

无所状态---->偏向锁状态---->撤销偏向锁状态---->轻量级锁状态---->重量级锁状态

偏向锁的撤销是狠耗时的

偏向锁通过偏向锁的撤销膨胀成轻量级锁·

轻量级锁通过自旋膨胀成重量级锁

3、原子操作的实现原理:(CAS操作同时具有volatile读-写的语义)

  (1)、总线锁

  (2)、缓冲锁(并不是都支持)

  (3)、CAS就是利用lock CMPXCHG指令实现的(JAVA所用)

    ①ABA问题

    ②循环时间长开销大

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

    ④JVM的synchronized的机制中也用了CAS的锁机制

三、JVM的内存模型

线程之间的通信方式:共享内存(JAVA所用)和消息传递

堆内存是所有线程共享的

缓冲区和重排序这两个问题

重排序:编译器重排序、指令集并行重排序、内存系统重排序【JMM会限制编译器重排序,也会通过插入内存屏障的指令的方式来禁止一些处理器重排序】

由于处理器使用了写缓冲区,所以会对storeload操作进行重排序【这个屏障会把当前所有写缓冲区的数据全部刷新到内存中】

as-if-serial[同一线程内的数据依赖性]

happen-before[多线程之间存在可见性,那么就一定存在happen-before规则]

顺序一致性模型

synchronized及临界区内的代码是可以重排序的

1、volatile语义:

  严格限制volatile变量和普通变量的重排序

  volatile的单个变量的读写操作具有原子性

  对一个volatile变量的读,总是能看到之前最后一次对这个变量的写

  volatile的写和锁的释放具有相同的内存语义

  volatile的读和锁的获取具有相同的内存语义

  当写一个volatile变量时,JMM会把该线程对应的本地内存中的所有写的共享变量值刷新到主存中

  当读一个volatile变量时,JMM会把该线程对应的本地内存值为无效。线程接下来将从主内从中读取所有读的共享变量

  内存语义的实现:1、storestore  volatile写  storeload  2、volatile读  loadload loadstore

  用途:一写多读

2、锁的内存语义:

  临界区互斥执行

  当线程释放锁时,JMM会把该线程对应的本地内存中的共享变量刷新到主内存中

  当线程获取锁时,JMM会把该线程对应的本地内存值为无效,从主内从中刷新共享变量

  内存语义的实现:

  CAS(compareAndSwap())同时具有volatile的写和读的内存语义

3、final的内存语义:

  (1)在构造函数中对final域的写入和把这个堆对象赋值给它的引用,他们之间不能重排序

  (2)初次读一个包含final域的对象的引用,与随后初次读这个final域,这两个操作之间不能进行重排序

垃圾回收器:

  1、垃圾回收线程是怎么让其他用户线程到安全点都自动停止下来的?

    主动式中断:不对线程操作,仅仅设置中断标志;每个线程执行时主动轮询这个标志,发现中断标志为真时,就自己中断挂起;轮询标志的地方和安全点是重合的。

  2、垃圾回收线程是怎么让其他用户线程到安全区域都自动停止下来的?

    当位于安全区域的线程在线程离开安全区域时,会检测系统是否已经完成了根节点的枚举,如果没有完成,则必须等待收到离开区域的信号为止。

原文地址:https://www.cnblogs.com/erdanyang/p/12346767.html