찾다
Javajava지도 시간Java 및 CLH 대기열 원칙의 스레드 안전 구현에 대한 간략한 소개(코드 예)

이 기사는 Java 및 CLH 대기열 원칙의 스레드 안전 구현에 대한 간략한 소개(코드 예제)를 제공합니다. 이는 특정 참조 가치가 있으므로 도움이 될 수 있습니다.

동기화 차단

Java에서는 여러 스레드의 공유 데이터에 대한 동시 액세스 문제를 해결하기 위해 상호 배타적인 동기화를 달성하기 위해 동기화 키워드를 자주 사용합니다. synchronzied 키워드가 컴파일된 후 두 개의 바이트코드 명령어인 monitorenter 및 monitorexit가 synchized에 포함된 동기화 코드 블록 앞뒤에 추가됩니다. 동기화 키워드를 사용하려면 잠금 및 잠금 해제를 위한 개체를 지정해야 합니다. 예를 들면 다음과 같습니다.

public class Main {

    private static final Object LOCK = new Object();
    
    public static void fun1() {
        synchronized (LOCK) {
            // do something
        }
    }
    
    public static void fun2() {
        synchronized (LOCK) {
            // do something
        }
    }
}

객체가 명시적으로 지정되지 않은 경우 동기화된 수정이 인스턴스 메서드인지 정적 메서드인지 여부에 따라 객체 인스턴스를 사용할지 클래스의 클래스 인스턴스를 객체로 사용할지 결정됩니다. 예:

public class SynchronizedTest {
    public synchronized void doSomething() {
        //采用实例对象作为锁对象
    }
}
public class SynchronizedTest {
    public static synchronized void doSomething() {
        //采用SynchronizedTest.class 实例作为锁对象
    }
}

동기화 기반으로 구현된 차단 뮤텍스로 인해 작동 스레드를 차단하는 것 외에도 운영 체제 수준에서 기본 스레드를 깨우거나 차단해야 하며 사용자 모드에서 커널로 전환해야 합니다. 이 상태 전환에 소요되는 시간은 사용자 코드의 실행 시간보다 길 수 있으므로 Java 언어에서는 동기화를 "무거운 잠금"이라고 자주 말합니다.

비차단 동기화

낙관적 잠금과 비관적 잠금

동기화 키워드를 사용한 동기화의 주요 문제점은 스레드 차단 및 깨우기로 인한 성능 소모입니다. 동기화 차단은 경쟁 가능성이 있는 한 잠금을 수행해야 한다고 믿는 비관적인 동시성 전략입니다.
그러나 동기화 전략에는 또 다른 낙관적 전략이 있습니다. 낙관적 동시성 전략은 데이터 작업을 진행하는 데 다른 스레드도 해당 데이터에서 작업한 것이 발견되지 않으면 작업이 성공한 것으로 간주됩니다. 다른 스레드도 데이터를 조작하는 경우 일반적으로 성공할 때까지 계속해서 재시도합니다. 이 낙관적 잠금 전략은 차단 스레드가 필요하지 않으며 비차단 동기화 수단입니다.

CAS

낙관적 동시성 전략은 주로 두 가지 중요한 단계로 구성됩니다. 하나는 데이터를 작동하는 것이고, 다른 하나는 충돌을 감지하는 것, 즉 다른 스레드도 데이터에 대해 작동하는지 여부를 감지하는 것입니다. 여기서 데이터 작업 및 충돌 감지에는 원자성이 필요합니다. 그렇지 않으면 i++와 유사한 문제가 쉽게 발생합니다.
CAS는 비교 및 ​​교환을 의미합니다. 현재 대부분의 CPU는 기본적으로 CAS 원자성 명령을 지원합니다. 예를 들어 IA64 및 x86의 명령 세트에는 CAS 기능을 완료하기 위한 cmpxchg와 같은 명령이 있습니다. .
CAS 명령에는 일반적으로 값의 메모리 주소, 예상되는 이전 값 및 새 값이라는 세 가지 매개변수가 필요합니다. CAS 명령어가 실행될 때 메모리 주소의 값이 예상되는 이전 값과 일치하면 프로세서는 메모리 주소의 값을 새 값으로 업데이트하고, 그렇지 않으면 업데이트되지 않습니다. 이 작업은 CPU 내에서 원자성이 보장됩니다.
Java에는 CAS 관련 API가 많이 있습니다. 가장 일반적인 API에는 AtomicInteger, 와 같은 <code>java.util.concurrent 패키지 아래에 다양한 원자 클래스가 포함됩니다. >AtomicReference등. java.util.concurrent 包下的各种原子类,例如AtomicIntegerAtomicReference等等。
这些类都支持 CAS 操作,其内部实际上也依赖于 sun.misc.Unsafe 这个类里的 compareAndSwapInt() 和 compareAndSwapLong() 方法。
CAS 并非是完美无缺的,尽管它能保证原子性,但它存在一个著名的 ABA 问题。一个变量初次读取的时候值为 A,再一次读取的时候也为 A,那么我们是否能说明这个变量在两次读取中间没有发生过变化?不能。在这期间,变量可能由 A 变为 B,再由 B 变为 A,第二次读取的时候看到的是 A,但实际上这个变量发生了变化。一般的代码逻辑不会在意这个 ABA 问题,因为根据代码逻辑它不会影响并发的安全性,但如果在意的话,可能考虑采用阻塞同步的方式而不是 CAS。实际上 JDK 本身也对这个 ABA 问题解决方案,提供了 AtomicStampedReference이러한 클래스는 모두 CAS 작업을 지원하며 내부적으로는 실제로 sun.misc.Unsafe 클래스의 CompareAndSwapInt() 및 CompareAndSwapLong() 메서드에 의존합니다.

