Java多线程,使线程等待另一个线程,或加入不同的线程

时间:2011-04-07 13:05:35

标签: java multithreading thread-safety

这是我第一次问这里。我在搜索之前有很多次帮助,总是找到这个网站。但现在我无法找到问题的答案。它有许多其他问题的情况,但我找不到我需要的那个。

好的,所以我有一个名为玩家,农场和土地的班级。农场就是那种控制者。 Farm有一个名为map的对象数组,它与land和player共享(在构造时给定),在map中是player和land本身。

我想做的是,

我左键单击了farm,然后根据我单击的位置从地图中获取了对象,然后将该对象保存为选中状态。然后我右键单击其他对象并获取对象,让我们调用它。 Actioned有一个方法,getMenu(选中),它返回一个JPopMenu,基于选中。作为一个例子,如果玩家是被选中的,并且陆地被动作,则getMenu()将返回菜单“犁”,“水”和“移动”。如果我点击“犁”,那么土地将会改变,当然地图也会改变,与其他物体共享。 问题是,在犁开始真正的动作之前,我需要玩家首先在陆地附近走到网格。所以在犁菜单中我添加了一个ActionListener,如下所示:

new ActionListener() {
        public void actionPerformed(ActionEvent e) {
                int gx = (int)Point2D.this.X();
                int gy = (int)Point2D.this.Y();
                Object lock = new Object();
                player.move(gx,gy,lock);
                try {
                    lock.wait();
                } catch (InterruptedException ex){}
                // do the real action of plow here
            }
        }
}

玩家的移动方法

public void move(int gx,int gy,Object lock){
    setDestination(gx, gy);
    this.lock = lock;
}

服务器场是一个Runnable对象,因此每次服务器场都会调用pl​​ayer.Update()。

public void update(){
    updatePosition();
    if (position==destination) {
        lock.notify();
    }
}

我想到了一个名为lock的对象,并将该对象赋予了玩家。 Listener本身就是一个正在运行的线程,并在单击菜单时启动,因此我根据锁定对象使Listener的线程等待。通知是从服务器场线程中提供的。

但是,当然,这不能正常工作,并抛出java.lang.IllegalMonitorStateException。我真的没有从网上获得同步解释。 问题1:我应该如何在此处使用同步来使代码正常工作?或者如果同步在这里永远不会有效,我该怎么办?

我也想过使用其他方法。

我将播放器作为可运行的线程。现在我有3个线程,农场,听众(土地)和玩家。

因此,我不是创建一个锁对象,而是创建一个新的Thread(Player)。玩家的跑步方法:

while (position != destination) {
       try {
            Thread.sleep(100);
       } catch (InterruptedException e){}
   }

新的侦听器方法:

        Player player = (Player) selected;
        int gx = (int)Point2D.this.X();
        int gy = (int)Point2D.this.Y();

        player.move(gx,gy);
        Thread waitplayernotmoving = new Thread(player);
        waitplayernotmoving.start();
        try {
            waitplayernotmoving.join();
        } catch (InterruptedException e){}

工作得很好。 问题2:是在等待玩家线程时陆地线程停止工作?如果没有,如何在等待玩家线程死亡时让陆地线程停止?

我还有其他问题。如果我耕地1,然后当玩家移动时,我点击land2并耕地2,玩家转向land2!然后在它再次移动之后,两块土地都被耕了!意味着land1的线程仍处于活动状态。

我有两个问题: 问题3:如果我向玩家设置另一个动作,例如犁land2,是否有可能使land1线程死亡?

问题4:是否有可能根据我的程序现在所做的事情,在land1线程之后执行land2线程?所以行动就像一个队列。我耕地1,然后耕地2,行动发生将是玩家移动到土地1然后耕地1,然后移到land2,耕地2。

问题5 还记得我的地图,对所有人共享的对象数组吗?如何确保安全?我的意思是,看起来会有很多线程读取和写入地图。我读到某个地方,如果它是不可变的,那就不会有问题了。但它经常被重写......这不是一个问题吗?

非常感谢你阅读这篇长篇解释,我希望你能给我一个答案。 :(

2 个答案:

答案 0 :(得分:2)

我将回答其中一个问题5:

你说“如果这是不可改变的,那就不会有问题”然后你说“但它经常重写” - 我将采取的意思是它不是一成不变的。最简单的方法是在整个阵列上进行同步。

synchronized (map) {
    // modify map
}

这种情况的结果是,你现在可以在想要修改数组的任何时候锁定所有其他线程(即使只有1个正在写入,10个正在读取)。并且很有可能多个并发线程实际上并不想修改应该没问题的相同元素。您需要担心的唯一情况是多个线程想要更​​改相同的map元素。第二种更精细的解决方案是读/写锁。只有在线程想要更​​改它时才锁定整个映射,并且永远不会阻塞读取。 (见ReentrantReadWriteLock

readLock.lock();
try {
   // read from map
} finally {
   readLock.unlock();
}

writeLock.lock();
try {
   // modify map
} finally {
   writeLock.unlock();
}

现在有一个更精细的粒度,你只能锁定地图中的各个元素 - 但这有点超出我认为的范围。 PLus为您提供了一些自己思考的东西。希望有所帮助。

答案 1 :(得分:0)

好吧,不知怎的,我找到了答案,但不是为了所有人。

问题1:Thx @noob注释,“无论何时等待并通知对象(在这种情况下,锁定),您都需要在同步块中执行这些操作”。所以我只是在同步块中进行锁定。像这样:

       int gx = (int)Point2D.this.X();
       int gy = (int)Point2D.this.Y();
       Object lock = new Object();
       player.move(gx,gy,lock);
       synchronized(lock){
           try {
               lock.wait();
           } catch (InterruptedException ex){}
       }

当然,当给出notify的锁也给它一个synchronized块。但它发出警告:“在非最终领域同步”。这是因为锁有可能被其他进程修改,并且会造成死锁,因为锁失去了引用。

问题2:不。土地仍然活跃。所以它正在使用资源。在等待播放器线程时线程仍处于活动状态。你可以通过让线程等待来使用1号技术。找到wait()的线程会将监视器提供给其他线程。它是一种睡眠,当调用wait()的对象调用notify()时唤醒。

问题3:有点找到答案,但还是没有尝试,所以我不会回答。

问题4:是的。有可能的。让玩家拥有一个锁而不是锁的队列。所以在move()中,它不是设置锁,而是为队列添加锁。而且通知也是这样做的,它只通知前锁。在前锁之后,给出通知。

问题5:向@Gandalf回答,但仍然不知道我是否正确。不知道如何测试它。