synchronized 是 Java 中的关键字,用于实现线程间的同步。
它可以用于方法或代码块上,确保同一时刻只有一个线程可以执行被 synchronized 修饰的代码块或方法,从而避免多线程环境中的并发问题。
synchronized 是 JVM 提供的原生机制,能保证可见性、原子性和有序性
synchronized 的实现原理:
synchronized 的底层实现依赖于 JVM 中的 对象监视器(Monitor) 机制。
每个对象的对象头中包含的元数据,其中之一就是用来实现 synchronized 的锁标志位。
synchronized方法级的同步是隐式的,当某个线程要访问同步方法的时候,
会先检查是否有ACC_synchronized标志,如果有就需要先获得监视器锁,然后在执行方法,等到执行完后再释放监视器锁。
如果有其它线程在synchronized同步方法执行过程中,再次执行这个方法,会因为无法获得监视器锁而被阻断。
synchronized同步代码块使用monitor enter 和 monitor exit两个指令实现,
可以把monitor enter理解为加锁,monitor exit理解为释放锁
每个对象都维护着一个被锁次数的计数器,执行monitor enter计数器自增,
执行monitor exit计数器自减,当计数器为0时,锁将被释放,其它线程便可以获得锁
线程在退出 synchronized 方法或代码块 或因抛出异常而离开代码块时,会自动释放锁
synchronized 锁有几个特点:
互斥性:同一时间,只能有一个线程可以获得锁
阻塞性:只能获得锁的线程可以执行synchronized修饰的代码片段,未获取锁的会被阻塞,等待锁的释放
可重入性:如果一个线程已经获得锁,在锁未被释放之前,再次请求锁的时候,是必然可以获得锁的
——————————————————————————
synchronized 锁的实现机制中存在锁的升级过程,以提高性能和减少不必要的线程争用。锁的升级主要涉及三种锁的状态:偏向锁、轻量级锁、和重量级锁。
JVM对于同步锁的处理是从偏向锁开始的,随着竞争越来越激烈,处理方式从偏向锁升级到轻量级锁,最终升级到重量级锁。
synchronized 锁升级:
偏向锁: 只有一个线程争夺时,这个线程来了不加锁。
轻量级锁: 少量线程来了之后,先尝试自旋,不挂起线程。
注意:挂起线程和恢复线程的操作都需要转入内核态中完成这些操作,给系统的并发性带来很大的压力。
重量级锁: 排队挂起线程,升级到重量级锁后,所有等待的线程都会被阻塞在操作系统的调度队列中,等待唤醒
在低竞争的情况下,偏向锁和轻量级锁可以大幅减少锁的开销,而在高竞争的情况下,重量级锁则保证了线程的正确性。
锁的降级:
JVM 允许从重量级锁降级为轻量级锁,或者轻量级锁降级为偏向锁,但这种降级并不常见,且主要由 JVM 内部控制