이 글은 동시성(코드 포함)의 싱글톤 모드에 대해 자세히 소개합니다. 필요한 친구들이 참고할 수 있기를 바랍니다.
单线程下的Singleton的稳定性是极好的,可分为两大类:
1. Eager(eager 유형): 클래스가 로드되는 즉시 객체를 생성합니다.
public class EagerSingleton { //1. 类加载时就立即产生实例对象,通过设置静态变量被外界获取 //2. 并使用private保证封装安全性 private static EagerSingleton eagerSingleton = new EagerSingleton(); //3. 通过构造方法的私有化,不允许外部直接创建对象,确保单例的安全性 private EagerSingleton(){ } public static EagerSingleton getEagerSingleton(){ return eagerSingleton; }
2. Lazy(lazy 유형): 클래스가 로드될 때 개체가 즉시 생성되지 않으며 첫 번째 사용자가 개체를 얻을 때까지 인스턴스화되지 않습니다.
public class LazySingleton { //1. 类加载时并没有创建唯一实例 private static LazySingleton lazySingleton; private LazySingleton() { } //2、提供一个获取实例的静态方法 public static LazySingleton getLazySingleton() { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } return lazySingleton; }
성능 측면에서 보면 LazySingleton이 EagerSingleton보다 확실히 더 좋습니다. 클래스를 로드하는 데 많은 리소스가 필요한 경우(예: 대용량 파일 정보 읽기) LazySingleton의 장점은 분명합니다. 하지만 코드를 읽어보면 치명적인 문제를 쉽게 발견할 수 있습니다. 여러 스레드 간에 보안을 유지하는 방법은 무엇입니까?
멀티 스레드 동시성 문제는 아래에서 분석됩니다.
이 문제를 해결하는 열쇠는 두 가지 측면에 있습니다. 1. 동기화 2. 성능
구출 방법?
Java 개발자라면 synchronized에 익숙할 텐데요. 멀티스레딩하면 대부분의 사람들이 그를 떠올립니다(JDK6 이후에는 성능이 엄청나게 향상되었으며 단순 동시성 문제를 해결하는 데 매우 적합합니다).
그럼 동기화를 사용하여 해결해 보겠습니다.
//由synchronized进行同步加锁 public synchronized static LazySingleton getLazySingleton() { if (lazySingleton == null) { lazySingleton = new LazySingleton(); } return lazySingleton; }
동기화 문제는 해결된 것 같지만 개발자로서 가장 중요한 것은 성능 보장입니다. 잠금 작업에 대한 코드 세그먼트는 비관적으로 잠겨 있으며 하나의 요청이 완료된 후에만 다음 요청을 실행할 수 있습니다. 일반적으로 동기화된 키워드가 포함된 코드 조각은 동일한 규모의 코드보다 몇 배 느릴 수 있는데, 이는 우리가 보고 싶지 않은 것입니다. 그렇다면 이 문제를 피하는 방법은 무엇입니까? Java의 동기화 정의에는 다음 제안이 있습니다. 나중에 동기화를 사용할수록 성능이 향상됩니다(세련된 잠금).
###### 2. 따라서 성능 문제 해결을 시작해야 합니다. 동기화에 따라 최적화: ######
public class DoubleCheckLockSingleton { //使用volatile保证每次取值不是从缓存中取,而是从真正对应的内存地址中取.(下文解释) private static volatile DoubleCheckLockSingleton doubleCheckLockSingleton; private DoubleCheckLockSingleton(){ } public static DoubleCheckLockSingleton getDoubleCheckLockSingleton(){ //配置双重检查锁(下文解释) if(doubleCheckLockSingleton == null){ synchronized (DoubleCheckLockSingleton.class) { if(doubleCheckLockSingleton == null){ doubleCheckLockSingleton = new DoubleCheckLockSingleton(); } } } return doubleCheckLockSingleton; } }
위의 소스 코드는 고전적인 휘발성 키워드(JDK1.5 이후 재탄생) + 이중 검사 잠금(DoubleCheck)으로, 동기화로 인해 발생하는 성능 오버헤드를 최적화합니다. 최대 규모. Volatile과 DoubleCheck에 대해서는 아래에서 설명하겠습니다.
1.휘발성
은 JDK1.5 이후에 공식적으로 구현되어 사용되었습니다. 이전 버전에서는 특별한 구현 없이 이 키워드만 정의했습니다. 휘발성을 이해하려면 JVM 자체 메모리 관리에 대해 어느 정도 이해해야 합니다.
1.1 무어의 법칙에 따르면 메모리의 읽기 및 쓰기 속도는 CPU를 만족시키지 못하기 때문에 현대 컴퓨터에서는 이 방법을 도입했습니다. CPU에 캐시를 추가하는 메커니즘은 캐시에서 메모리 값을 미리 읽고 이를 캐시에 임시 저장한 후 메모리의 해당 값을 업데이트합니다.
**1.2** JVM은 PC의 접근 방식을 모방하여 자체 **작업 메모리**를 메모리에 분할합니다. 이 부분은 캐시와 동일하게 기능하므로 JVM 작업 효율성이 크게 향상됩니다. 이 접근 방식은 작업 메모리가 다른 메모리와 통신할 때 전송 문제도 발생합니다. 휘발성의 한 가지 기능은 캐시와 메모리 간의 불일치를 피하기 위해 메모리에서 최신 값을 강제로 읽는 것입니다.
1.3 휘발성의 또 다른 기능은 JVM과도 관련이 있습니다. 즉, JVM은 자체 판단을 사용하여 소스 코드의 실행 순서를 재정렬하여 명령 파이프라인의 연속성을 보장하여 최적의 결과를 얻습니다. 실행 계획. 이 접근 방식은 성능을 향상시키지만 DoubleCheck에 예상치 못한 결과가 발생하고 두 스레드가 서로 간섭할 수 있습니다. Volatile은 발생 전 보장(쓰기가 읽기보다 우선)을 제공하므로 개체가 방해받지 않고 안전과 안정성을 보장합니다.
2.DoubleCheck
이것은 현대 프로그래밍의 유산입니다. 동기화된 블록에 들어간 후 객체가 인스턴스화되었으며 다시 판단이 필요하다고 가정합니다.
물론 공식적으로 권장되는 싱글톤 구현 방법도 있습니다.
클래스 구성이 정의에서 이미 원자적이므로 위에서 언급한 문제는 다시 발생하지 않으며 좋은 싱글톤 방법입니다. 구현, 권장됩니다.
아아아아위 내용은 동시성 싱글톤 모드에 대한 자세한 소개(코드 포함)의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!