CAS는 원자성을 보장하지만 그 유명한

ABA 문제를 안고 있습니다. 변수의 값은 처음 읽을 때 A이고, 다시 읽을 때에도 A입니다. 이 변수는 두 번의 읽기 사이에 변경되지 않았다고 설명할 수 있습니까? 할 수 없습니다. 이 기간 동안 변수는 A에서 B로, 그리고 B에서 A로 바뀔 수 있습니다. 두 번째 읽을 때는 A로 보이지만 실제로는 변수가 변경되었습니다. 일반 코드 로직에서는 이 ABA 문제를 신경 쓰지 않습니다. 코드 로직에 따르면 동시성 보안에 영향을 주지 않기 때문입니다. 하지만 관심이 있는 경우 CAS 대신 차단 동기화를 사용하는 것을 고려할 수 있습니다. 실제로 JDK 자체도 ABA 문제를 해결하기 위해 변수에 버전을 추가하는 AtomicStampedReference 클래스를 제공하여 이 ABA 문제에 대한 솔루션을 제공합니다.

스핀 잠금
동기화로 표시되는 차단 동기화. 차단된 스레드가 스레드 작업을 재개하기 때문입니다. 이를 위해서는 운영 체제 수준에서 사용자 모드와 커널 모드 간 전환이 필요하며 이는 시스템 성능에 큰 영향을 미칩니다. 큰. 스핀 잠금의 전략은 스레드가 잠금을 획득할 때 다른 스레드가 이미 잠금을 차지하고 있음을 발견하면 즉시 CPU의 실행 시간 조각을 포기하지 않고 "의미 없는" 루프에 들어가는 것입니다. 스레드 잠금이 포기되었는지 확인하십시오.

그러나 스핀 잠금은

중요 섹션

이 상대적으로 작은 상황에 적합합니다. 잠금을 너무 오랫동안 유지하면 스핀 작업 자체가 시스템 성능을 낭비하게 됩니다. 🎜🎜다음은 간단한 스핀 잠금 구현입니다. 🎜
import java.util.concurrent.atomic.AtomicReference;
public class SpinLock {
   private AtomicReference<thread> owner = new AtomicReference<thread>();
   public void lock() {
       Thread currentThread = Thread.currentThread();
        // 如果锁未被占用,则设置当前线程为锁的拥有者
       while (!owner.compareAndSet(null, currentThread)) {}
   }

   public void unlock() {
       Thread currentThread = Thread.currentThread();
        // 只有锁的拥有者才能释放锁
       owner.compareAndSet(currentThread, null);
   }
}</thread></thread>

上述的代码中, owner 变量保存获得了锁的线程。这里的自旋锁有一些缺点,第一个是没有保证公平性,等待获取锁的线程之间,无法按先后顺序分别获得锁;另一个,由于多个线程会去操作同一个变量 owner,在 CPU 的系统中,存在着各个 CPU 之间的缓存数据需要同步,保证一致性,这会带来性能问题。

公平的自旋

为了解决公平性问题,可以让每个锁拥有一个服务号,表示正在服务的线程,而每个线程尝试获取锁之前需要先获取一个排队号,然后不断轮询当前锁的服务号是否是自己的服务号,如果是,则表示获得了锁,否则就继续轮询。下面是一个简单的实现:

import java.util.concurrent.atomic.AtomicInteger;

public class TicketLock {
   private AtomicInteger serviceNum = new AtomicInteger(); // 服务号
   private AtomicInteger ticketNum = new AtomicInteger(); // 排队号

   public int lock() {
       // 首先原子性地获得一个排队号
       int myTicketNum = ticketNum.getAndIncrement();
       // 只要当前服务号不是自己的就不断轮询
       while (serviceNum.get() != myTicketNum) {
       }
       return myTicketNum;
    }

