如何理解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.

相关内容

热门资讯

安卓9系统怎样应用分身,轻松实... 你有没有发现,手机里的APP越来越多,有时候一个APP里还要处理好多任务,分身功能简直就是救星啊!今...
获取安卓系统的ip地址,轻松获... 你有没有想过,你的安卓手机里隐藏着一个神秘的IP地址?没错,就是那个能让你在网络世界里找到自己的小秘...
LG彩电安卓系统升级,畅享智能... 你家的LG彩电是不是最近有点儿“闹别扭”,屏幕上时不时地跳出个升级提示?别急,今天就来给你详细说说这...
阴阳师安卓苹果系统,安卓与苹果... 亲爱的玩家们,你是否曾在深夜里,手握手机,沉浸在阴阳师的神秘世界?今天,就让我带你一起探索这款风靡全...
华为安卓系统区别在哪,独特创新... 你知道吗?最近手机圈里可是热闹非凡,尤其是华为的新动作,让很多人眼睛都瞪大了。没错,我说的就是华为自...
怎么重新刷安卓手机系统,深度解... 手机用久了,是不是感觉卡顿得厉害?别急,今天就来教你怎么重新刷安卓手机系统,让你的手机焕然一新,速度...
刷正版安卓系统教程,刷正版安卓... 你有没有想过,让你的安卓手机焕然一新,体验一把正版系统的魅力呢?别急,今天就来手把手教你如何刷正版安...
移动支撑系统安卓版,助力移动办... 你有没有发现,现在的生活越来越离不开手机了?无论是工作还是娱乐,手机几乎成了我们生活的必需品。而今天...
安卓怎么进win系统界面,安卓... 亲爱的安卓用户,你是否曾幻想过在安卓设备上直接体验Windows系统的魅力?别再羡慕那些Window...
incall可以升级安卓系统吗... 你有没有想过,你的手机是不是也能像电脑一样,时不时地来个系统升级呢?今天,咱们就来聊聊这个话题——i...
安卓系统带农历软件,尽享传统节... 你知道吗?现在智能手机上有个特别实用的功能,那就是农历显示。对于咱们中国人来说,农历可是有着深厚的历...
安卓系统资源占用高,揭秘原因与... 你有没有发现,你的安卓手机最近变得越来越慢了?是不是觉得打开一个应用都要等半天,甚至有时候还会卡死?...
安卓10的系统有哪些,功能升级... 你有没有发现,你的安卓手机最近是不是变得有点不一样了?没错,就是那个神秘的安卓10系统!它就像一位魔...
固态硬盘系统迁移到安卓,固态硬... 你有没有想过,把你的固态硬盘系统迁移到安卓设备上,是不是能让你在移动办公或者娱乐时更加得心应手呢?想...
平板电脑能玩安卓系统吗,畅享丰... 你有没有想过,平板电脑竟然也能玩安卓系统?这可不是天方夜谭,而是科技发展的新趋势。想象你手中的平板瞬...
安卓刷精简系统下载,轻松打造高... 你有没有想过,你的安卓手机是不是有点儿“臃肿”了呢?运行速度慢,电池续航短,有时候还卡得要命。别急,...
安卓子系统windows11,... 你知道吗?最近科技圈可是炸开了锅,因为安卓子系统在Windows 11上的兼容性成了大家热议的话题。...
电脑里怎么下载安卓系统,电脑端... 你有没有想过,你的电脑里也能装上安卓系统呢?没错,就是那个让你手机不离手的安卓!今天,就让我来带你一...
索尼相机魔改安卓系统,魔改系统... 你知道吗?最近在摄影圈里掀起了一股热潮,那就是索尼相机魔改安卓系统。这可不是一般的改装,而是让这些专...
安卓系统哪家的最流畅,安卓系统... 你有没有想过,为什么你的手机有时候像蜗牛一样慢吞吞的,而别人的手机却能像风一样快?这背后,其实就是安...