다중 스레드 아래의 단일 모드를 당신은 정말 이해합니까?

11055 단어 단일 모드
첫 번째(게으름뱅이, 라인이 안전하지 않음):
 
    public class Singleton {  

        private static Singleton instance;  

        private Singleton (){}  

      

        public static Singleton getInstance() {  

        if (instance == null) {  

            instance = new Singleton();  

        }  

        return instance;  

        }  

    }  

 
이런 작법은 lazy loading이 뚜렷하지만 치명적인 것은 다선정이 정상적으로 작동하지 못한다는 것이다.
두 번째(게으름뱅이, 라인 안전):
 
    public class Singleton {  

        private static Singleton instance;  

        private Singleton (){}  

        public static synchronized Singleton getInstance() {  

        if (instance == null) {  

            instance = new Singleton();  

        }  

        return instance;  

        }  

    }  

 
이런 맞춤법은 다중 라인에서 좋은 작업을 할 수 있을 뿐만 아니라, 보기에도 좋은 레이지 로드링을 갖추고 있는 것 같지만, 유감스럽게도 효율이 매우 낮고 99퍼센트의 경우 동기화할 필요가 없다.
세 번째(아사자):
    public class Singleton {  

        private static Singleton instance = new Singleton();  

        private Singleton (){}  

        public static Singleton getInstance() {  

        return instance;  

        }  

    }  

이런 방식은classloder 메커니즘을 바탕으로 여러 라인의 동기화 문제를 피하지만 instance는 클래스를 불러올 때 실례화된다. 클래스를 불러오는 원인은 여러 가지가 있지만 단일 모드에서 getInstance 방법을 호출하지만 다른 방식(또는 다른 정적 방법)이 클래스를 불러오는 것을 확정할 수 없다.이때 instance를 초기화하는 것은 분명히 lazy loading 효과에 이르지 못했다.
네 번째(아사자, 변종):
 
    public class Singleton {  

        private Singleton instance = null;  

        static {  

        instance = new Singleton();  

        }  

        private Singleton (){}  

        public static Singleton getInstance() {  

        return this.instance;  

        }  

    }  

표면적으로 보면 차이가 매우 커 보이지만 사실 세 번째 방식은 차이가 많지 않다. 모두 클래스 초기화 즉 실례화 instance이다.
다섯 번째(정적 내부 클래스):
 
    public class Singleton {  

        private static class SingletonHolder {  

        private static final Singleton INSTANCE = new Singleton();  

        }  

        private Singleton (){}  

        public static final Singleton getInstance() {  

        return SingletonHolder.INSTANCE;  

        }  

    }  

이런 방식 역시classloder의 메커니즘을 이용하여 instance를 초기화할 때 하나의 라인만 있음을 보장한다. 이것은 세 번째와 네 번째 방식과 다른 것은 (미세한 차이)이다. 세 번째와 네 번째 방식은singleton류가 불러오면 instance가 실례화된다(lazy loading 효과에 이르지 못했다). 이런 방식은singleton류가 불러온다.instance가 반드시 초기화되는 것은 아닙니다.SingletonHolder 클래스가 주동적으로 사용되지 않았기 때문에 getInstance 방법을 호출할 때만 SingletonHolder 클래스를 불러오는 것을 표시하여 instance를 실례화합니다.만약에 실례화된 instance가 자원을 많이 소모한다면 나는 그를 불러오는 것을 늦추고 싶다. 다른 한편, 나는singleton류가 불러올 때 실례화하는 것을 원하지 않는다. 왜냐하면 나는singleton류가 다른 곳에서 주동적으로 사용되어 불러올 수 있다는 것을 확보하지 못하기 때문이다. 그러면 이때 실례화된 instance는 분명히 적합하지 않다.이럴 때 이런 방식은 세 번째와 네 번째 방식에 비해 매우 합리적으로 보인다.
여섯 번째(열거):
 
    public enum Singleton {  

        INSTANCE;  

        public void whateverMethod() {  

        }  

    }  