    public void unlock(int myTicket) {
        // 只有当前线程拥有者才能释放锁
        int next = myTicket + 1;
        serviceNum.compareAndSet(myTicket, next);
    }
}

虽然解决了公平性的问题,但依然存在前面说的多 CPU 缓存的同步问题,因为每个线程占用的 CPU 都在同时读写同一个变量 serviceNum,这会导致繁重的系统总线流量和内存操作次数,从而降低了系统整体的性能。

MCS 自旋锁

MCS 的名称来自其发明人的名字:John Mellor-Crummey和Michael Scott。
MCS 的实现是基于链表的,每个申请锁的线程都是链表上的一个节点,这些线程会一直轮询自己的本地变量,来知道它自己是否获得了锁。已经获得了锁的线程在释放锁的时候,负责通知其它线程,这样 CPU 之间缓存的同步操作就减少了很多,仅在线程通知另外一个线程的时候发生,降低了系统总线和内存的开销。实现如下所示:

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class MCSLock {
    public static class MCSNode {
        volatile MCSNode next;
        volatile boolean isWaiting = true; // 默认是在等待锁
    }
    volatile MCSNode queue;// 指向最后一个申请锁的MCSNode
    private static final AtomicReferenceFieldUpdater<mcslock> UPDATER = AtomicReferenceFieldUpdater
            .newUpdater(MCSLock.class, MCSNode.class, "queue");

    public void lock(MCSNode currentThread) {
        MCSNode predecessor = UPDATER.getAndSet(this, currentThread);// step 1
        if (predecessor != null) {
            predecessor.next = currentThread;// step 2
            while (currentThread.isWaiting) {// step 3
            }
        } else { // 只有一个线程在使用锁,没有前驱来通知它,所以得自己标记自己已获得锁
            currentThread.isWaiting = false;
        }
    }

    public void unlock(MCSNode currentThread) {
        if (currentThread.isWaiting) {// 锁拥有者进行释放锁才有意义
            return;
        }

        if (currentThread.next == null) {// 检查是否有人排在自己后面
            if (UPDATER.compareAndSet(this, currentThread, null)) {// step 4
                // compareAndSet返回true表示确实没有人排在自己后面
                return;
            } else {
                // 突然有人排在自己后面了,可能还不知道是谁,下面是等待后续者
                // 这里之所以要忙等是因为:step 1执行完后,step 2可能还没执行完
                while (currentThread.next == null) { // step 5
                }
            }
        }
        currentThread.next.isWaiting = false;
        currentThread.next = null;// for GC
    }
}</mcslock>

MCS 的能够保证较高的效率,降低不必要的性能消耗,并且它是公平的自旋锁。

CLH 自旋锁

CLH 锁与 MCS 锁的原理大致相同,都是各个线程轮询各自关注的变量,来避免多个线程对同一个变量的轮询,从而从 CPU 缓存一致性的角度上减少了系统的消耗。
CLH 锁的名字也与他们的发明人的名字相关:Craig,Landin and Hagersten。
CLH 锁与 MCS 锁最大的不同是,MCS 轮询的是当前队列节点的变量,而 CLH 轮询的是当前节点的前驱节点的变量,来判断前一个线程是否释放了锁。
实现如下所示:

import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
public class CLHLock {
    public static class CLHNode {
        private volatile boolean isWaiting = true; // 默认是在等待锁
    }
    private volatile CLHNode tail ;
    private static final AtomicReferenceFieldUpdater<clhlock> UPDATER = AtomicReferenceFieldUpdater
            . newUpdater(CLHLock.class, CLHNode .class , "tail" );
    public void lock(CLHNode currentThread) {
        CLHNode preNode = UPDATER.getAndSet( this, currentThread);
        if(preNode != null) {//已有线程占用了锁,进入自旋
            while(preNode.isWaiting ) {
            }
        }
    }

    public void unlock(CLHNode currentThread) {
        // 如果队列里只有当前线程,则释放对当前线程的引用(for GC)。
        if (!UPDATER .compareAndSet(this, currentThread, null)) {
            // 还有后续线程
            currentThread.isWaiting = false ;// 改变状态,让后续线程结束自旋
        }
    }
}</clhlock>

从上面可以看到,MCS 和 CLH 相比,CLH 的代码比 MCS 要少得多;CLH是在前驱节点的属性上自旋,而MCS是在本地属性变量上自旋;CLH的队列是隐式的,通过轮询关注上一个节点的某个变量,隐式地形成了链式的关系,但CLHNode并不实际持有下一个节点,MCS的队列是物理存在的,而 CLH 的队列是逻辑上存在的;此外,CLH 锁释放时只需要改变自己的属性,MCS 锁释放则需要改变后继节点的属性。

CLH 队列是 J.U.C 中 AQS 框架实现的核心原理。

