深入浅出AndroidAndroid技术知识

java多线程之二——Synchronized

2017-11-10  本文已影响209人  Vinctor

在java多线程并发编程中,Synchronized一直占有很重要的角色。Synchronized通过获取锁来实现同步。先来看一下,它的使用方法:

package com.Vinctor.Tst;

public class VinctorSyncDemo {

   public static synchronized void staticSyncMethod() {
       System.out.println("static synchronized");
   }

   public synchronized void normalSyncMethod() {
       System.out.println("normal  SyncMethod");
   }

   public void normalInnerMethod() {
       synchronized (this) {
           
       }
   }

   public void staticInnerMethod() {
       synchronized (VinctorSyncDemo.class) {

       }
   }
}

如上,分为三种方式:

我们将上述代码javap之后,看一下字节码指令:

image.png

可以看到看到staticInnerMetho方法在执行到synchronized代码块时,有两个指令monitorentermonitorexit,而下面异常表指向的位置10,也同样执行了monitorexit。可见同步代码块的实现是使用monitorentermonitorexit两个代码块进行获取锁已释放锁的,发生异常之后,也同样会释放锁。JVM 必须保证monitorentermonitorexit相对应。当监视器一个线程被持有时,那么这个线程就持有了锁,其他线程就不能获取这个监视器,直至monitorexit释放锁。同步方法同样也可以用着两个指令获取和释放锁。

当一个线程试图访问同步方法或者同步代码块时,首先需要获取锁,退出同步方法或者同步代码块时需要释放锁。可以看出锁在Synchronized中起到至关重要的作用。锁是什么呢?他是怎么存储的呢?
通过以前的文章,我们了解到实例对象存储在堆中,类对象存储在方法中,一个对象区域包括:对象头,对象数据,对其数据。此处对象头中存储着Synchronized的锁。对象头中有一个称为Mark Word的区域,存储着对象的 hashCode,分代年龄和锁标记位。如图:

image.png

运行期间,mark word 中存储的数据锁的标记位的变化而变化,其可能变化情况如下:

image.png

可以看到,分为很多锁:偏向锁,轻量级锁,重量级锁。

锁的分类

内置锁按照状态分为:无锁状态,偏向锁,轻量级锁,重量级锁。四中状态随着锁的竞争加剧而升级,但是不是回退降级。(本文仅讨论 HotSpot)

锁的升级

虚拟机开发人员研究发现,大多数情况下,多线程存在竞争的情况很少,为了避免同一线程多次进行锁的获取,故引入了偏向锁的概念。

当一个线程A访问同步代码块的时候,
首先检查锁标志位:如果为01(无锁或偏向锁),再检查是否为偏向锁,

锁升级轻量级锁之后,对象头中不在存储线程 ID 等信息,而是将这些信息拷贝至持有锁的线程栈中锁记录中,再将对象头指向该地址。上面的例子🌰中,

拥有轻量级锁的线程执行完同步代码块后,需要解锁轻量级锁,这是还是需要使用 CAS 操作,将栈中锁记录替换会对象头,如果成功,表示没有竞争;如果失败,表示存在竞争,其他线程尝试获取过锁,那就需要在释放锁的过程中,唤醒被挂起的线程。

贴一张收藏的流程图(出处不明,如侵权,请告知):


点击可放大,可查看原图

一个🌰

以上就是锁升级的过程,比较乱,举个现实生活的栗子,上厕所。厕所几位锁(对象)。

为了方便对比,我们定一个规则,当一个人上厕所时,需要将自己的牌子挂在厕所的门上,上完厕所,从厕所出来就不必摘下牌子,下次再上的时候就不需要挂了,只是看一下这个牌子是不是自己的就可以了。此为偏向锁。

当你再上厕所的过程中,小明过来了也想上厕所,就看到了牌子,不是自己的,想要把牌子换成自己的,但是这是你还在上厕所,没办法,小明只能在厕所门前晃来晃去,等着你上完厕所出来。如果你能在短时间里出来,那小明就进去上厕所了。此时为轻量级锁。

但是万一你闹肚子,小明也不可能一直在外面等着,这是他就选择回座位等着,并告诉你一声:“哥们,上完告诉我一声!”,这时的小明不在主动去想要获取锁,而是等着你上完厕所出来喊他,他才上厕所。这是即为重量级锁。

CAS

CAS,Compare and Swap即比较并替换,设计并发算法时常用到的一种技术。java 中的原子类以及concurrent包大量使用了该技术进行原子操作。

CAS有三个操作数:内存值V、旧的预期值A、要修改的值B,当且仅当预期值A和内存值V相同时,将内存值修改为B并返回true,否则什么都不做并返回false

JDK中有一个类Unsafe(sun.misc.Unsafe),它提供了硬件级别的原子操作。Unsafe类中的一个方法如下:

public final native boolean compareAndSwapInt(Object o, long offset,
                                              int expected,
                                              int x);

此为 native 方法,JVM 会将此方法映射为cmpxchgCPU 指令,该指令为原子操作,故多用于多线程环境中而不会产生数据错误。

上一篇下一篇

猜你喜欢

热点阅读