在Java中使用Synchronized关键字
我读过一些关于同步方法的文章(包括Oracle的),我不确定我是否理解正确 我有以下代码:在Java中使用Synchronized关键字,java,multithreading,synchronized,Java,Multithreading,Synchronized,我读过一些关于同步方法的文章(包括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<Integ
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;
}
}
公共类播放器扩展javax.swing.JLabel实现可运行{
私有静态int off2[]={-1,0,1,0},off1[]={0,1,0,1};
private static final Map dir=new java.util.HashMap();
静止的{
目录put(KeyEvent.VK_UP,0);
方向放置(KeyEvent.VK_RIGHT,1);
方向放置(KeyEvent.VK_DOWN,2);
方向放置(KeyEvent.VK_左,3);
}
私有布尔移动[]=新布尔[4];
@凌驾
公开募捐{
while(true){
对于(对象i:dir.values()){
if(isPressed((Integer)i))设置位置(getX()+off1[(Integer)i],getY()+off2[(Integer)i]);
}
试一试{
睡眠(10);
}catch(java.lang.InterruptedException e){
System.err.println(“中断异常:+e.getMessage());
}
}
}
公开作废开始(){
(新线程(此)).start();
}
私有同步布尔值isPressed(整数i){
如果(移动[i])返回true;
否则返回false;
}
按下公共同步无效设置(KeyEvent evt){
如果(dir.containsKey(evt.getKeyCode())移动[(Integer)dir.get(evt.getKeyCode())]=true;
}
已发布公共同步无效设置(KeyEvent evt){
如果(dir.containsKey(evt.getKeyCode())移动[(Integer)dir.get(evt.getKeyCode())]=false;
}
}
现在它所做的只是运动。我的理解是,当我的主窗体的键侦听器注册按键按下和释放事件时,setPressed和setrelease从主线程调用。这个准则合理吗?是否正确使用synchronized关键字?(代码在没有它的情况下可以工作,但我怀疑最好有它?使用synchronized是正确的,但我认为没有必要,因为在当前的实现中,对阵列的并行访问不会导致不一致 但是,我认为您的代码中存在一个不同的线程问题。您不应该从单独的线程与Swing(=调用setLocation())交互,请参阅。因此,您需要将更新代码包装成可运行的:
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。不确定事情的摇摆面,但一般来说,您需要同步来保护可能由多个线程访问的共享数据(在您的情况下是移动[]) 在这种情况下,您需要同步,因为在setXxx方法(主线程)写入时或在您启动的线程读取时可能会访问moving[]
自java 5以来,Java.Actudio包中的设备要好得多,我建议您考虑使用锁来保护Actudi[]或ActuiBooLo../P>。 几个“风格问题”:
// ...
private Lock movingLock = new ReentrantLock();
private boolean isPressed(Integer i){
try {
movingLock.acquire();
return moving[i];
} finally
movingLock.release();
}
}
您必须通过调用Lock.acquire()和release()“包装”setXxx方法中对moving[]的赋值。怀疑这样做是不好的理由,在这种情况下也是不正确的。您能告诉我为什么不正确吗?您能告诉我哪两个线程将同时调用这些方法中的任何一个吗?
if(moving[i])返回true;否则返回false
应该是return-moving[i]
足够正确,该方法是匆忙编写的。我有一个处理键盘输入的主框架,然后调用“Player”类方法。因此“Player”中的移动线程和主线程可以同时访问移动[]数组。如果需要的话,我可以在代码中进行编辑,但我认为这是不必要的。你能帮助我理解为什么我不能从两个线程与类交互吗?我查看了该链接,但它并没有真正帮助我……Swing不是线程安全的——基本上它不使用synchronized anywhere来保持设计简单和无死锁。因此,当您从单独的线程与swing交互时,您需要使用invokeLater。好的,谢谢。我想我现在明白了:通过invokeLater()将Runnable放在一个要完成的事情队列中?然后在一个单独的线程中一次执行一个?这是正确的吗?是的。我用一些代码片段更新了答案,以说明它是如何使用的;非常感谢。非常有帮助!除此之外,还有你提到的其他事情,代码看起来还行吧?还有,为什么从事件处理线程调用dirUpdate()可以消除同步的需要?那根线能提供某种安全吗?好的。非常感谢你。这个回答为我澄清了一些事情。首先,很明显我没有疯,因为你似乎明白我说的多线程访问它的意思,哈哈。第二,谢谢你提供的关于原始类型的提示。那个石膏让我很烦恼,但我不知道如何修复它。第三,感谢您提供关于锁的提示,但是您能帮助我理解锁的作用和synchronized关键字的作用之间的区别吗?