Synchronized一道墙

Synchronized一道墙

大家好,上次阿清的文章,提到了并发问题的三大根源,为了保证并发编程的一致性,Java提供了一个synchronized关键字来保证并发的正确执行。

synchronized能避免并发的三个问题,即操作原子性、缓存可见一致性、指令重排序 这三个并发问题。它能保证多线程的安全。



01


synchronized加锁的方式



分为显式加锁隐式加锁两种。

举两个🌰:

synchronized加锁的两种方法

(1)显式加锁:

public class Test {
    
     public  void  codeLock(){
         //显示加锁,锁的是当前Test实例对象
         synchronized(this){
             //Todo
         }
     }
    
     Object objA=new Object();
     public  void  objLock(){
         //显示加锁,锁的是objA对象
         synchronized(objA){
             //Todo
         }
     }
 }

(2)隐式加锁:

public class Test {
     //修饰在对象方法上,隐式加锁,效果等于synchronized(this),即Test的实例对象
     public synchronized void  thisLock(){
 
     }
}

public class Test {
     //修饰在类方法上,隐式加锁,效果等于synchronized(Test.class)
     public static synchronized void  classLock(){
     
     }
}

注意:Test.class 与 Test的实例对象在Java堆中是两个不一样的对象。

因此,若在同一个类中,分别为 xx.class对象 或 xx的实例对象加锁后,此时也许会导致并发出现错误。

大家看下面这段代码:

public class Test {
     // 静态变量 count
     static Integer count;
     
     //线程A执行此方法
     public synchronized void thisLock(){
         count++;
     }
     
     //线程B执行此方法
     public static synchronized void classLock(){
         count++;
     }
 
 }

此时,在多线程的情况下,count的数值便会发生错误,因为在 获得Test.class对象锁 与 Test的实例对象锁之后,都能够操作count变量。



02


synchronized的底层原理



synchronized的底层使用monitor进行加锁与解锁:

monitorenter进行加锁,monitorexit进行解锁。

加锁过程:

  1. 使用monitorenter进行锁的竞争。

  2. 获得锁后,访问共享资源,若获得锁失败,则进入等待队列。

  3. 分两种情况:

    • 若线程执行条件不成立,则进入wait状态,并释放锁,该线程进入等待队列,选择notify 或 notifyAll 唤醒等待队列中的其他线程。

    • 若条件成立,则正常执行,使用monitorexit退出临界区。

  4. 使用notify 、或notifyAll唤醒其他的线程


03


synchronized锁升级过程



锁升级过程:

  1. 无锁状态:此时不存在锁的竞争,无需加锁

  2. 偏向锁状态:经常只有一个线程来加锁避免加锁的开销,并不会真正的加锁,过程如下:

    Synchronized一道墙
  3. 轻量级锁状态:有线程来参与锁的竞争,但是锁竞争的时间很短。

    若申请锁不成功,通过自旋继续获取这个锁,超过一定时间后,则进行升级成重量级锁。

  4. 重量级锁状态:有大量的线程参与锁的竞争,冲突性很高。

    若尝试获取不到锁,线程则进入队列等待,等待前面获取锁的线程释放了锁之后再开启下一轮的锁竞争,这就是重量级锁,只有当锁升级为重量级锁的时候才真正意义上的在操作系统层面进行了加锁操作。

阿清用一张图来展示这个过程:

Synchronized一道墙
synchronized锁升级过程

为什么要进行锁升级?

因为重量级锁的获取与释放需要经过操作系统,这是一个十分浪费资源的操作。synchronized正是一个重量级锁。因此,在并发的情况下,为了提高运行的效率,在线程量不多的情况下,进行其他的约定(即偏向锁、轻量级锁)来避免出现过多的重量级锁。锁升级机制是对synchronized一种优化



04


总结



今天主要向大家介绍synchronized关键字。包括它的加锁方式,底层原理,以及锁升级过程。

其中锁升级过程,阿清在面试中就被问过。

加锁方式主要分为显式加锁与隐式加锁,以及加锁的对象是xx.class对象,还是xx的实例对象。

底层原理则要注意在获取锁资源后,需要判断执行的条件是否成立,若不成立,会调用wait、notify、notifyall方法。

关注六只栗子,面试不迷路。


作者    阿清

编辑   一口栗子  

原文始发于微信公众号(六只栗子):Synchronized一道墙

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/88583.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!