自Java 6/Java 7开始,Java虚拟机对内部锁的实现进行了一些优化。这些优化主要包括锁消除(Lock Elision)、锁粗化(Lock Coarsening)、偏向锁(Biased Locking)以及适应性自旋锁(Adaptive Locking)。这些优化仅在Java虚拟机server模式下起作用(即运行Java程序时我们可能需要在命令行中指定Java虚拟机参数“-server”以开启这些优化)
Java对象头
要了解Synchronized为什么实现线程同步,就必须先了解Java对象头的概念。在HotSpot虚拟机中,对象在内存中存储的布局分为对象头、实例数据、对齐填充3块区域。
对象头包含两部分,第一部分存储对象自身的运行时数据,例如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向线程ID、偏向时间戳等,官方称它为“Mark Word”,锁的标志位信息就存在Mark Word中
以上信息参考《深入理解Java虚拟机》
Monitor
Monitor理解为一个同步工具或机制,通常被描述为一个对象。每一个Java对象都有一把看不见的锁,称为内部锁或者Monitor锁。
Monitor是线程私有的数据结构,每一个线程都有一个可用monitor record列表,同时还有一个全局的可用列表。每一个被锁住的对象都会和一个monitor关联,同时monitor中有一个Owner字段存放拥有该锁的线程的唯一标识,表示该锁被这个线程占用。
synchronized通过Monitor来实现线程同步,Monitor是依赖于底层的操作系统的Mutex Lock(互斥锁)来实现的线程同步。
自旋锁
Java虚拟机的开发工程师们在分析过大量数据后发现:共享数据的锁定状态一般只会持续很短的一段时间,为了这段时间去挂起和恢复线程其实并不值得。因此可以让后面来的线程“稍微等一下”,但是并不放弃处理器的执行时间,看看持有锁的线程会不会很快释放锁。这个“稍微等一下”的过程就是自旋。自旋锁在JDK1.6中默认开启。自旋锁和阻塞锁最大的区别就是,到底要不要放弃处理器的执行时间。对于阻塞锁和自旋锁来说,都是要等待获得共享资源。但是阻塞锁是放弃了CPU时间,进入了等待区,等待被唤醒。而自旋锁是一直“自旋”在那里,时刻的检查共享资源是否可以被访问JDK 1.6引入了更加聪明的自旋锁,即自适应自旋锁。所谓自适应就意味着自旋的次数不再是固定的,它是由前一次在同一个锁上的自旋时间及锁的拥有者的状态来决定。它怎么做呢?线程如果自旋成功了,那么下次自旋的次数会更加多,因为虚拟机认为既然上次成功了,那么此次自旋也很有可能会再次成功,那么它就会允许自旋等待持续的次数更多。反之,如果对于某个锁,很少有自旋能够成功的,那么在以后要或者这个锁的时候自旋的次数会减少甚至省略掉自旋过程,以免浪费处理器资源。偏向锁
偏向锁,顾名思义,它会偏向于第一个访问锁的线程,如果在运行过程中,同步锁只有一个线程访问,不存在多线程争用的情况,则线程是不需要触发同步的,这种情况下,就会给线程加一个偏向锁。它通过消除资源无竞争情况下的同步原语,进一步提高了程序的运行性能。如果在运行过程中,遇到了其他线程抢占锁,则持有偏向锁的线程会被挂起,JVM会消除它身上的偏向锁,将锁恢复到标准的轻量级锁。如果线程争用激烈,那么应该禁用偏向锁(高并发的应用要禁用掉偏向锁)开启偏向锁:-XX:+UseBiasedLocking -XX:BiasedLockingStartupDelay=0关闭偏向锁:-XX:-UseBiasedLocking轻量级锁
轻量级锁是由偏向所升级来的,偏向锁运行在一个线程进入同步块的情况下,当第二个线程加入锁争用的时候,偏向锁就会升级为轻量级锁与偏向锁的区别是,引入偏向锁是假设同一个锁都是由同一线程多次获得,而轻量级锁是假设同一个锁是由n个线程交替获得;相同点是都是假设不存在多线程竞争引入轻量级锁的主要目的是,在没有多线程竞争的前提下,减少传统的重量级锁使用操作系统互斥量产生的性能消耗(多指时间消耗)触发轻量级锁的条件是当关闭偏向锁功能或者多个线程竞争偏向锁导致偏向锁升级为轻量级锁,则会尝试获取轻量级锁,此时Mark Word的结构也变为轻量级锁的结构。如果存在多个线程同一时间访问同一锁的场合,就会导致轻量级锁膨胀为重量级锁。重量级锁
锁标志的状态值变为10,此时Mark Word中存储的是指向重量级锁的指针,此时等待锁的线程都会进入阻塞状态。
synchronized
synchronized修饰方法实例方法此时锁住的是类的实例对象。例如:public synchronized void method(){}静态方法此时锁住的是类对象。例如:public static synchronized void method(){}synchronized修饰代码块实例对象锁住的是类的实例对象。例如:synchronized(this){}class对象锁住的是类对象。例如:synchronized(Demo.class){}任意实例对象object锁住的是实例对象object。例如:String str = ""; synchronized(str){}今天主要写了一下JVM级别的锁,自旋锁、轻量级锁以及synchronized,明天继续锁的章节,将会介绍JDK级别的锁,包括ReentrantLock、ReadWriteLock以及著名的AQS,明天见。
举报/反馈

猿圈话源

147获赞 48粉丝
java小学生一枚,专注于分享java干货
关注
0
0
收藏
分享