Я хотел бы понять, когда добавить модификатор synchronized
к методу, который изменяет общий объект, а когда нет.
Я написал модифицированную версию прыгающие шарики игра.Каждый шар (я назвал его «камень») - это thread
.Чтобы управлять процессом repaint
, я сохраняю HashSet
камней, и этот набор обрабатывается несколькими методами:
, когда я добавляю новый камень (если пользователь нажимает кнопку огня наGUI);
, когда я убиваю один или несколько камней (если пользователь нажимает на камни на panel
);
, когдаодин из камней-убийц (особый «плохой» вид камней) касается любого из нормальных «хороших» камней и убивает их;
и, наконец, когда метод paintComponent()
звонил.
Ну, я думал, что все методы, которые обрабатывают эти вещи, должны быть объявлены synchronized
.Но я предпринял некоторые попытки и обнаружил, что:
в некоторых случаях необходим модификатор synchronized
(если я его удаляю, я получаю исключение, ОК, это то, что я ожидал);
В некоторых других случаях я удалил модификатор synchronized
и Я никогда не получал никаких исключений , даже запуская программу все больше и больше, создавая и убивая тонны убийцкамни и / или хорошие камни.
Я много гуглил, но я читал, что модификатор synchronized
всегда нужен , когда метод обращается к общему объекту ион не нужен, только если объект неизменен .Но тогда я не могу понять, почему я могу удалить модификатор synchronized
из некоторых из этих методов без получения исключений .
Теперь я присоединяю класс StoneSet
, где всеэти методы определены.Этот класс является singleton : только один его экземпляр создается и используется почти всеми другими объектами в приложении.Я очистил класс от всего ненужного кода и написал много комментариев, чтобы помочь читателям понять класс и (надеюсь) рассказать мне, что происходит.Я прошу прощения за длинное вложение (более 100 строк).
package rollingstones;
import java.awt.Graphics;
import java.util.HashSet;
import java.util.Iterator;
class StoneSet {
private final HashSet<Stone> set = new HashSet<>(64); // this is the set
private AreaGrafica areaGrafica; // this is the JPanel
void setAreaGrafica(AreaGrafica areaGrafica) { // invoked at the beginning
this.areaGrafica = areaGrafica;
}
/**
* This method is called by the paintComponent() of the panel.
* HERE THE SYNCHRONIZED MODIFIER IS NEEDED: IF I REMOVE IT, I GET java.util.ConcurrentModificationException
*/
synchronized void redrawAll(Graphics g) {
final Iterator<Stone> iter = set.iterator();
Stone stone;
while (iter.hasNext()) {
stone = iter.next();
g.setColor(stone.getColor());
g.fillOval(stone.getX(), stone.getY(), stone.getSize(), stone.getSize());
}
}
/**
* This method is called when the user clicks the GUI's Fire button (actionPerformed awt event).
*/
void addGoodStone() {
Stone stone = new GoodStone(); // GoodStone is a Stone
addStone(stone);
}
/**
* This method is called when the user clicks the GUI's Killer button (actionPerformed awt event).
*/
void addKillerStone() {
Stone stone = new KillerStone(); // KillerStone is a Stone
addStone(stone);
}
/**
* This method adds a stone into the set, so it modifies the set, but...
* ...HERE I REMOVED THE SYNCHRONIZED MODIFIER AND I NEVER GOT ANY EXCEPTION.
*/
private void addStone(Stone stone) {
stone.start(); // start the thread (each stone is a thread)
set.add(stone); // put the stone into the set
System.out.print(set.size() + " ");
}
/**
* This method is called when the user clicks a point on the panel (mouseClicked awt event).
* This method removes more than one of the stones from the set, but...
* ...HERE I REMOVED THE SYNCHRONIZED MODIFIER AND I NEVER GOT ANY EXCEPTION.
*/
void killStone(int xClicked, int yClicked) {
final Iterator<Stone> iter = set.iterator();
Stone stone;
while (iter.hasNext()) {
stone = iter.next();
if (SOME CONDITIONS, READING THE STONE STATUS) {
stone.interrupt(); // stop the thread
iter.remove(); // remove the stone from the set
System.out.print(set.size() + " ");
}
}
if (set.isEmpty()) {
areaGrafica.repaint(); // remove the image of the final stone from the panel
}
}
/**
* This method is called by the run() method of the killer stones (see later).
* HERE THE SYNCHRONIZED MODIFIER IS NEEDED: IF I REMOVE IT, I GET java.util.ConcurrentModificationException
*/
synchronized void killNeighbouringGoodStones(int x, int y, int radius) {
final Iterator<Stone> iter = set.iterator();
Stone stone;
while (iter.hasNext()) {
stone = iter.next();
if (SOME OTHER CONDITIONS, USING THE STONE STATUS) {
stone.interrupt(); // stone is a thread
iter.remove(); // remove the stone from the set
System.out.print(set.size() + " ");
}
}
}
}
}
/**
* This is the run() method of the Stone class.
*/
@Override
public void run() {
try { // while into the try
while (true) {
animate(); // this simple method changes the stone state (*)
Stone.areaGrafica.repaint();
Thread.sleep(SLEEP); // SLEEP is 50 ms
}
} catch (InterruptedException ex) {
System.err.println(ex.getMessage());
}
}
(*) if the stone is a killer stone, the animate() method is overridden:
@Override
void animate() {
super.animate();
set.killNeighbouringGoodStones(getCenterX(), getCenterY(), getSize() / 2); // here set is the singleton StoneSet
}
/**
* This is the paintComponent() method of the panel.
*/
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
set.redrawAll(g);
}
Я думал, что модификатор synchronized
является обязательным для всех методов, которые обращаются к общемуобъект, но, видимо, это не так.
РЕДАКТИРОВАТЬ
У меня была идея: возможно ...
... redrawAll () иДля killNeighbouringGoodStones () требуется модификатор synchronized, поскольку эти методы вызываются другими потоками, которые я создал, в то время как ...
... addStone () и killStone () могут не синхронизироваться, посколькуметоды вызываются слушателями API с поддержкой графического интерфейса.
Может ли это быть правдой?