0

좋은 하루, 내가 만든 싱글 문제로 생성 :싱글은 열거 형, 스레드 안전

import java.util.Arrays; 
import java.util.Collections; 
import java.util.LinkedList; 

public enum Singleton { 
    FIRST_INSTANCE; 

    String[] scrabbleLetters = { 
      "a","a","a","a","a","a","a","a","a","b","b","b","b","b","b","b","b","b", 
      "c","c","c","c","c","c","c","c","c","d","d","d","d","d","d","d","d","d","d", 
    }; 

    private LinkedList<String> letterList = new LinkedList<>(Arrays.asList(scrabbleLetters)); 

    private Object lock = new Object(); 

    private Singleton() { 
     Collections.shuffle(letterList); 
    } 

    public static Singleton getInstance() { 
     return FIRST_INSTANCE; 
    } 

    public LinkedList<String> getLetterList() { 
     synchronized (lock) { 

     return FIRST_INSTANCE.letterList; 
     } 
    } 

    public LinkedList<String> getTiles(int howManyTiles) { 
     synchronized (lock) { 

     LinkedList<String> tilesToSend = new LinkedList<>(); 
     for(int i=0; i<= howManyTiles; i++) { 
      tilesToSend.add(FIRST_INSTANCE.letterList.remove(0)); 
     } 
     return tilesToSend; 

     } 
    } 

} 

나는이 예제와 스레드 안전에 테스트 한 : 그것을 실행 한 후

import java.util.LinkedList; 

public class ScrabbleTest { 
    public static void main(String[] args) { 
     Runnable getTiles =() -> { 

      System.out.println("In thread : " +Thread.currentThread().getName()); 
      Singleton newInstance = Singleton.getInstance(); 
      System.out.println("Instance ID: " + System.identityHashCode(newInstance)); 
      System.out.println(newInstance.getLetterList()); 

      LinkedList<String> playerOneTiles = newInstance.getTiles(7); 
      System.out.println("Player : " + Thread.currentThread().getName() + playerOneTiles); 
      System.out.println("Got Tiles for " + Thread.currentThread().getName()); 
     }; 

     new Thread(getTiles, "First").start(); 
     new Thread(getTiles, "Second").start(); 
    } 
} 

10 번, 나는 아무런 문제가 없다고 확신했지만 지난번에이 스택 추적을 받으면 나는이 스택 추적을 받았다 :

In thread : Second 
In thread : First 
Instance ID: 1380197535 
Instance ID: 1380197535 
[d, d, b, c, b, b, a, d, c, d, a, d, c, a, a, d, c, a, a, b, d, b, b, a, b, c, a, d, c, a, c, b, c, c, b, d, d] 
Player : First[d, d, b, c, b, b, a, d] 
Got Tiles for First 
Exception in thread "Second" java.util.ConcurrentModificationException 
    at java.util.LinkedList$ListItr.checkForComodification(Unknown Source) 
    at java.util.LinkedList$ListItr.next(Unknown Source) 
    at java.util.AbstractCollection.toString(Unknown Source) 
    at java.lang.String.valueOf(Unknown Source) 
    at java.io.PrintStream.println(Unknown Source) 
    at ScrabbleTest.lambda$0(ScrabbleTest.java:10) 
    at java.lang.Thread.run(Unknown Source) 

이 예외는 거의 종료되지 않으며 20 회 실행시 약 1 회 발생합니다. 개체의 동시 수정을 감지 한 메서드가 그러한 수정을 허용하지 않는 경우 ConcurrentModificationException이 throw 될 수 있음을 발견했습니다. 이 상황을 방지해야하는 잠금이있는 코드에는 동기화 된 블록의 목록을 변경하고 검색하는 것과 동일한 잠금 장치가 있습니다. 나는 이것이 왜 일어나는 지 상상조차하지 않는다.

+2

코드가 스레드로부터 안전하지 않습니다. 기간. 이 문제는 동기화 된 블록에 의해 보호되지 않는'System.out.println (newInstance.getLetterList()); '에서 발생합니다. –

+0

메소드'getLetterList()'에 동기화 된 블록이 있습니다. 충분하지 않습니까? – Maksim

+1

아니요, ** 문제가 ** ** 목록이 반환 된 후 (동기화 된 블록이 종료 된 후) 발생합니다. 목록을 복사해야하거나 목록을 동기화 된 블록의 문자열로 변환해야합니다. 대신. 그러나 당신의 접근 방식은 당신이 대신 고쳐야 할 많은 다른 문제들로 가득 차 있습니다. 왜 여기에 싱글 톤을 사용하는지 자문 해보십시오. 같은 실행, 여러 게임 등 여러 게임을하고 싶다면 어떻게해야할까요?이 솔루션은 올바른 것이 아니며 대신 해결해야 할 문제입니다. –

답변

1

CME은 이름이 생각하는 것처럼 동시성과 관련이 없습니다. CME의 가장 일반적인 경우는 단일 스레드 컨텍스트입니다. 그러나이 경우에는 스레딩이 관련됩니다.

letterList을 수정중인 tilesToSend.add(FIRST_INSTANCE.letterList.remove(0));에서 문제가 발생하지만 동시에 println에 의해 반복됩니다. 현실적으로 가능한 것보다 훨씬 더 큰 블록을 동기화해야하기 때문에 동기화는 여기서 도움이되지 않습니다. 여기

쉬운 솔루션은

return new LinkedList<>(FIRST_INSTANCE.letterList); 

처럼 getLetterList()println이 사본을 반복하는 동안 원래 목록이 remove()에 의해 수정 될 수있는이 방법을 목록의 복사본을 반환하는 것입니다.