Добрый день, я создал Singleton:
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)
Это исключение бывает редко, примерно 1 раз на 20 исполнений. Я обнаружил, что ConcurrentModificationException может вызываться методами, которые обнаруживают одновременное изменение объекта, когда такое изменение недопустимо. В коде у меня есть блокировка, которая должна предотвращать такие ситуации, есть такая же блокировка для изменения и получения списка для синхронизированных блоков. Я даже не представляю, почему это происходит.
System.out.println(newInstance.getLetterList());
, который не защищен никаким синхронизированным блоком. - person Mark Rotteveel   schedule 22.09.2017getLetterList()
имеет синхронизированный блок, разве этого недостаточно? - person Maksim   schedule 22.09.2017