스핀 잠금: 스레드가 잠금을 획득할 때 다른 스레드가 잠금을 획득한 경우 스레드는 루프에서 기다린 다음 계속해서 잠금을 결정하는 것을 의미합니다. 잠금을 성공적으로 획득할 수 있으면 잠금을 획득할 때까지 루프가 종료되지 않습니다.
낙관적 잠금: 충돌이 없다고 가정하고 데이터 수정 시 이전에 얻은 데이터와 일치하지 않는 데이터가 발견되면 최신 데이터를 읽고 다시 수정을 시도합니다.
비관적 잠금: 동시성 충돌이 발생할 것이라고 가정하고 데이터와 관련된 모든 작업을 동기화하고 데이터 읽기부터 잠금을 시작합니다.
배타적 잠금(쓰기): 리소스에 쓰기 잠금을 추가하면 스레드가 리소스를 수정할 수 있으며 다른 스레드는 리소스를 다시 잠글 수 없습니다(단일 쓰기).
공유 잠금(읽기): 리소스에 읽기 잠금을 추가한 후에는 읽을 수만 있고 수정할 수는 없습니다. 다른 스레드는 읽기 잠금만 추가할 수 있고 쓰기 잠금(다중)을 추가할 수 없습니다. 그냥 세마포어(semaphore)라고 생각하시면 됩니다.
재진입 잠금 및 비재진입 잠금: 스레드가 잠금을 획득한 후 동일한 잠금으로 동기화된 다른 코드를 자유롭게 입력할 수 있습니다.
공정한 잠금 & 불공정한 잠금: 잠금을 놓고 경쟁하는 순서는 선착순이면 공정합니다. 즉, 자물쇠를 잡는 순서와 자물쇠를 잡는 순서가 동일하다는 것이 보장된다면 공정한 자물쇠이다.
특징: 재진입, 독점, 비관적 잠금.
잠금 관련 최적화:
잠금 제거: 잠금 제거를 활성화하는 매개변수는 -XX:+DoEscapeAnalytic
, -XX:+EliminateLocks
입니다. -XX:+DoEscapeAnalysis
、 -XX:+EliminateLocks
。
锁粗化:JDK做了锁粗化的优化,但我们自己可从代码层面优化。
/** * 锁消除示例,JIT即时编译,进行了锁消除 * @author 刘亚楼 * @date 2020/1/16 */ public class LockEliminationExample { /** * StringBuilder线程不安全,StringBuffer用了synchronized关键字,是线程安全的 * 针对下面这种单线程加锁、解锁操作,JIT会进行优化,进行锁消除 */ public static void eliminateLock() { StringBuffer stringBuffer = new StringBuffer(); stringBuffer.append("a"); stringBuffer.append("b"); stringBuffer.append("c"); stringBuffer.append("a"); stringBuffer.append("b"); stringBuffer.append("c"); stringBuffer.append("a"); stringBuffer.append("b"); stringBuffer.append("c"); } }
/** * 锁粗化示例 * @author 刘亚楼 * @date 2020/1/16 */ public class LockCoarseningExample { /** * 针对下面这种无意义的加锁操作,JIT会进行优化,对变量i的所有操作放到一个同步代码块里 */ public static void lockCoarsening() { int i = 0; synchronized (LockCoarseningExample.class) { i++; } synchronized (LockCoarseningExample.class) { i--; } synchronized (LockCoarseningExample.class) { i++; } synchronized (LockCoarseningExample.class) { i++; i--; i++; } } }
备注:锁消除和锁粗化的区别在于锁消除是针对单个线程重复加解锁做的优化,最终没有锁的存在。而锁粗化不只是针对单线程,且最终还是有锁的存在。
首先,对象在堆中由对象头、实例数据和对齐填充组成。
对象头包含两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向锁id等,这部分数据官方称为"Mark Word"。
对象头的另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。
synchronized实现的锁是通过改变对象头的"Mark Word"来实现的。
"Mard Word"在32位和64位的虚拟机(未开启压缩指针)中分别为32位和64位。32位虚拟机"Mark Word"如下:
无锁变成轻量级锁时,多个线程会读取对象的对象头的无锁状态mark word内容,然后进行cas
操作进行修改,预期值是无锁状态mark word内容,新值是轻量级锁状态mark word内容,若修改成功,Lock record address
指向成功获取锁的线程的Lock Record
。
演示流程如下:
由于未成功获取锁的线程会自旋,长时间自旋会消耗CPU资源,因此自旋到一定次数会进行锁升级,由轻量级锁转变为重量级锁。
重量级锁是通过object monitor(对象监视器)实现的,对象监视器包括entryList(锁池)、owner(持锁者)、waitSet(等待集合)等。
升级为重量级锁时对象头mark word的内容是monitor address(对象监视器地址),指向对象监视器。
演示流程如下:
备注:抢锁失败线程会进入entryList(锁池),在调用wait方法后,线程会进入waitSet(等待集合),waitSet中的线程被唤醒后会重新进入entryList。
加锁之后不解锁,针对单线程
所谓偏向就是偏心,单线程加锁之后就不再解锁,减少了加锁→业务处理→释放锁→加锁操作流程。
在JDK6以后,默认已经开启了偏向锁这个优化,通过JVM参数-XX:-UseBiasedLocking
cas
작업을 수행하여 이를 수정합니다. 예상 값은 잠금 해제 상태 표시 단어 내용이고 새 값은 경량 잠금 상태 표시 단어 내용이 성공적으로 수정된 경우 잠금 레코드 주소
는 잠금을 성공적으로 획득한 스레드의 잠금 레코드
를 가리킵니다. 🎜🎜시연 과정은 다음과 같습니다. 🎜🎜🎜-XX:-UseBiasedLocking
을 통해 비활성화됩니다. 바이어스 잠금이 켜져 있으면 하나의 스레드만 잡을 수 있습니다. 잠금을 해제하고 바이어스를 얻습니다. 🎜🎜바이어스 잠금 표시 단어에 대한 내용은 다음과 같습니다. 🎜🎜🎜🎜바이어스 표시는 처음에는 유용하지만 경합이 발생한 후에는 쓸모가 없습니다.
편향 잠금의 본질은 잠금이 없다는 것입니다. 잠금에 대한 다중 스레드 경쟁이 없으면 JVM은 이를 단일 스레드로 간주하고 동기화가 필요하지 않습니다.
참고: JVM의 작업을 줄이기 위해 JVM 하단에서 여러 작업을 통해 동기화를 구현합니다. 경합이 없으면 동기화 작업을 수행할 필요가 없습니다.
바이어스 잠금이 켜지지 않은 경우 잠금 없는 상태에서 먼저 경량 잠금으로 업그레이드되고, 경량 잠금에서 중량 잠금으로 어느 정도 업그레이드됩니다.
바이어스 잠금이 켜져 있으면 두 가지 상황이 있습니다.
잠금이 점유되어 있지 않으면 잠금 없음으로 업그레이드되고 잠금 없음이 경량 잠금으로 업그레이드된 다음 경량 잠금 장치가 중량 잠금 장치로 업그레이드됩니다.
자물쇠가 점유되면 경량자물쇠로 업그레이드 되고, 이후 경량자물쇠에서 중량자물쇠로 업그레이드됩니다.
위 내용은 Java 키워드 동기화 원리 및 잠금 상태 예시 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!