synchronized介绍

synchronized

原理:

从JVM规范中可以看到Synchonized在JVM里的实现原理,JVM基于进入和退出Monitor对象来实现方法同步和代码块同步,但两者的实现细节不一样。代码块同步是使用monitorenter 和monitorexit指令实现的,而方法同步是使用另外一种方式实现的,通过编译之后,同步方法与普通方法不同是被加入了一个acc_synchronized标识,然后jvm通过识别这个标识来确定这是一个同步方法。但是,方法的同步同样可以使用这两个指令来实现。 monitorenter指令是在编译后插入到同步代码块的开始位置,而monitorexit是插入到结束位置和异常处,JVM要保证每个monitorenter必须有对应的monitorexit与之配对。任何对象都有一个monitor与之关联,线程执行到monitorenter 指令时,将会尝试获取对象所对应的monitor的所有权,即尝试获得对象的锁。

优化

在早期的时候,sync他是重量级的锁,就是操作系统层面的锁,需要从用户态切换到内核态。这是非常耗时的操作,性能低。

在java6之后,引入了一系列的优化,锁升级。随着的竞争加剧,锁会一步步升级。从低到高是:无锁,偏向锁,轻量级锁,重量级锁,并且是不可降级的。

对象的内存分布:

0 - 4 markword

4 - 8 markword

markword存储锁信息,gc信息

8 - 12 指向.class

以上统称为对象头。

后面是成员变量占的位置,不足8的整数倍,需要补字节到8的整数倍

在这里插入图片描述

重量级锁中指向互斥量的指针实际是指向monitor对象。

锁升级:

由低到高:无锁状态、偏向锁状态、轻量级锁状态(自旋锁)和重量级锁状态
在这里插入图片描述

偏向锁,轻量级锁,都是在用户态的。

用户态–》内核态 : 操作系统执行终端0x80

轻量级锁-------重量级锁:

jdk1.6之前:可以设置升级的线程自旋的次数阈值或者自旋的线程数阈值

jdk1.6之后:自适应,别设置了。

jvm启动时候的,有一段时间是不会启动偏向锁的,因为偏向锁适合的无线程竞争的情况,在明确知道会有多线程竞争的情况的时候,请直接使用轻量级锁,此时轻量级锁的效率可能更高,因为开始就启动偏向锁,还是会升级为轻量级锁,但是中间有一个偏向锁撤销的过程,浪费cpu资源。jvm启动必定是存在多线程竞争,所以jvm会延迟开启偏向锁。默认4s,也可以通过参数设置(-xx: BiasedLockingStartDelay== yourValue (unit:ms)

禁用偏向锁:xx - UseBiasedLocking

匿名偏向:

markword中的线程指针没有指向任何线程:匿名偏向。

如果synchronized(对象),对象中的markword中的偏向锁指针指向当前线程数,这个时候就不是匿名偏向锁了。

自旋锁

​ 共享变量。

自旋锁缺点:

在这里插入图片描述

锁重入

一个方法可能被多个方法调用,而这个多个方法的同步逻辑不一样。

Java 中的 `synchronized` 关键字是用于实现线程同步的重要机制。它通过提供互斥锁、对象锁和类锁等功能,确保在多线程环境下数据的一致性和程序的稳定性。 ### 作用 1. **原子性**:保证一个操作或多个操作要么全部执行,要么全部不执行,中间不会被其他线程打断。 2. **可见性**:确保线程在释放锁之前对共享变量的修改对其他线程可见。 3. **有序性**:保证程序按照代码的顺序执行,防止指令重排导致的问题[^4]。 ### 使用方法 #### 修饰实例方法 当 `synchronized` 修饰实例方法时,锁住的是该方法所属对象的实例。这意味着如果多个线程试图同时调用同一个对象上的这个同步方法,则只有一个线程能够执行该方法。 ```java public class SyncThread implements Runnable { private static int count; public SyncThread() { count = 0; } public synchronized void method() { for (int i = 0; i < 5; i++) { try { System.out.println(Thread.currentThread().getName() + ":" + (count++)); Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } } public void run() { method(); } public int getCount() { return count; } } ``` #### 修饰静态方法 当 `synchronized` 修饰静态方法时,锁住的是当前类的 `Class` 对象。这表示即使创建了多个对象,只要这些对象属于同一个类,那么它们的静态同步方法在同一时刻也只能被一个线程访问。 ```java public class SynchronizedDemo { // ...其他代码... public static synchronized void testMethod1(){ // 方法体... } public static synchronized void testMethod2(){ // 方法体... } } ``` #### 同步代码块 有时并不需要将整个方法设为同步,而是只需要同步特定的代码段。这时可以使用同步代码块来减少锁定范围,提高并发性能。 ```java public void someMethod() { // 非同步代码... synchronized(this) { // 锁定当前对象 // 需要同步的代码... } // 更多非同步代码... } ``` 此外,还可以指定任意的对象作为锁: ```java private Object lock = new Object(); public void anotherMethod() { synchronized(lock) { // 使用自定义对象作为锁 // 需要同步的代码... } } ``` 选择合适的同步策略对于提升应用程序的性能至关重要。例如,在高并发场景下,应该尽量减小同步区域以降低线程间的竞争;而对于需要保护共享资源的情况,则应合理利用对象锁和类锁来确保数据一致性与系统稳定性[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

zhou吗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值