1. 싱글 톤 모드 란 무엇입니까?
싱글 톤 패턴은 애플리케이션의 전 생애 동안 하나의 인스턴스 만 존재한다고 말합니다. 싱글 톤 패턴은 널리 사용되는 디자인 패턴입니다. 인스턴스 객체의 중복 생성을 피하고 인스턴스 생성 시스템 오버 헤드를 줄이며 메모리를 저장할 수있는 많은 이점이 있습니다.
싱글 톤 모드에는 세 가지 요구 사항이 있습니다.
2. 싱글 톤 패턴과 정적 클래스의 차이
먼저, 정적 클래스가 무엇인지 이해합시다. 정적 클래스는 클래스에 정적 메소드와 정적 필드가 있음을 의미합니다. 생성자는 비공개로 수정되므로 인스턴스화 할 수 없습니다. 수학 클래스는 정적 클래스입니다.
정적 클래스가 무엇인지 알면 그들 사이의 차이점에 대해 이야기 해 봅시다.
1) 우선, 싱글 톤 패턴은 전 세계적으로 고유 한 객체를 제공합니다. 정적 클래스는 많은 정적 방법 만 제공합니다. 이러한 방법을 만들 필요는 없으며 클래스를 통해 직접 호출 할 수 있습니다.
2) 싱글 톤 패턴의 유연성이 높고 정적 클래스가 모두 정적 방법이므로 무시할 수 없기 때문에 방법은 무시할 수 있습니다.
3) 매우 무거운 물체 인 경우 싱글 톤 패턴은로드하기가 게으른 것이지만 정적 클래스는 그렇게 할 수 없습니다.
그러면 정적 클래스를 사용해야하며 언제 싱글 톤 모드를 사용해야합니까? 우선, 몇 가지 도구 방법을 사용하려면 정적 클래스를 사용하는 것이 가장 좋습니다. 정적 유사체는 싱글 톤 클래스보다 빠릅니다. 정적 결합이 컴파일 기간 동안 수행되기 때문입니다. 상태 정보 또는 액세스 리소스를 유지하려면 싱글 톤 모드를 사용해야합니다. 또한 객체 지향 기능 (예 : 상속, 다형성)이 필요할 때 싱글 톤 클래스를 선택하고 일부 방법 만 제공하면 정적 클래스를 선택한다고 말할 수 있습니다.
3. 싱글 톤 모드를 구현하는 방법
1. 배고픈 사람 모드
소위 배고픈 모드는 즉시로드하는 것입니다. 일반적으로 GetInstancef 메소드를 호출하기 전에 인스턴스가 생성되었으며, 이는 클래스가로드 될 때 생성되었음을 의미합니다. 이 모델의 단점은 매우 명백하며, 이는 자원을 차지한다는 것입니다. 싱글 톤 클래스가 크면 실제로 사용하고 인스턴스를 생성하려고합니다. 따라서이 방법은 리소스가 적고 초기화 중에 사용되는 클래스에 적합합니다.
Class Singletonhungary {Private Static Singletonhungary Singletonhungary = New Singletonhungary (); // 새로운 개인 SingletonHungary ()를 통한 인스턴스화를 금지하기 위해 생성자를 비공개로 설정합니다. }}2. 게으른 모드
게으른 모드는 게으른 하중이며 게으른 하중이라고도합니다. 메모리가 낭비되지 않도록 프로그램을 사용해야 할 때 인스턴스를 만듭니다. 게으른 모드의 경우 5 가지 구현 방법이 있습니다. 일부 구현 방법은 스레드 inforecure이므로 자원 동기화 문제가 다중 스레드 동시성 환경에서 발생할 수 있습니다.
우선, 첫 번째 방법은 단일 스레드에 문제가 없지만 멀티 스레딩에 문제가 있다는 것입니다.
// 싱글 톤 모드의 게으른 구현 1- 스레드 안전하지 않은 클래스 SingletonLazy1 {private static singletonlazy1 singletonlazy; private singletonlazy1 () {} public static singletonlazy1 getInstance () {if (null == singletonLazy) {객체 스레드를 만들기 전에 준비를 시뮬레이션합니다. } catch (InterruptedException e) {e.printstacktrace (); } singletonlazy = new SingletonLazy1 (); } 반환 싱글 톤 라치를 반환합니다. }}테스트 할 10 개의 비동기 스레드를 시뮬레이션합시다.
공개 클래스 SingletonLazyTest {public static void main (string [] args) {Thread2 [] ThreadAr = new Thread2 [10]; for (int i = 0; i <threadarr.length; i ++) {threadarr [i] = new Thread2 (); Threadarr [i] .start (); }}} // test stread class stread2 스레드를 확장 {@override public void run () {system.out.println (SingletonLazy1.getInstance (). hashcode ()); }}실행 결과 :
124191239
124191239
872096466
1603289047
1698032342
1913667618
371739364
124191239
1723650563
367137303
해시 코드가 모두 동일하지 않다는 것을 알 수 있습니다. 즉, 다중 스레드 환경에서 여러 객체가 생성되어 싱글 톤 패턴의 요구 사항을 충족하지 못합니다.
그렇다면 스레드를 안전하게 만드는 방법? 두 번째 방법에서는 동기화 된 키워드를 사용하여 GetInstance 메소드를 동기화합니다.
// 싱글 톤 모드 게으른 구현 2- 스레드 안전 // 동기화 메소드를 설정하여 효율이 너무 낮아서 전체 메소드가 잠겨 있습니다. private singletonlazy2 () {} public static synchronized singletonlazy2 getInstance () {try {if (null == SingletonLazy) {// 객체 스레드를 만들기 전에 일부 준비를 시뮬레이션합니다 (1000); SingletonLazy = New SingletonLazy2 (); }} catch (InterruptedException e) {e.printstacktrace (); } 반환 싱글 톤 라치를 반환합니다. }}위의 테스트 클래스를 사용하여 테스트 결과 :
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
1210004989
알 수 있듯이이 방법은 스레드 안전성을 달성합니다. 그러나 단점은 효율이 너무 낮고 동기식으로 실행된다는 것입니다. 다음 스레드가 객체를 얻으려면 계속 실행되기 전에 이전 스레드가 출시 될 때까지 기다려야합니다.
그런 다음 메소드를 잠글 수는 없지만 코드를 내부 고정하여 스레드 안전성을 달성 할 수 있습니다. 그러나이 방법은 동기화 방법과 동일하며 동기식으로 실행되며 효율이 매우 낮습니다.
// SingletonLazy 구현 3- 스레드 안전 // 동기 코드 블록을 설정하여 효율성이 너무 낮고 전체 코드 블록이 클래스로 잠겨 있습니다. private singletonlazy3 () {} public static singletonlazy3 getInstance () {try {synchronized (singletonlazy3.class) {if (null == singletonlazy) {// 객체 스레드를 만들기 전에 약간의 준비를 수행하기 위해 시뮬레이션합니다 .sleep (1000); SingletonLazy = New SingletonLazy3 (); }}} catch (InterruptedException e) {// todo : handle exception} return singletonlazy; }}코드를 계속 최적화합시다. 객체를 생성하는 코드 만 잠그지 만 스레드 안전을 보장 할 수 있습니까?
// 싱글 톤 모드 4의 게으른 구현-스레드 불안한 // 인스턴스를 생성하는 코드 만 동기화 코드 블록을 설정하여 동기화되어 있지만 여전히 스레드 안전 문제가 있습니다. private singletonlazy4 () {} public static singletonlazy4 getInstance () {try {if (null == singletonLazy) {// 코드 1 // 객체 스레드를 만들기 전에 일부 준비를 수행하기 위해 시뮬레이션 (1000); 동기화 된 (SingletonLazy4.class) {SingletonLazy = New SingletonLazy4 (); // code 2}}} catch (InterruptedException e) {// todo : handle exception} return singletonlazy; }}실행중인 결과를 살펴 보겠습니다.
1210004989
1425839054
1723650563
389001266
1356914048
389001266
1560241484
278778395
124191239
367137303
결과를 판단하면이 방법은 스레드 안전을 보장 할 수 없습니다. 왜? 객체가 아직 비어 있기 때문에 두 개의 스레드 A와 B가 동시에 '코드 1'로 이동한다고 가정 해 봅시다. 따라서 둘 다 메소드를 입력 할 수 있습니다. 실을 먼저 잡으십시오. 잠금 장치를 잡고 개체를 만듭니다. 스레드 B가 잠금을 받으면 릴리스되면 '코드 2'로 이동하고 객체가 생성됩니다. 따라서 다중 스레드 환경에서는 싱글 톤을 보장 할 수 없습니다.
계속 최적화합시다. 위의 방법에는 문제가 있으므로 동기화 코드 블록에서 널 판단을 할 수 있습니다. 이 방법은 DCL 이중 점검 잠금 장치입니다.
// Singleton Mode의 Slazy Man은 5- 스레드 안전 // 동기화 코드 블록을 설정하여 DCL Double Check Mechanism을 사용하여 Double Check Lock 메커니즘을 사용하여 Singleton 모드에서 Lazy Man이 구현 한 스레드 불안 및 효율성 문제를 성공적으로 해결합니다. // DCL은 또한 Singeleton 모드와 함께 사용되는 대부분의 다중 읽기 모드에서 사용하는 솔루션을 성공적으로 해결합니다. SingletonLazy5 객체가 생성 될 때, SingletonLazy5 객체를 얻을 때, 동기화 코드 블록의 잠금을 확인하고 후속 코드의 잠금을 확인하고 SingletonLazy5 개체를 직접 반환 할 필요가 없습니다. // 두 번째 IF 판단의 기능 : MultithReading에서 보안 문제를 해결하려면, 즉 객체의 유능함을 보장합니다. 클래스 SingletonLazy5 {개인 정적 휘발성 SingletonLazy5 SingletonLazy; private singletonlazy5 () {} public static singletonlazy5 getInstance () {try {if (null == singletonLazy) {// 객체 스레드를 만들기 전에 일부 준비를 시뮬레이션합니다. 동기화 (SingletonLazy5.class) {if (null == SingletonLazy) {SingletonLazy = New SingletonLazy5 (); }}}} catch (InterruptedException e) {} return singletonlazy; }}실행 결과 :
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
124191239
DCL 더블 체크 잠금 장치는 게으른 하중 싱글 톤 모드의 효율 및 스레드 안전 문제를 해결한다는 것을 알 수 있습니다. 이것은 또한 우리가 가장 자주 사용하는 방법입니다.
휘발성 키워드
여기서 SingletonLazy를 정의 할 때 휘발성 키워드가 사용됨을 알았습니다. 이것은 지침이 재정렬을 방지하는 것입니다. 왜 우리는 이것을해야합니까? 시나리오를 살펴 보겠습니다.
코드는 SingletonLazy = New SingletonLazy5 ()로 이동합니다. 문장 인 것처럼 보이지만 원자 연산은 아닙니다 (모두 실행되거나 모두 실행되지 않으며 절반을 실행할 수 없습니다). 이 문장은 8 개의 어셈블리 지침으로 편집되어 있으며 대략 3 가지가 수행됩니다.
1. SingletonLazy5 인스턴스에 메모리를 할당하십시오.
2. SingletonLazy5 생성자를 초기화합니다
3. 할당 된 메모리 공간에 SingletonLazy 물체를 가리 킵니다 (이 인스턴스는 null이 아님).
Java 컴파일러를 사용하면 프로세서가 주문 외부 (외부)를 실행할 수 있고 캐시 순서는 JDK1.5 이전의 JMM (Java Memory Medel)의 메인 메모리 쓰기 백에 등록 할 수 있으므로 위의 두 번째 및 세 번째 점의 순서는 보장 될 수 없습니다. 즉, 실행 순서는 1-2-3 또는 1-3-2 일 수 있습니다. 후자이고 3이 실행되고 2가 실행되기 전에 스레드 2로 전환됩니다.이 시점에서 SingletonLazy는 이미 스레드 1에서 세 번째 지점을 이미 실행했으며, SingletonLazy는 이미 비어 있지 않으므로 스레드 2는 직접적으로 SingleTonLazy를 사용하고 자연스럽게 오류를보고합니다. 또한, 추적하기 어려우고 재생하기 어려운 이러한 종류의 오류는 디버깅의 마지막 주에 발견되지 않을 수 있습니다.
싱글 톤을 구현하기위한 DCL의 작문 방법은 많은 기술 책과 교과서 (이전 버전의 JDK1.4를 기반으로하는 책 포함)에서 권장되지만 실제로는 완전히 정확하지 않습니다. 실제로, DCL은 2 단계 및 3 단계의 순서가 보장 될 수 있는지에 따라 일부 언어 (예 : C)에서는 가능합니다. JDK1.5 이후, 공무원은이 문제를 발견 했으므로 JMM이 조정되었고 휘발성 키워드가 구체화되었습니다. 따라서 JDK가 1.5 이상의 버전 인 경우 SingletonLazy의 정의에 휘발성 키워드를 추가하면 SingletonLazy가 매번 기본 메모리에서 읽을 수 있으며 재정렬을 금지 할 수 있으며 DCL 쓰기 방법을 사용하여 Singleton 모드를 완료 할 수 있습니다. 물론 휘발성은 성능에 어느 정도 영향을 미칩니다. 가장 중요한 것은 JDK1.42 및 이전 버전을 고려해야한다는 것입니다. 따라서 싱글 톤 패턴 쓰기의 개선은 여전히 계속되고 있습니다.
3. 정적 내부 클래스
위의 고려 사항에 따라 정적 내부 클래스를 사용하여 싱글 톤 패턴을 구현할 수 있습니다. 코드는 다음과 같습니다.
// 정적 내부 클래스를 사용하여 싱글 톤 모드를 구현합니다. 스레드 클래스 SingletonStaticInner {private singletonstaticinner () {} 개인 정적 클래스 싱글 론 니너 {private static singletonstaticinner singletonstaticinner = new SingletonstaticInner (); } public static singletonstaticinner getInstance () {try {thread.sleep (1000); } catch (InterruptedException e) {// todo 자동 생성 캐치 블록 e.printstacktrace (); } return singletoninner.singletonstaticinner; }}이러한 방식으로 동기화 작업을 명시 적으로 수행하지 않으므로 스레드 안전을 어떻게 보장 하는가? 배고픈 맨 모드와 마찬가지로 JVM이 클래스의 정적 멤버를 한 번만로드 할 수 있도록 JVM 레벨의 인스턴스 객체 만 있도록하는 기능입니다. 문제는이 방법과 배고픈 사람 모델의 차이점은 무엇입니까? 즉시로드하지 않습니까? 실제로 클래스가로드되면 내부 클래스는 동시에로드되지 않습니다. 정적 부재 중 하나 (정적 도메인, 생성자, 정적 메소드 등)가 호출되는 경우에만 클래스가로드됩니다.
이 방법은 싱글 톤 패턴을 구현하기위한 최적의 솔루션이라고 말할 수 있습니다.
4. 정적 코드 블록
다음은 정적 코드 블록 구현 싱글 톤 패턴입니다. 이 방법은 첫 번째 방법과 유사하며 배고픈 사람 모델이기도합니다.
// 정적 코드 블록을 사용하여 싱글 톤 모드 클래스 싱글 톤 스트라 티카 블록 {private static singletonstaticblock SingletonstaticBlock; static {SingletonstaticBlock = New SingletonStaticBlock (); } public static singletonstaticblock getinstance () {return singletonstaticblock; }}5. 직렬화 및 사제화
LZ가 직렬화 및 사제화를 권장하는 이유는 무엇입니까? 싱글 톤 모드는 스레드 안전을 보장 할 수 있지만 직렬화 및 사태화의 경우 여러 객체가 생성됩니다. 다음 테스트 클래스를 실행하고
공개 클래스 SingletonStaticInnerserializetest {public static void main (String [] args) {try {singletonstaticinnerserialize serialize = singletonstaticinnerserialize.getinstance (); System.out.println (serialize.hashcode ()); // serialize fileoutputStream fo = 새 FileOutputStream ( "tem"); ObjectOutputStream OO = 새로운 ObjectOutputStream (FO); oo.writeobject (Serialize); oo.close (); fo.close (); // deserialize fileInputStream fi = new FileInputStream ( "tem"); ObjectInputStream oi = 새로운 ObjectInputStream (fi); Singletonstaticinnerserialize serialize2 = (SingletonstaticInnerserialize) oi.readobject (); oi.close (); fi.close (); System.out.println (serialize2.hashcode ()); } catch (예외 e) {e.printstacktrace (); }}} // 익명의 내부 클래스를 사용하여 싱글 톤 패턴을 구현합니다. 직렬화 및 사막화에 직면 할 때 동일한 인스턴스가 얻어지지 않습니다 .//Solve이 문제는 직렬화 중에 readResolve 방법, 즉 주석의 일부를 제거하는 것입니다. Class SingletonstaticInnerserialize는 시리얼이즈 가능 { / *** 2018 년 3 월 28 일* / Private STATIC Final Long SerialversionUID = 1L; 개인 정적 클래스 내부 클래스 {private static singletonstaticinnerserialize singletonstaticinnerserialize = new singletonstaticinnerserialize (); } public static singletonstaticinnerserialize getinstance () {return innerclass.singletonstaticinnerserialize; } // Protected Object ReadResolve () {// system.out.println ( "readresolve 메소드가 호출되었습니다"); // return Innerclass.singletonstaticinnerserialize; //}}당신은 볼 수 있습니다 :
865113938
1078694789
결과는 실제로 싱글 톤 패턴을 위반하는 두 가지 다른 객체 인스턴스임을 보여줍니다. 그렇다면이 문제를 해결하는 방법은 무엇입니까? 해결책은 사막화에서 readResolve () 메소드를 사용하여 위의 주석 코드를 제거한 다음 다시 실행하는 것입니다.
865113938
readresolve 방법이 호출되었습니다
865113938
문제는 누가 readresolve () 메소드의 신성한 방법은 누구입니까? 실제로, JVM이 메모리에서 새 객체를 제외하고 "조립"할 때, 우리가 지정한 객체를 반환하기 위해이 readResolve 메소드를 자동으로 호출하고 싱글 톤 규칙이 보장됩니다. ReadResolve ()의 출현으로 프로그래머는 스스로 사막화를 통해 얻은 물체를 제어 할 수 있습니다.
위는이 기사의 모든 내용입니다. 모든 사람의 학습에 도움이되기를 바랍니다. 모든 사람이 wulin.com을 더 지원하기를 바랍니다.