a:可见性:一个线程修改了某个共享变量的值,其余线程可以立马得知这个修改。java
b:禁止特定的处理器重排序。多线程
volatile的内存语义:并发
1.当写一个volatile变量的时候,jmm会把本地内存中的共享变量刷新到主内存。框架
2.当读一个volatile变量的是时候,jmm会把线程本地内存的值设置为无效,而后从主内存中读取共享变量。jvm
volatile的重排序有三个规则:性能
1.当第二个操做为volatile写的时候,第一个操做不论是什么,都不容许重排序。优化
2.当第一个操做为volatile读的时候,第二个操做不论是什么,都不容许重排序。spa
3.当第一个操做为volatile写的时候,第二个操做是volatile读的时候,不容许重排序。操作系统
除此之外的状况,都运行重排序。而重排序的实现是靠加入内存屏障来实现的。内存屏障时用来禁止特定的重排序的cpu指令。包括4中,loadload,store store,store load与load/store。load能够理解为读操做,store能够理解为写操做,举例说明,loadload是保证在第二个load和其余一系列操做以前要确保第一个load的读操做完成。store store是保证在第二个store及写操做以前,第一个store写操做对其余处理器可见。其中store load的开销最大,是个万能屏障,兼具其余三个屏障的功能。线程
synchronized和lock的底层实现和对比:
synchronized是经过jvm原生实现的,其中能够分为给代码块加锁,给方法加锁,给静态类加锁。给代码块加锁锁住的是加锁的对象,给方法加锁锁住的是对象实例,给静态类加锁锁住的是整个类。
jvm经过进入和退出monitor来实现代码块和方法的同步。其中给代码块加锁能够理解为在方法的入口和出口分别加入了monitorenter和monitorexit字节码指令来实现的。必需要保证有一个monitorenter对应一个monitorexit,进入到monitorenter就表示拿到了相应的锁。java1.6以前synchronized能够说是重量级锁,1.6以后对synchronized作出的优化使得synchronized没有那么重量级了,加入了锁粗化,自旋锁和自适应自旋,锁消除,轻量级锁,偏向锁。
锁粗化是对对一个代码块家了不少锁,因为要不停的进入和出去加大的开销,能够把一部分联系紧密的代码块合并为一个锁或者少许的锁,使锁的力度变粗。
锁消除是当代码块中有锁可是检测到不存在竞争没有必要加锁的时候就把锁去掉。
自旋锁:同步的时候阻塞会影响性能,挂起线程和恢复线程的操做都须要转入内核态来完成,这些操做给系统的并发性能带来的很大的压力。在不少共享数据的锁定状态以后持续很短的一段时间,为了这段时间去挂起和恢复线程并不值得。若是物理机上不止一个处理器,能让两个或以上的线程同时并行执行,咱们可让后面请求锁的线程“稍等一下”,可是不放弃cpu,看看持有锁的线程是否很快就会释放锁。为了让线程等待,咱们只需让线程执行一个等待,这就是自旋。自旋值默认是10。1.6中引入了自适应的自旋,若是前一个刚刚得到过锁,而且持有锁的线程还在进行中,那么虚拟机会认为下一次自旋也有可能成功,进而容许自旋等待更长时间。对于不多得到的锁,直接放弃自旋,避免资源浪费,直接挂起线程。
轻量级锁:轻量级锁是相对于重量级锁而言的,它的本意是在没有多线程竞争的前提下,减小传统的重量级锁使用操做系统互斥量产生的性能消耗。在进入代码块的时候,若是此同步对象没有被锁定,也就是锁标志位是01状态,虚拟机首先在当前线程的栈帧上创建一个锁记录(lock record),用于存储Mark world的拷贝,而后虚拟机将使用cas操做尝试将对象的Mark world更新指向lock record的指针。更新成功了那么该线程就拥有了锁,而且对象的锁标志位将装换为00,即表示此对象处于轻量级锁的状态。更新失败了,虚拟机首先检查Mark world是否指向lock record,是的话说明当前线程已经拥有了这个对象的锁,那就直接进去代码块继续执行,不然说明锁对象已经被其余线程抢占了。若是有两个以上的对象争用一个锁,那么轻量级锁再也不有效,升级为重量级锁,锁状态变为10,mark word中存储的就是重量级锁的指针。
偏向锁:若是说轻量级锁是在无竞争的条件下使用cas操做去消除同步使用的互斥量,那么偏向锁就是在无竞争的条件下把整个同步都省掉,连cas操做都不作了。偏向的偏,意思就是若是获取了锁,下一个获取的话偏向由上一次获取它的线程来获取。当线程第一次获取到锁对象,状态改写为01,而后使用cas操做,把获取到锁的线程的id记录在mark word中,若是cas成功,持有偏向锁的线程之后每次进入这个锁相关的同步块时,虚拟机均可以不在进行任何同步操做。当有另一个线程去尝试获取这个锁时,偏向模式宣告结束。根据锁对象目前是否处于锁定的状态,撤销偏向后恢复到未锁定或轻量级锁定。
而lock是Java写的,基于aqs框架,须要显示的获取锁和释放锁,而且包含在try catch finally语句块里面,一样是可重入锁,lock还提供了比synchronized更强大的一些功能。主要包括三点:
1.可中断获取锁,获取锁的时候能够定时,若是过了这个时间仍是没有得到锁,那么就改成作其余事情。
2.能够绑定多个条件。synchronized里面,若是用wait、notify的话,实现的是隐含的一个条件,若是要和多于一个的条件绑定的话须要再建立锁。而lock不须要,一个锁能够产生多个condition,只须要经过lock的newcondition方法便可。
3.能够实现公平锁。