A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 舞出我人生 于 2019-8-14 14:55 编辑

多线程共享内存,需要解决两个问题:
     竞态条件
     内存可见性
可以使用Synchronized 解决上面的问题
从几个方面来理解synchronized
     可重入性
    内存可见性
    死锁
          可重入性是synchronized 一个特点,原理如下:
          可重入锁是记录锁的持有线程,和持有数量来实现的,当被synchronized 保护的代码时 ,检查对象是否已经被锁,如果是,那么检查是否被当前的线程锁定。如果是,增加持有数量,如果不是被当前线程锁定,才加入等待队列,当释放锁时,减少持有数量,当数量变为0时才释放这个锁。

          内存可见性
          synchronized 处理保证原子操作外,还可以保证内存可见性。在释放锁的时候,内存会写回内存,获得锁,都会从内存中读取最新的数据。相对volatile 来说,                  synchronized保持可见性的成本比较高。
       提个问题,volitale 内存可见性是怎么实现的?什么是指令重排?
    死锁
    举个例子,A,B两个线程,A持有锁a,等待线程B,B持有锁b,等待线程B,这样就陷入了相互等待?
    提个问题, 死锁的条件是啥? 如何避免死锁?
   简单的来讲,执行Synchronized方法过程大致如下:
   尝试获得锁,如果能得到锁,那么继续,如果不能得到锁,那么进入等待队列
    执行synchronized 修饰方法实体
     释放锁,等待队列中有等待的线程,取出一个并且唤醒
      synchronized可以来干什么?
               修饰代码块
               修饰一个方法
               修饰一个静态的方法
               修饰一个类
     synchronized不需要主动释放锁
      JVM 基于进入和退出Monitor对象来实现方法同步和代码块同步,monitorenter指令是在同步代码块开始位置,monitorexit是插入到方法结束处和异常处


    Synchronized的实现原理
    Synchronized是JVM实现的一种锁,其中锁的获取和释放分别是monitorenter和monitorexit指令。
    该锁在实现上分为了偏向锁、轻量级锁和重量级锁,其中偏向锁在1.6是默认开启的,轻量级锁在多线程竞争的情况下会膨胀成重量级锁
    偏向锁
      当一线程访问同步块的时候,会在对象头和栈帧中的锁记录存储偏向锁的线程ID,以后该线程进入或者退出同步块时,不需要进行CAS操作来加锁或者解锁,只需要简单测试一下对象头的Mark Word里是否存储着指向当前线程的偏向锁。如果测试成功,表示该线程获得了锁。如果测试失败,需要再测试一下Mark Word中偏向锁的标识是否设置为1(表示当前是偏向锁) ,如果没有设置,则使用CAS竞争锁,如果设置了,则尝试使用CAS将对象头的偏向锁指向当前线程。

  轻量级锁
    加锁
    线程在执行同步块之前,JVM会先在当前线程的栈帧中创建用于存储锁的记录空间,并将对象头中的MarkWord字段复制到锁记录中,当然会线程尝试使用CAS将对象头中的Mark Word替换成指向锁记录的指针。如果获得成功,当前线程获得了锁,如果失败,表示其他线程竞争锁,当前线程采用CAS来获取锁。
    解锁
     轻量级锁解锁的时候,会使用原子的CAS操作将 锁记录中的Mark Word 替换会对象头,如果成功,表示没有竞争发送,如果失败,当前存在锁竞争,锁就会膨胀成重量级锁。一旦锁变成了重量级锁,就不会再恢复到轻量级锁,当锁处于这个状态下,其他线程试图获取锁是,都会被阻塞,当持有的线程释放锁之后会唤醒这些线程,被唤醒的线程开始新一轮的锁争抢。
           
版权声明:本文为CSDN博主「wangxiaoming」的原创文章,遵循CC 4.0 by-sa版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/wangming520liwei/article/details/88117176


0 个回复

您需要登录后才可以回帖 登录 | 加入黑马