>Java >java지도 시간 >Java 키워드 동기화 원리 및 잠금 상태 예시 분석

Java 키워드 동기화 원리 및 잠금 상태 예시 분석

王林
王林앞으로
2023-05-11 15:25:06763검색

1. Java의 잠금 개념

  • 스핀 잠금: 스레드가 잠금을 획득할 때 다른 스레드가 잠금을 획득한 경우 스레드는 루프에서 기다린 다음 계속해서 잠금을 결정하는 것을 의미합니다. 잠금을 성공적으로 획득할 수 있으면 잠금을 획득할 때까지 루프가 종료되지 않습니다.

  • 낙관적 잠금: 충돌이 없다고 가정하고 데이터 수정 시 이전에 얻은 데이터와 일치하지 않는 데이터가 발견되면 최신 데이터를 읽고 다시 수정을 시도합니다.

  • 비관적 잠금: 동시성 충돌이 발생할 것이라고 가정하고 데이터와 관련된 모든 작업을 동기화하고 데이터 읽기부터 잠금을 시작합니다.

  • 배타적 잠금(쓰기): 리소스에 쓰기 잠금을 추가하면 스레드가 리소스를 수정할 수 있으며 다른 스레드는 리소스를 다시 잠글 수 없습니다(단일 쓰기).

  • 공유 잠금(읽기): 리소스에 읽기 잠금을 추가한 후에는 읽을 수만 있고 수정할 수는 없습니다. 다른 스레드는 읽기 잠금만 추가할 수 있고 쓰기 잠금(다중)을 추가할 수 없습니다. 그냥 세마포어(semaphore)라고 생각하시면 됩니다.

  • 재진입 잠금 및 비재진입 잠금: 스레드가 잠금을 획득한 후 동일한 잠금으로 동기화된 다른 코드를 자유롭게 입력할 수 있습니다.

  • 공정한 잠금 & 불공정한 잠금: 잠금을 놓고 경쟁하는 순서는 선착순이면 공정합니다. 즉, 자물쇠를 잡는 순서와 자물쇠를 잡는 순서가 동일하다는 것이 보장된다면 공정한 자물쇠이다.

2. 동기화 키워드 동기화 기능

특징: 재진입, 독점, 비관적 잠금.

잠금 관련 최적화:

  • 잠금 제거: 잠금 제거를 활성화하는 매개변수는 -XX:+DoEscapeAnalytic, -XX:+EliminateLocks입니다. -XX:+DoEscapeAnalysis-XX:+EliminateLocks

  • 锁粗化:JDK做了锁粗化的优化,但我们自己可从代码层面优化。

1、锁消除示例

