我已经阅读过有关同步方法的一些文章(包括Oracle),我不确定我是否理解它。
我有以下代码:
public class Player extends javax.swing.JLabel implements Runnable{
private static int off2[] = {-1, 0, 1, 0}, off1[] = {0, 1, 0, -1};
private static final Map dir = new java.util.HashMap<Integer, Integer>();
static{
dir.put(KeyEvent.VK_UP, 0);
dir.put(KeyEvent.VK_RIGHT, 1);
dir.put(KeyEvent.VK_DOWN, 2);
dir.put(KeyEvent.VK_LEFT, 3);
}
private boolean moving[] = new boolean[4];
@Override
public void run(){
while(true){
for(Object i : dir.values()){
if(isPressed((Integer)i)) setLocation(getX() + off1[(Integer)i], getY() + off2[(Integer)i]);
}
try{
Thread.sleep(10);
}catch(java.lang.InterruptedException e){
System.err.println("Interrupted Exception: " + e.getMessage());
}
}
}
public void start(){
(new Thread(this)).start();
}
private synchronized boolean isPressed(Integer i){
if(moving[i]) return true;
else return false;
}
public synchronized void setPressed(KeyEvent evt) {
if(dir.containsKey(evt.getKeyCode()))moving[(Integer)dir.get(evt.getKeyCode())] = true;
}
public synchronized void setReleased(KeyEvent evt){
if(dir.containsKey(evt.getKeyCode()))moving[(Integer)dir.get(evt.getKeyCode())] = false;
}
}
现在它所做的只是运动。我的理解是,当我的主窗体的键监听器注册keyPressed和Released事件时,从主线程调用setPressed和setReleased。这段代码合理吗?它是否正确使用synchronized关键字? (代码在没有它的情况下工作,但我怀疑拥有它更好吗?)
答案 0 :(得分:1)
使用synchronized是正确的,但我不认为这是必要的,因为在当前的实现中,并行访问数组不会导致不一致。
但是,我认为您的代码中存在不同的线程问题。您不应该从单独的线程与Swing(= call setLocation())进行交互,请参阅http://docs.oracle.com/javase/1.4.2/docs/api/javax/swing/SwingUtilities.html#invokeLater(java.lang.Runnable)。因此,您需要将更新代码包装到Runnable中:
private boolean moving[] = new boolean[4];
Runnable dirUpdate = new Runnable() {
for(Object i : dir.values()){
if(isPressed((Integer)i)) setLocation(getX() + off1[(Integer)i], getY() + off2[(Integer)i]);
}
};
来自线程的调用将如下所示:
SwingUtils.invokeLater(dirUpdate);
请注意,在这种情况下,您将不再需要同步,因为将从事件处理线程调用dirUpdate。
您可能希望在isPressed()中检查null,以避免在未映射的键上出现异常。
运动的简单表示可能是dx和dy变量可以通过键事件设置为-1,1或0。
答案 1 :(得分:1)
不确定Swing方面的事情,但一般来说你需要synchronized来保护那些可能被多个线程访问的共享数据(在你的情况下移动[])。
在这种情况下,您需要同步,因为move []可以通过setXxx方法(主线程)写入时访问,也可以在您启动的线程读取时访问。
从Java 5开始,java.concurrent包中的工具要好得多,我建议你考虑使用Lock来保护moving []或AtomicBoolean。一些“风格问题”:
不要使用'raw types' - 更喜欢Map&lt; Integer,Integer&gt;映射(也可以保存你从setXxxx()方法中未经检查的-ugly-cast)
避免单行if - 使代码难以理解:)
如果你决定使用Lock(在这种情况下不是一个很大的优势v。同步)你的isPressed()应该是这样的:
// ...
private Lock movingLock = new ReentrantLock();
private boolean isPressed(Integer i){
try {
movingLock.acquire();
return moving[i];
} finally
movingLock.release();
}
}
你必须通过调用Lock.acquire()和release()
将setX'方法中的赋值“包装”到move []中