Java并发编程-开篇:volatile与synchronized

关于多线程的问题,一直是面试过程中最令笔者头痛的问题。因为项目开发过程中,很少会遇到多线程的开发任务(可能是本人还比较low)。所以打算认真来学习一下多线程、高并发相关的知识。以《Java并发编程的艺术》这本书作为主要学习资料来看看。并且记录在自己的博当中,作为读书笔记。

Java中所使用的并发机制依赖于JVM的实现和CPU指令。

volatile关键字的应用:

Java语言规范中对volatile的定义如下:Java编程语言允许线程访问共享变量,为确保共享变量能被准确和一致地更新,线程应该确保通过排他锁单独获得这个变量。Java语言提供了volatile,在某些情况下比锁要更加方便。如果一个字段被申明成volatile,Java线程内存模型确保所有线程看到这个变量的值是一致的。

Java的并发机制是由JVM和CPU指令来保障的。那么volatile变量修饰的共享变量再被多个线程操作的时候会有什么不同呢?就是编译形成的字节码转换为汇编指令之后,votaile变量的操作命令是加Lcok前缀的。Lock前缀的指令在多核处理器下会发生两件事情:

    1)当前处理器缓存行的数据回写到系统内存(工作内存回写到主内存);

    2)这个回写的操作会使其他CPU里缓存了该地址的数据无效(那么再用到的时候就需要从主内存更新到工作内存中)。

至于JVM和CPU的底层实现原理,那就不做考究了,如果面试官还追究这样的问题,我觉得可能他是刁难人的。

紧接着来看synchronized的实现原理以及应用:

synchronized实现同步的基础:Java中的每一个对象都可以作为锁。具体表现形式为以下3种形式:

  • 对于普通方法同步,锁是当前实例对象;
  • 对于静态同步方法,锁是当前类的Class对象;
  • 对于同步方法块,锁是synchronized括号里配置的对象。

JVM规范中可以看到synchronized在JVM里的实现原理,JVM基于进入和退出Monitor对象(每个对象都有一个监视器锁)来实现方法同步和代码块同步,两者的实现方式略有不同。代码块是使用monitorenter和monitorexit指令实现的,而方法同步是使用另一种方式实现的(反编译之后,可以看到常量池中多了ACC_SYNCHRONIZED标示符,那么如果有这个标识符,那么就需要去获取Monitor了),细节在JVM规范里并没有详细说明。但是,方法的同步也可以使用这两个命令来实现。都是基于Monitor的。

synchronized用的锁是存在于Java对象头(Mark  Word(存储对象的HashCode、分代年龄和锁标记位))里的,运行期间,Mark Word里存储的数据会跟随着锁标志位的变化而变化。锁的级别有不同,分别为:无锁状态、偏向锁、轻量级锁、重量级锁。

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

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章