/**
 * 锁消除示例,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");
	}
}

2、锁粗化示例

/**
 * 锁粗化示例
 * @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++;
		}
	}
}

备注:锁消除和锁粗化的区别在于锁消除是针对单个线程重复加解锁做的优化,最终没有锁的存在。而锁粗化不只是针对单线程,且最终还是有锁的存在。

三、synchronized关键字原理

1、关于Mark Word

首先,对象在堆中由对象头、实例数据和对齐填充组成。

对象头包含两部分信息,第一部分用于存储对象自身的运行时数据,如哈希码、GC分代年龄、锁状态标志、线程持有的锁、偏向锁id等,这部分数据官方称为"Mark Word"。

对象头的另一部分是类型指针,即对象指向它的类元数据的指针,虚拟机通过这个指针来确定这个对象是哪个类的实例。

synchronized实现的锁是通过改变对象头的"Mark Word"来实现的。

"Mard Word"在32位和64位的虚拟机(未开启压缩指针)中分别为32位和64位。32位虚拟机"Mark Word"如下:

Java 키워드 동기화 원리 및 잠금 상태 예시 분석

2、锁的状态变化

(1) 无锁 → 轻量级锁

无锁变成轻量级锁时,多个线程会读取对象的对象头的无锁状态mark word内容,然后进行cas操作进行修改,预期值是无锁状态mark word内容,新值是轻量级锁状态mark word内容,若修改成功,Lock record address指向成功获取锁的线程的Lock Record

演示流程如下:

Java 키워드 동기화 원리 및 잠금 상태 예시 분석

(2) 轻量级锁 → 重量级锁

由于未成功获取锁的线程会自旋,长时间自旋会消耗CPU资源,因此自旋到一定次数会进行锁升级,由轻量级锁转变为重量级锁。

重量级锁是通过object monitor(对象监视器)实现的,对象监视器包括entryList(锁池)、owner(持锁者)、waitSet(等待集合)等。

升级为重量级锁时对象头mark word的内容是monitor address(对象监视器地址),指向对象监视器。

演示流程如下:

Java 키워드 동기화 원리 및 잠금 상태 예시 분석

备注:抢锁失败线程会进入entryList(锁池),在调用wait方法后,线程会进入waitSet(等待集合),waitSet中的线程被唤醒后会重新进入entryList。

(3) 关于偏向锁

加锁之后不解锁,针对单线程

所谓偏向就是偏心,单线程加锁之后就不再解锁,减少了加锁→业务处理→释放锁→加锁操作流程。

在JDK6以后,默认已经开启了偏向锁这个优化,通过JVM参数-XX:-UseBiasedLocking

잠금 조정: JDK는 잠금 조정을 최적화했지만 코드 수준에서 직접 최적화할 수 있습니다.

Java 키워드 동기화 원리 및 잠금 상태 예시 분석

1. 잠금 제거의 예

rrreee

2. 잠금 해제의 예

rrreee🎜참고: 잠금 제거와 잠금 해제의 차이점은 잠금 제거가 일정 기간 동안 반복된다는 것입니다. 단일 스레드. 잠금 해제는 결국 잠금이 발생하지 않도록 최적화됩니다. 잠금 조잡화는 단일 스레드에만 적용되는 것이 아니라 궁극적으로 잠금에도 적용됩니다. 🎜🎜3. 동기화 키워드의 원리🎜

1. 마크 워드에 대하여

🎜먼저 힙의 객체는 객체 헤더, 인스턴스 데이터, 정렬 패딩으로 구성됩니다. 🎜🎜객체 헤더에는 두 부분의 정보가 포함되어 있습니다. 첫 번째 부분은 해시 코드, GC 생성 기간, 잠금 상태 플래그, 스레드가 보유한 잠금, 편향된 잠금 ID 등과 같은 객체 자체의 런타임 데이터를 저장하는 데 사용됩니다. . 이 부분은 공식적인 데이터입니다. "마크 워드(Mark Word)"라고 합니다. 🎜🎜객체 헤더의 다른 부분은 객체의 클래스 메타데이터에 대한 포인터인 유형 포인터입니다. 가상 머신은 이 포인터를 사용하여 객체가 어떤 클래스의 인스턴스인지 결정합니다. 🎜🎜동기화로 구현된 잠금은 개체 헤더의 "마크 워드"를 변경하여 구현됩니다. 🎜🎜"Mard Word"는 32비트 및 64비트 가상 머신에서 각각 32비트 및 64비트입니다(압축 포인터는 켜지지 않음). 32비트 가상 머신 "Mark Word"는 다음과 같습니다. 🎜🎜Java 키워드 동기화 원리 및 잠금 상태 분석 예시🎜

2. 잠금 상태 변경

(1) 잠금 없음→ 경량 잠금
🎜 잠금 없음이 경량 잠금이 되면 다중 스레드 개체 헤더의 잠금 해제 상태 표시 단어 내용을 읽은 다음 cas 작업을 수행하여 이를 수정합니다. 예상 값은 잠금 해제 상태 표시 단어 내용이고 새 값은 경량 잠금 상태 표시 단어 내용이 성공적으로 수정된 경우 잠금 레코드 주소는 잠금을 성공적으로 획득한 스레드의 잠금 레코드를 가리킵니다. 🎜🎜시연 과정은 다음과 같습니다. 🎜🎜Java 키워드 동기화 원리 및 잠금 상태 예 분석🎜
(2) 경량 잠금→ 중량 잠금
🎜 잠금 획득에 실패한 스레드가 회전하게 되므로 장기 회전은 CPU 리소스를 소비하게 되므로 회전이 일정 수준에 도달하게 됩니다. 제한 잠금 장치는 경량 잠금 장치에서 중량 잠금 장치로 여러 번 업그레이드됩니다. 🎜🎜헤비웨이트 잠금은 EntryList(잠금 풀), 소유자(잠금 보유자), waitSet(대기 세트) 등을 포함하는 개체 모니터를 통해 구현됩니다. 🎜🎜중량 잠금 장치로 업그레이드할 때 개체 헤더 표시 단어의 내용은 개체 모니터를 가리키는 모니터 주소(개체 모니터 주소)입니다. 🎜🎜시연 과정은 다음과 같습니다. 🎜🎜Java 키워드 동기화 원리 및 잠금 상태 example Analysis🎜🎜참고: 잠금 확보에 실패한 스레드는 EntryList(잠금 풀)에 들어가게 됩니다. wait 메소드를 호출한 후 스레드는 waitSet(대기 세트)에 들어가게 됩니다. waitSet의 스레드는 다시 시작됩니다. -각성 후 입장 목록에 입장하세요. 🎜
(3) 편향된 잠금 정보
🎜단일 스레드의 경우 잠금 후 잠금 해제 없음🎜🎜 소위 바이어스는 단일 스레드 잠금 후에는 더 이상 잠금 해제되지 않으므로 잠금(비즈니스 처리)이 줄어듭니다. ;잠금 해제→잠금 작업 과정입니다. 🎜🎜JDK6 이후에는 바이어스 잠금 최적화가 기본적으로 활성화되어 있습니다. 바이어스 잠금은 JVM 매개변수 -XX:-UseBiasedLocking을 통해 비활성화됩니다. 바이어스 잠금이 켜져 있으면 하나의 스레드만 잡을 수 있습니다. 잠금을 해제하고 바이어스를 얻습니다. 🎜🎜바이어스 잠금 표시 단어에 대한 내용은 다음과 같습니다. 🎜🎜🎜🎜

바이어스 표시는 처음에는 유용하지만 경합이 발생한 후에는 쓸모가 없습니다.

편향 잠금의 본질은 잠금이 없다는 것입니다. 잠금에 대한 다중 스레드 경쟁이 없으면 JVM은 이를 단일 스레드로 간주하고 동기화가 필요하지 않습니다.

참고: JVM의 작업을 줄이기 위해 JVM 하단에서 여러 작업을 통해 동기화를 구현합니다. 경합이 없으면 동기화 작업을 수행할 필요가 없습니다.

(4) 전체 잠금 업그레이드 프로세스

바이어스 잠금이 켜지지 않은 경우 잠금 없는 상태에서 먼저 경량 잠금으로 업그레이드되고, 경량 잠금에서 중량 잠금으로 어느 정도 업그레이드됩니다.

바이어스 잠금이 켜져 있으면 두 가지 상황이 있습니다.

  • 잠금이 점유되어 있지 않으면 잠금 없음으로 업그레이드되고 잠금 없음이 경량 잠금으로 업그레이드된 다음 경량 잠금 장치가 중량 잠금 장치로 업그레이드됩니다.

  • 자물쇠가 점유되면 경량자물쇠로 업그레이드 되고, 이후 경량자물쇠에서 중량자물쇠로 업그레이드됩니다.

Java 키워드 동기화 원리 및 잠금 상태 예시 분석

위 내용은 Java 키워드 동기화 원리 및 잠금 상태 예시 분석의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제