如何理解Synchronized
创始人
2025-06-01 10:52:44
0

synchronized用法

synchronized是java提供的一种解决多线程并发问题的内置锁,是目前java中解决并发问题最常用的方法,也是最简单的方法。从语法上讲,synchronized的用法可以分为三种,分别为同步实例方法,同步静态方法和同步代码块

同步实例方法

当一个类中的普通方法被synchronized修饰时,相当于对this对象加锁,这个方法被声明为同步方法。此时,多个线程并发调用同一个对象实例中被synchronized修饰的方法是线程安全的。
修饰同步方法

    public synchronized void methodHandler(){//方法逻辑}

演示多线程调用同一个方法出现线程安全问题

 1. 创建成员变量count,初始值为02. 创建方法increment()对count进行自增处理,该方法没有被synchronized修饰,多线程调用会出现线程安全问题。3. 创建execute()方法,实现多线程调用
    private Long count = 0L;
    public void incrementCount(){count++;}
    public Long execute() throws InterruptedException {Thread thread1 = new Thread(()->{IntStream.range(0,1000).forEach(i->incrementCount());});Thread thread2 =new Thread(()->{IntStream.range(0,1000).forEach(i->incrementCount());});//启动线程1 2thread1.start();thread2.start();//等待线程1和线程2执行完毕thread1.join();thread2.join();return count;}
        public static void main(String[] args) throws InterruptedException {SynchronizedTest synchronizedTest = new SynchronizedTest();System.out.println(synchronizedTest.execute());}

结果如图所示
在这里插入图片描述
通过多次运行测试,预期输出的值都是小于2000,产生了线程安全问题。
解决方法
在increment方法上添加synchronized关键字即可以解决线程安全问题

    public synchronized void incrementCount(){count++;}

在这里插入图片描述

同步静态方法

在java的静态方法上添加synchronized关键字对其进行修饰,当一个类的某个静态方法被synchronized修饰时,相当于对这个类的Class对象加锁,而一个类只对应一个Class对象。此时,无论创建多少个当前类的对象被synchronized修饰的静态方法,这个方法都是线程安全的。
修饰静态方法

    public static synchronized void methodHandler(){//方法逻辑}

代码示例

  private static Long count = 2000L;public static void decrementCount(){count--;}public static Long execute() throws InterruptedException {Thread thread1 = new Thread(()->{IntStream.range(0,1000).forEach(i->decrementCount());});Thread thread2 =new Thread(()->{IntStream.range(0,1000).forEach(i->decrementCount());});//启动线程1 2thread1.start();thread2.start();//等待线程1和线程2执行完毕thread1.join();thread2.join();return count;}
    public static void main(String[] args) throws InterruptedException {System.out.println(execute());}

多次运行预期结果为0,实际大于0,说明在多线程调用之间产生了线程安全问题。
在这里插入图片描述
解决方法
在静态方法上添加synchronized关键字,如下所示:

    public static synchronized void decrementCount(){count--;}

在这里插入图片描述

同步代码块

synchronized关键字修饰的方法可以保证当前方法是线程安全的,但是如果修饰的方法临界区域较大,或者方法的业务逻辑过多,则可能影响程序的执行效率。此时最好的方法是将一个大的方法分成小的临界区代码。
比如下面的代码

	private static Long countA = 0L;private static Long countB = 0L;public static synchronized void incrementCount(){countA++;countB++;}

在incrementCount方法中分别对countA和countB进行自增操作,对于countA和countB来说,面对的是两个不同的临界区资源。当某个线程进入incrementCount方法时,会对整个方法加锁,占用全部资源。即使在线程对countA进行自增操作而没有对countB进行自增操作时,也会占用countB的资源,其他线程只有等到当前线程执行完countA和countB的自增操作并释放synchronized锁后才能进入incrementCount方法。
所以,如果只将synchronized添加到方法上,其方法包含互不影响的多个临界区资源时,就会造成临界区资源的限制等待,影响程序的性能。为了提高程序的性能,可以将synchronized添加到方法体内,也就是synchronized修饰代码块。
synchronized修饰代码块可以分为两种情况,一种是对某个对象加锁,另一种是对类的class对象加锁。
对某个对象加锁

    public void methodHandler(){synchronized (obj){}//方法逻辑}

当obj为this时,相当于在普通方法上添加synchronized关键字。
对类的Class对象加锁

    public static void methodHandler(){synchronized (SynchronizedTest3.class){}}

上述方法相当于在类的静态方法上添加synchronized关键字。
可以将countA和countB当做两个互不影响的临界区资源,可以修改为下面的代码:

  private static Long countA = 0L;private static Long countB = 0L;private Object countALock = new Object();private Object countBLock = new Object();public void incrementCount(){synchronized (countALock){countA++;    }synchronized (countBLock){countB++;   }}

当线程进入incrementCount()方法后,正在执行countB的自增操作时,其他线程依然可以进入incrementCount方法中执行countA的自增操作,因此提高了程序的执行效率。同时incrementCount方法是线程安全的。

synchronized底层原理

synchronized是基于JVM中的monitor锁实现的,jdk1.5版本之前的synchronized锁性能较低,但是从jdk1.6版本开始,对synchronized锁进行了大量的优化,引入了锁粗化,锁消除,偏向锁,轻量级锁,适应性自旋等技术来提升synchronized锁的性能。
当synchronized修饰方式时,当前方法会比普通方法在常量池中多一个ACC_SYNCHRONIZED标识符,synchronized修饰方法的核心原理如下图所示:
在这里插入图片描述
JVM在执行程序时,会根据这个ACC_SYNCHRONIZED标识符完成方法的同步。如果调用了synchronized修饰的方法,则调用的指令会检查方法是否设置了ACC_SYNCHRONZIED标识符。
如果方法设置了SYNCHRONZIED标识符,则当前线程先获取monitor对象,在获取成功后执行同步代码逻辑,执行完毕释放monitor对象。同一时刻,只会有一个线程获取monitor对象成功,进入方法体执行方法逻辑。在当前线程执行方法逻辑之前。也就是当前县城释放monitor对象之前,其他线程无法获取同一个monitor对象。从而保证了同一时刻只能有一个线程进入被synchronized修饰的方法中执行方法体的逻辑。
当synchronized修饰代码块时,synchronized关键字会被编译成monitorenter和monitorexit两条指令,monitorenter指令会被放在同步代码的前面,monitorexit指令会被放在同步代码的后面,synchronized修饰代码快的核心原理如下图:
在这里插入图片描述
由上图可以看出,当源码中使用了synchronized修饰代码块,源码中被编译字节码后,同步代码的逻辑前后分别被添加monitorenter和monitorexit指令,使得同一时刻只能一个线程进入monitorenter和monitorexit两条指令中间的同步代码块。
synchronized修饰方法和修饰代码块,在底层的实现上并没有本质区别,只是当synchronized修饰方法时,不需要JVM编译出的字节码完成加锁操作,是一种隐式的实现方式。而当synchronized修饰代码块时,是通过编译出的字节码生成的monitorenter和monitorexit指令完成的,在字节码层面上是一种现实的实现方式。
无论synchronized修饰方法,还是修饰代码块,底层都是通过JVM调用操作系统的Mutex锁实现的,当线程被阻塞时会被挂起,等待CPU重新调度,这会导致线程在操作系统的用户态和内核态之间切换,影响程序的执行性能。

Monitor锁原理

synchronized底层是基于Monitor锁实现的,而monitor锁是基于操作系统的Mutex锁实现的,Mutex锁是操作系统级别的重量级锁,其性能较低。
在java中,创建出来的任何一个对象在JVM中都会关联一个Monitor对象,当Monitor对象被一个java对象持有后,这个Monitor对象将处于锁定状态,synchronized在JVM底层本质上都是基于进入和退出Monitor对象来实现同步方法和同步代码快的。
在HotSpot JVM中,Montior是由ObjectMonitor实现的,ObjectMonitor存在两个集合,分别为_waitSet和_EntryList。每个在竞争锁时未获取到锁的线程都会被封装成ObjectWaiter对象,而_waitSet和_EntryList集合就用来存储这些ObjectWaiter对象。
另外,ObjectMonitor中的_owner用来指向获取到ObjectMonitor对象的线程。当一个线程获取到ObjectMonitor对象时,这个ObjectMonitor对象就存储在当前对象的对象头中的MarkWord中(实际上存储的是指向ObjectMonitor对象的指针)。所以,在java中可以使用任意对象作为synchronized锁对象。
当多个线程同时访问一个被synchronized修饰的方法或代码块时,synchronized加锁与解锁在JVM底层的实现大致分为如下几个步骤:

  1. 进入_EntryList集合,当某个线程获取到Monitor对象后,这个线程就会进入_Owner区域,同时,会把Monitor对象中的_owner变量复制为当前线程,并把Monitor对象中的_count变量加1
  2. 当线程调用wait()方法时,当前线程会释放持有的Monitor对象,并且把Monitor对象中的_owner变量设置为null,_count变量减1.同时,当前线程会进入_WaitSet集合中等待被再次唤醒。
  3. 如果获取到Monitor对象的线程执行完毕,则也会释放Monitor对象,当Monitor对象中的_owner变量设置为null,_count变量值减1.

相关内容

热门资讯

原生安卓系统怎样升级,从基础到... 你有没有发现,你的安卓手机用久了,有时候就像老牛拉车一样,慢吞吞的?别急,今天就来给你支个招,让你的...
安卓13系统怎么开发,开发者的... 你有没有听说安卓13系统已经发布了?这可是个大新闻呢!作为一个热衷于手机开发的小伙伴,你是不是也跃跃...
安卓q系统镜像下载,轻松升级体... 你有没有听说安卓Q系统已经发布了?这可是安卓家族里的一大亮点呢!今天,我就要来给你详细介绍一下安卓Q...
安卓系统色彩校正软件,打造个性... 你有没有发现,手机屏幕的色彩有时候会让人感觉不太对劲?有时候,画面看起来有点灰蒙蒙的,有时候又太艳丽...
苹果能否下个安卓系统,开启新篇... 你有没有想过,苹果的iOS系统会不会有一天突然宣布,它要拥抱安卓的大家庭呢?想象iPhone和iPa...
树莓派 装 安卓系统,轻松安装... 你有没有想过,用树莓派装上安卓系统,那会是怎样一番景象呢?想象一个迷你电脑,竟然能运行起我们日常使用...
安卓系统怎么打印小票,安卓系统... 你是不是也遇到了这样的烦恼:手机里存了好多重要的小票,但是想打印出来保存或者报销,却发现安卓系统里的...
安卓10安装系统应用,轻松上手... 你有没有发现,你的安卓手机最近是不是有点儿“慢吞吞”的?别急,别急,今天就来给你支个招——升级安卓1...
美国不提安卓系统华为,迈向自主... 华为与美国:一场关于技术、市场与政策的较量在当今这个数字化的世界里,智能手机已经成为我们生活中不可或...
安卓系统怎么打开ppt,选择文... 你有没有遇到过这种情况:手里拿着安卓手机,突然需要打开一个PPT文件,却怎么也找不到方法?别急,今天...
谷歌退回到安卓系统,探索创新未... 你知道吗?最近科技圈可是炸开了锅,谷歌竟然宣布要退回到安卓系统!这可不是一个简单的决定,背后肯定有着...
安卓系统待机耗电多少,深度解析... 你有没有发现,手机电量总是不经用?尤其是安卓系统,有时候明明没怎么用,电量就“嗖”的一下子就下去了。...
小米主题安卓原生系统,安卓原生... 亲爱的手机控们,你是否曾为手机界面单调乏味而烦恼?想要给手机换换“衣服”,让它焕然一新?那就得聊聊小...
voyov1安卓系统,探索创新... 你有没有发现,最近你的手机是不是变得越来越流畅了?没错,我要说的就是那个让手机焕发青春的Vivo V...
电脑刷安卓tv系统,轻松打造智... 你有没有想过,家里的安卓电视突然变得卡顿,反应迟钝,是不是时候给它来个“大保健”了?没错,今天就要来...
安卓系统即将要收费,未来手机应... 你知道吗?最近有个大消息在科技圈里炸开了锅,那就是安卓系统可能要开始收费了!这可不是开玩笑的,这可是...
雷凌车载安卓系统,智能出行新体... 你有没有发现,现在的汽车越来越智能了?这不,我最近就体验了一把雷凌车载安卓系统的魅力。它就像一个聪明...
怎样拍照好看安卓系统,轻松拍出... 拍照好看,安卓系统也能轻松搞定!在这个看脸的时代,拍照已经成为每个人生活中不可或缺的一部分。无论是记...
安卓车机系统音频,安卓车机系统... 你有没有发现,现在越来越多的汽车都开始搭载智能车机系统了?这不,咱们就来聊聊安卓车机系统在音频方面的...
老苹果手机安卓系统,兼容与创新... 你手里那台老苹果手机,是不是已经陪你走过了不少风风雨雨?现在,它竟然还能装上安卓系统?这可不是天方夜...