위 내용은 Java 및 CLH 대기열 원칙의 스레드 안전 구현에 대한 간략한 소개(코드 예)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
고급 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 또는 Gradle을 어떻게 사용합니까?고급 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 또는 Gradle을 어떻게 사용합니까?Mar 17, 2025 pm 05:46 PM

이 기사에서는 Java 프로젝트 관리, 구축 자동화 및 종속성 해상도에 Maven 및 Gradle을 사용하여 접근 방식과 최적화 전략을 비교합니다.

적절한 버전 및 종속성 관리로 Custom Java 라이브러리 (JAR Files)를 작성하고 사용하려면 어떻게해야합니까?적절한 버전 및 종속성 관리로 Custom Java 라이브러리 (JAR Files)를 작성하고 사용하려면 어떻게해야합니까?Mar 17, 2025 pm 05:45 PM

이 기사에서는 Maven 및 Gradle과 같은 도구를 사용하여 적절한 버전 및 종속성 관리로 사용자 정의 Java 라이브러리 (JAR Files)를 작성하고 사용하는 것에 대해 설명합니다.

카페인 또는 구아바 캐시와 같은 라이브러리를 사용하여 자바 애플리케이션에서 다단계 캐싱을 구현하려면 어떻게해야합니까?카페인 또는 구아바 캐시와 같은 라이브러리를 사용하여 자바 애플리케이션에서 다단계 캐싱을 구현하려면 어떻게해야합니까?Mar 17, 2025 pm 05:44 PM

이 기사는 카페인 및 구아바 캐시를 사용하여 자바에서 다단계 캐싱을 구현하여 응용 프로그램 성능을 향상시키는 것에 대해 설명합니다. 구성 및 퇴거 정책 관리 Best Pra와 함께 설정, 통합 및 성능 이점을 다룹니다.

캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA (Java Persistence API)를 어떻게 사용하려면 어떻게해야합니까?캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA (Java Persistence API)를 어떻게 사용하려면 어떻게해야합니까?Mar 17, 2025 pm 05:43 PM

이 기사는 캐싱 및 게으른 하중과 같은 고급 기능을 사용하여 객체 관계 매핑에 JPA를 사용하는 것에 대해 설명합니다. 잠재적 인 함정을 강조하면서 성능을 최적화하기위한 설정, 엔티티 매핑 및 모범 사례를 다룹니다. [159 문자]

Java의 클래스로드 메커니즘은 다른 클래스 로더 및 대표 모델을 포함하여 어떻게 작동합니까?Java의 클래스로드 메커니즘은 다른 클래스 로더 및 대표 모델을 포함하여 어떻게 작동합니까?Mar 17, 2025 pm 05:35 PM

Java의 클래스 로딩에는 부트 스트랩, 확장 및 응용 프로그램 클래스 로더가있는 계층 적 시스템을 사용하여 클래스로드, 링크 및 초기화 클래스가 포함됩니다. 학부모 위임 모델은 핵심 클래스가 먼저로드되어 사용자 정의 클래스 LOA에 영향을 미치도록합니다.

See all articles

핫 AI 도구

Undresser.AI Undress

Undresser.AI Undress

사실적인 누드 사진을 만들기 위한 AI 기반 앱

AI Clothes Remover

AI Clothes Remover

사진에서 옷을 제거하는 온라인 AI 도구입니다.

Undress AI Tool

Undress AI Tool

무료로 이미지를 벗다

Clothoff.io

Clothoff.io

AI 옷 제거제

AI Hentai Generator

AI Hentai Generator

AI Hentai를 무료로 생성하십시오.

인기 기사

R.E.P.O. 에너지 결정과 그들이하는 일 (노란색 크리스탈)
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 최고의 그래픽 설정
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
R.E.P.O. 아무도들을 수없는 경우 오디오를 수정하는 방법
3 몇 주 전By尊渡假赌尊渡假赌尊渡假赌
WWE 2K25 : Myrise에서 모든 것을 잠금 해제하는 방법
4 몇 주 전By尊渡假赌尊渡假赌尊渡假赌

뜨거운 도구

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse용 SAP NetWeaver 서버 어댑터

Eclipse를 SAP NetWeaver 애플리케이션 서버와 통합합니다.

Atom Editor Mac 버전 다운로드

Atom Editor Mac 버전 다운로드

가장 인기 있는 오픈 소스 편집기

ZendStudio 13.5.1 맥

ZendStudio 13.5.1 맥

강력한 PHP 통합 개발 환경

VSCode Windows 64비트 다운로드

VSCode Windows 64비트 다운로드

Microsoft에서 출시한 강력한 무료 IDE 편집기

스튜디오 13.0.1 보내기

스튜디오 13.0.1 보내기

강력한 PHP 통합 개발 환경