단일 모드 스 레 드 안전 실험

12238 단어
비록 단일 모델 은 디자인 모델 에서 가장 간단 한 모델 에 속 하지만 깊이 탐색 하면 사실은 일부 학문 이 있다.예 를 들 면:
  • 어떻게 스 레 드 안전 사례 를 설계 합 니까?
  • 다 중 스 레 드 환경 에서 시간 이 비교적 낮은 사례 를 어떻게 설계 합 니까?그 다음 에 저 는 다음 과 같은 절차 에 따라 단일 모델 의 스 레 드 안전 문 제 를 실험 으로 검증 하고 다 중 스 레 드 환경 에서 대상 인 스 턴 스 를 어떻게 만 드 는 지 시간 이 더 적 습 니 다.

  • 비 스 레 드 안전 사례 - 게으름뱅이 식
    public class SingletonLazy {
    
        private static SingletonLazy singleton = null;
    
        private static int counter = 0;
    
        private SingletonLazy() {
            counter++;
            System.out.println(String.format("  [%s]     ,     [%d] ", Thread.currentThread().getName(), counter));
        }
    
        public static SingletonLazy getInstance() {
            if (singleton == null) {
                singleton = new SingletonLazy();
            }
            return singleton;
        }
    
    }
    

    그렇다면, 우 리 는 그것 이 라인 이 안전 한 지 어떻게 압 니까?그 다음 에 우 리 는 Runnable 을 사용 하여 10 개의 스 레 드 를 만 들 고 구조 기 가 몇 번 호출 되 었 는 지 관찰 합 니 다.여러 번 호출 되면 자 연 스 럽 게 여러 개의 대상 을 만 들 었 습 니 다. 바로 스 레 드 가 안전 하지 않 습 니 다.
    public class SingleTonApplication {
    
        /**
         *                     。
         * @param args
         */
        public static void main(String[] args) {
            //        
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    SingletonLazy singletonLazy = SingletonLazy.getInstance();
                    System.out.println(String.format("[%s]        :%s", Thread.currentThread().getName(), singletonLazy.toString()));
                }
            };
            for (int i = 0; i < 10; i++) {
                Thread thread = new Thread(runnable);
                thread.start();
            }// end for
    
        }// end main
    }
    

    출력 결 과 는 다음 과 같 습 니 다.
    스 레 드 [Thread - 7] 호출 구조 기, 대상 은 [8] 차 스 레 드 [Thread - 3] 호출 구조 기, 대상 은 [3] 차 스 레 드 [Thread - 1] 호출 구조 기, 대상 은 [4] 차 스 레 드 [Thread - 6] 호출 구조 기, 대상 은 [7] 차 스 레 드 [Thread - 2] 호출 구조 기, 대상 은 [2] 차 스 레 드 [Thread - 4] 호출 구조 기, 대상 은 [5] 차 스 레 드 를 만 들 었 습 니 다.[Thread - 0] 호출 구조 기, 대상 생 성 [2] 차 스 레 드 [Thread - 5] 호출 구조 기, 대상 생 성 [6] 차 스 레 드 [Thread - 9] 호출 구조 기, 대상 생 성 [10] 회 [Thread - 0] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@2116aeb스 레 드 [Thread - 8] 호출 구조 기, 대상 생 성 [9] 회 [Thread - 4]스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@2eac0b4[Thread - 2] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@1c2bd9d7[Thread - 6] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@4bcf6203[Thread - 1] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@475f7458 [Thread-3]스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@46d8dc2e[Thread - 7] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@44fd2254[Thread - 8] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@4268d15[Thread - 5] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@45826b5c [Thread-9]스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@755688aa
    이 를 통 해 알 수 있 듯 이 여러 스 레 드 가 CPU 자원 을 경쟁 할 때 하나의 인 스 턴 스 를 만 들 었 습 니 다. 이 실험 을 통 해 이 사례 는 스 레 드 가 안전 하지 않 습 니 다. 그래서 우 리 는 getInstance 방법 에 synchronized 키 워드 를 추가 합 니 다.
    public static synchronized SingletonLazy getInstance() {...}
    

    출력 은 다음 과 같 습 니 다:
    스 레 드 [Thread - 8] 호출 구조 기, 대상 생 성 [1] 회 [Thread - 2] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33[Thread - 4] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33[Thread - 5] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33 [Thread-8]스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33[Thread - 3] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33[Thread - 9] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33[Thread - 0] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33 [Thread-1]스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33[Thread - 6] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33[Thread - 7] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazy.SingletonLazy@48c85a33 Process finished with exit code 0
    CPU 자원 을 빼 앗 아 유일한 인 스 턴 스 를 만 든 스 레 드 [Thread-8] 를 볼 수 있 습 니 다. 이 때 스 레 드 안전 문제 가 해결 되 었 습 니 다. 그러나 새로운 문 제 를 일 으 켰 습 니 다. 여러 스 레 드 가 거의 동시에 접근 getInstance 하 는 방법 이 있 을 때 여러 스 레 드 가 순서대로 방 법 에 들 어가 야 합 니 다. 이 로 인해 여러 스 레 드 가 임계 구역 (잠 겨 있 는 코드 블록) 에 들 어 갈 때 까지 기 다 려 야 합 니 다.시간. 우 리 는 getInstance 방법 에 sleep() 을 추가 하여 임계 구역 에 들 어 가 는 스 레 드 를 잠시 기다 리 게 합 니 다. 아 날로 그 대상 이 대상 을 얻 을 때 걸 리 는 시간, 개 조 된 코드 는 다음 과 같 습 니 다.
    스 레 드 안전 사례 - 게으름뱅이 식
    public class Singleton {
    
        private static Singleton singleton = null;
    
        private static int counter = 0;
    
        private Singleton() {
            counter++;
            System.out.println(String.format("       [%d] ", counter));
        }
    
        /**
         *             getInstance   ,               ,
         *                      (       )   。
         * @return
         */
        public static synchronized Singleton getInstance() {
            //            start
            try {
                System.out.println(String.format("[%s]        1 ", Thread.currentThread().getName()));
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            //            end
            if (singleton == null) {
                singleton = new Singleton();
            }
            return singleton;
        }
    
    }
    

    현재, 우 리 는 CountDownLatch 클래스 를 통 해 main 메 인 스 레 드 가 모든 하위 스 레 드 실행 이 끝 날 때 까지 기다 린 다음 에 10 번 의 대상 인 스 턴 스 를 얻 는 데 걸 리 는 시간 을 통계 해 야 합 니 다.
    public class SingleTonApplication {
    
        public static void main(String[] args) throws InterruptedException {
    
            int createTimes = 10;
    
            final CountDownLatch latch = new CountDownLatch(createTimes);
    
            //        
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    Singleton singleton = Singleton.getInstance();
                    System.out.println(String.format("[%s]        :%s", Thread.currentThread().getName(), singleton.toString()));
                    latch.countDown();
                }
            };
    
            long start = System.currentTimeMillis();
    
            for (int i = 0; i < createTimes; i++) {
                Thread thread = new Thread(runnable);
                thread.start();
            }// end for
    
            latch.await(); //          
            System.out.println(String.format("    [%d]   :[%dms]", createTimes, (System.currentTimeMillis() - start)));
    
        }// end main
    }
    

    출력 은 다음 과 같 습 니 다:
    [Thread - 5] 대상 인 스 턴 스 를 가 져 와 1 초 동안 구조 대상 이 호출 될 때 까지 기 다 립 니 다. [Thread - 9] 대상 인 스 턴 스 를 가 져 와 1 초 동안 기 다 립 니 다. [Thread - 5] 스 레 드 는 현재 대상 을 인쇄 합 니 다: com. hua. singleton. lazythreadsafe.Singleton@b07848[Thread - 8] 대상 인 스 턴 스 를 가 져 와 1 초 동안 기 다 립 니 다. [Thread - 9] 스 레 드 는 현재 대상 을 인쇄 합 니 다: com. hua. singleton. lazythreadsafe.Singleton@b07848 [Thread-7]대상 인 스 턴 스 를 가 져 와 1 초 기다 리 기 [Thread - 8] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazythreadsafe.Singleton@b07848[Thread - 6] 대상 인 스 턴 스 를 가 져 와 1 초 를 기 다 립 니 다. [Thread - 7] 스 레 드 는 현재 대상 을 인쇄 합 니 다: com. hua. singleton. lazythreadsafe.Singleton@b07848[Thread - 1] 대상 인 스 턴 스 가 져 오기 1 초 대기 [Thread - 6]스 레 드 인쇄 현재 대상: com. hua. singleton. lazythreadsafe.Singleton@b07848[Thread - 2] 대상 인 스 턴 스 를 가 져 와 1 초 를 기 다 립 니 다. [Thread - 1] 스 레 드 는 현재 대상 을 인쇄 합 니 다: com. hua. singleton. lazythreadsafe.Singleton@b07848[Thread - 4] 대상 인 스 턴 스 를 가 져 와 1 초 동안 기 다 립 니 다. [Thread - 2] 스 레 드 는 현재 대상 을 인쇄 합 니 다: com. hua. singleton. lazythreadsafe.Singleton@b07848 [Thread-3]대상 인 스 턴 스 를 가 져 와 1 초 를 기 다 립 니 다. [Thread - 4] 스 레 드 는 현재 대상 을 인쇄 합 니 다: com. hua. singleton. lazythreadsafe.Singleton@b07848[Thread - 0] 대상 인 스 턴 스 를 가 져 와 1 초 동안 기 다 립 니 다. [Thread - 3] 스 레 드 는 현재 대상 을 인쇄 합 니 다: com. hua. singleton. lazythreadsafe.Singleton@b07848[Thread - 0] 스 레 드 인쇄 현재 대상: com. hua. singleton. lazythreadsafe.Singleton@b07848대상 가 져 오기 [10]회 소모 시간: [10052 ms]
    출력 로그 에 따 르 면 모든 스 레 드 는 인 스 턴 스 를 가 져 올 때 일정한 시간 을 기 다 려 야 합 니 다. 다 중 스 레 드 의 장점 은 발휘 되 지 않 았 습 니 다. 사실 우 리 는 대상 을 만 들 때 동기 화 를 해 야 합 니 다. 따라서 이중 검증 잠 금 의 단일 생 성 모드 를 도입 합 니 다.
    스 레 드 안전, 시간 소모 가 적은 단일 예 - 이중 검사 잠 금
    public class SingletonDCL {
    
        private volatile static SingletonDCL singleton;
    
        private static int counter = 0;
    
        private SingletonDCL() {
            counter++;
            System.out.println(String.format("       [%d] ", counter));
        }
    
        /**
         *       (                  )              ,
         *                      ,
         *            "lazythreadsafe" ,         ,              
         * @return
         */
        public static SingletonDCL getInstance() {
            //            start
            try {
                System.out.println(String.format("[%s]        1 ", Thread.currentThread().getName()));
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                e.printStackTrace();
            }
            //            end
            if (singleton == null) {
                synchronized (Singleton.class) {
                    if (singleton == null) {
                        singleton = new SingletonDCL();
                    }// end if
                }// end syn
            }// end if
            return singleton;
        }
    
    }
    

    실험 코드:
    public class SingleTonApplication {
    
        public static void main(String[] args) throws InterruptedException {
            int createTimes = 10;
            final CountDownLatch latch = new CountDownLatch(createTimes);
    
            //        
            Runnable runnable = new Runnable() {
                @Override
                public void run() {
                    SingletonDCL singleton = SingletonDCL.getInstance();
                    System.out.println(String.format("[%s]        :%s", Thread.currentThread().getName(), singleton.toString()));
                    latch.countDown(); //  count  1
                }
            };
    
            long start = System.currentTimeMillis();
    
            for (int i = 0; i < createTimes; i++) {
                Thread thread = new Thread(runnable);
                thread.start();
            }// end for
    
            //           
            latch.await();
            System.out.println(String.format("    [%d]   :[%dms]", createTimes, (System.currentTimeMillis() - start)));
    
        }// end main
    }
    
    

    콘 솔 출력:
    [Thread - 9] 대상 인 스 턴 스 1 초 대기 [Thread - 6] 대상 인 스 턴 스 1 초 대기 [Thread - 1] 대상 인 스 턴 스 1 초 대기 [Thread - 7] 대상 인 스 턴 스 1 초 대기 [Thread - 2] 대상 인 스 턴 스 1 초 대기 [Thread - 3] 대상 인 스 턴 스 1 초 대기 [Thread - 5] 대상 인 스 턴 스 1 초 대기 [Thread - 8] 대상 인 스 턴 스 1 초 대기 [Thread - 0]대상 인 스 턴 스 가 져 오기 1 초 대기 [Thread - 4] 대상 인 스 턴 스 가 져 오기 1 초 대기 구조 대상 이 호출 [1] 회 [Thread - 1] 스 레 드 로 현재 대상 인쇄: com. hua. singleton. dcl.SingletonDCL@7115e166[Thread - 2] 스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166[Thread - 3] 스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166 [Thread-6]스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166[Thread - 9] 스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166[Thread - 8] 스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166[Thread - 5] 스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166 [Thread-7]스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166[Thread - 0] 스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166[Thread - 4] 스 레 드 인쇄 현재 대상: com. hua. singleton. dcl.SingletonDCL@7115e166획득 대상 [10] 회 소모 시간: [1031 ms]
    기다 리 는 동안 여러 스 레 드 가 동시에 getInstance 방법 으로 들 어 가 는 것 을 볼 수 있 습 니 다. 그 다음 에 먼저 한 번 검사 singleton 합 니 다.생 성 되 었 는 지, 생 성 되 었 는 지, 바로 되 돌 립 니 다. 생 성 되 지 않 았 다 면 동기 코드 블록 에 들 어 갑 니 다. 동기 코드 블록 에서 singleton 대상 이 있 는 지 다시 한 번 판단 하고 있 으 면 처리 하지 않 습 니 다. 없 을 때 만 들 었 습 니 다. 이 럴 때 동기 코드 블록 에서 도 판단 해 야 하 는 이 유 를 묻 는 친구 가 있 을 수 있 습 니 다 if (singleton == null).여러 스 레 드 가 동시에 getInstance 방법 으로 가 는 것 을 상상 할 수 있 습 니 다. 이 때 동기 코드 블록 에 singleton 이 아직 만 들 어 지지 않 았 습 니 다. 판단 하지 않 으 면 프로그램 은 여기 서 인 스 턴 스 를 만들어 야 한다 고 생각 합 니 다. 그러면 여러 개의 인 스 턴 스 가 생 성 될 것 입 니 다. 우리 의 단일 모드 는 자 연 스 럽 게 작 동 하지 않 습 니 다.
    총결산
    이중 검사 잠 금 식 의 단일 모드 스 레 드 가 안전 하고 나중에 스 레 드 가 먼저 대상 을 만 든 후에 도 임계 구역 에서 물 러 나 지 않 은 상태 에서 기다 리 는 것 을 피 할 수 있 습 니 다.

    좋은 웹페이지 즐겨찾기