이런 방식은 Effective Java 저자 Josh Bloch가 제창한 방식이다. 다중 스레드 동기화 문제를 피할 수 있을 뿐만 아니라 반서열화로 새로운 대상을 다시 만드는 것을 방지할 수 있기 때문에 매우 강한 장벽이라고 할 수 있다. 그러나 개인적으로는 1.5에 enum 특성이 들어갔기 때문에 이런 방식으로 쓰는 것은 낯설음을 느끼지 않을 수 없다. 실제 업무에서나도 이렇게 쓴 적이 매우 드물다.
일곱 번째(이중 잠금):
 
    public class Singleton {  

        private volatile static Singleton singleton;  

        private Singleton (){}  

        public static Singleton getSingleton() {  

        if (singleton == null) {  

            synchronized (Singleton.class) {  

            if (singleton == null) {  

                singleton = new Singleton();  

            }  

            }  

        }  

        return singleton;  

        }  

    }  

총결산
주의해야 할 두 가지 질문이 있습니다.
1. 하나의 예를 다른 클래스 마운트에 불러오면 여러 개의 클래스의 실례가 존재할 수 있습니다.원격 접근이 아니라고 가정하십시오. 예를 들어 일부 servlet 용기는 모든 servlet에 완전히 다른 클래스 마운트를 사용합니다. 그러면 두 개의 servlet이 하나의 클래스에 접근하면 각각의 실례가 있을 것입니다.
2.Singleton이java를 실현했다면.io.Serializable 인터페이스, 이 종류의 실례는 서열화되고 복원될 수 있습니다.어쨌든, 만약 당신이 하나의 단례류의 대상을 서열화하고, 다음에 여러 개의 그 대상을 복원한다면, 당신은 여러 개의 단례류의 실례를 가지게 될 것이다.
첫 번째 문제를 해결하는 방법은 다음과 같습니다.
 
    private static Class getClass(String classname)      

                                             throws ClassNotFoundException {     

          ClassLoader classLoader = Thread.currentThread().getContextClassLoader();     

          

          if(classLoader == null)     

             classLoader = Singleton.class.getClassLoader();     

          

          return (classLoader.loadClass(classname));     

       }     

    }  

두 번째 문제를 해결하는 방법은 다음과 같습니다.
 
    public class Singleton implements java.io.Serializable {     

       public static Singleton INSTANCE = new Singleton();     

          

       protected Singleton() {     

            

       }     

       private Object readResolve() {     

                return INSTANCE;     

          }    

    }   

저에게 있어서 저는 세 번째 방식과 다섯 번째 방식을 비교적 좋아합니다. 간단하고 알기 쉽습니다. 그리고 JVM층에서 라인 안전(여러 종류의 캐리어 환경이 아니라면)을 실현했습니다. 일반적인 상황에서 저는 세 번째 방식을 사용합니다. lazy loading 효과를 명확하게 실현하려면 다섯 번째 방식을 사용합니다. 또한,만약에 반서열화 창설 대상과 관련이 있을 때 나는 매거진 방식을 사용하여 하나의 예를 실현하려고 한다. 그러나 나는 내 프로그램이 안전하다는 것을 보장할 것이다. 그리고 나는 첫 번째 방식과 두 번째 방식을 영원히 사용하지 않을 것이다. 만약에 다른 특수한 수요가 있다면 나는 일곱 번째 방식을 사용할 것이다. 결국, JDK1.5 잠금 검사를 이중으로 하는 문제가 없습니다.
========================================================================
그러나 일반적으로 첫 번째는 단례를 계산하지 않고 네 번째와 세 번째는 일종이다. 계산하면 다섯 번째도 따로 쓸 수 있다.그래서 일반적인 단례는 모두 다섯 가지 문법이다.게으름뱅이, 악한, 이중 검사 자물쇠, 매거와 정적 내부류.
나는 이런 독자가 있어서 함께 격려하는 것을 매우 기쁘게 생각한다.

좋은 웹페이지 즐겨찾기