我想知道什么时候向修改共享对象的方法添加synchronized
修饰符,什么时候不添加。
我写了一个弹跳球游戏的修改版本。每个球(我称之为“石头”)都是一个thread
。为了管理repaint
进程,我保存了一个石头的HashSet
,有几个方法处理这个集合:
当我添加一个新的石头(如果用户按下图形用户界面上的按钮)时触发
panel
);paintComponent()
方法时。好吧,我认为所有处理这些东西的方法都必须声明为synchronized
。但我做了一些尝试,我发现:
synchronized
修饰符(如果我删除它,我会得到一个异常,OK,这就是我要做的事情)另外一些情况下,我删除了synchronized
修饰符expected);我谷歌了很多,但我读到当方法访问共享对象时总是需要synchronized
修饰符,而不是只有当对象不可变时才需要它。但是我不能理解为什么我可以从那些方法中删除synchronized
修饰符,而不获得异常。
现在我附加了StoneSet
类,所有这些方法都是在这个类中定义的。这个类是一个单例:应用程序中几乎所有其他对象都只创建和共享它的一个实例。我清除了这个类中所有不必要的代码,并写了许多注释来帮助读者理解这个类,并(我希望)告诉我发生了什么。我为我的长篇附件(超过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
修饰符对于所有访问共享对象的方法都是强制的,但显然这不是真的。
编辑
我有个想法:也许..。
这是真的吗?
发布于 2019-05-20 05:43:54
HashSet
是一个快速失败的集合,并且不允许您同时使用不同的迭代器从其中删除元素。换句话说:即使你同步了HashSet
本身,你仍然会得到这个错误,即使--从纯线程安全的角度--代码是正确的。发布于 2019-05-20 01:31:30
如果多个线程试图同时更改同一对象,则使用同步。正如你注意到的,如果从你的一些方法中移除synchronized,你会得到一个异常,因为所有这些方法都试图同时修改HashSet。
您必须确保以下情况不会同时发生(由两个不同的线程触发):
1. Iteration over the values in the the HashSet
2. Modification of the Data inside the HashSet
从代码风格的角度来看,同步从来都不是“强制性的”。这取决于开发人员找出在哪里使用它,以便您的代码正确工作。
您还需要查看“volatile”关键字,以确保所有线程都能看到相同的内容。
https://stackoverflow.com/questions/56209755
复制相似问题