【问题标题】:In Java, why is my multithreading not working?在 Java 中,为什么我的多线程不起作用?
【发布时间】:2012-05-04 22:19:03
【问题描述】:

一开始我是这样做的:

public SpaceCanvas(){
    new Thread(new Runnable () {//this is the thread that triggers updates, no kidding
        int fcount = 0;

        @Override
        public void run() {
                System.out.println("Update thread started!");
                while(!Thread.interrupted()){
                    fcount++;
                    while(players.iterator().hasNext()){
                        players.iterator().next().update(fcount);
                    }
                    while(entities.iterator().hasNext()){
                        entities.iterator().next().update(fcount);
                    }
                    System.out.println("About to paint");
                    repaint();
                    System.out.println("Done with paints");
                    try {
                        Thread.sleep(500);
                    } catch (InterruptedException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
        }
    }).start();
    players.add(new LocalPlayer(0, 9001, 0, 0, 0, 0, this, null));
}

在我称之为 SpaceCanvas 的事物的初始化程序中。 但是,这不允许创建画布,因此也不允许创建它所在的小程序,因为线程 实际上 不会异步运行。然后,我将“.start()”替换为“.run()”,线程只运行了一次,但 SpaceCanvas 完美初始化。

我做错了什么,我该如何解决?

【问题讨论】:

    标签: java multithreading asynchronous applet graphics2d


    【解决方案1】:

    我不确定这种代码是否按您期望的方式工作:

    while(players.iterator().hasNext()){
        players.iterator().next().update(fcount);
    

    players.iterator()players 集合获取一个新的迭代器。如果集合中有 0 个项目,那么它将是 false 但如果有任何项目,您将处于无限循环中,每次都会创建一个新的迭代器。 players 内部的 iterator() 调用也会生成另一个新的迭代器对象。

    我认为你应该这样做:

    Iterator iterator = players.iterator();
    while (iterator.hasNext()) {
        iterator.next().update(fcount);
    }
    

    这也与您的 entities 循环相同。更好的模式(从 Java 5 开始)是使用 for 循环:

    for (Player player : players) {
        player.update(fcount);
    }
    

    此外,如果多个线程正在访问这些集合,它们必须以某种方式synchronized。您可以使用并发集合,也可以确保每个访问(读取和写入)都在 synchronized 块内。

    synchronized (players) {
        for (Player player : players) {
            player.update(fcount);
        }
    }
    ...
    // down in the outer thread
    synchronized (players) {
        players.add(new LocalPlayer(0, 9001, 0, 0, 0, 0, this, null));
    }
    

    显然entities 需要以同样的方式成为synchronized

    【讨论】:

    • 你也可以使用for (Iterator<Player> i = players.iterator(); i.hasNext(); ) i.next().update(fcount);
    【解决方案2】:
    1. 在这个千禧年,使用 Swing (JApplet/JPanel) 而不是 AWT (Applet/Canvas)
    2. 使用 Swing 时,建立一个 SwingTimer,每 500 毫秒调用一次repaint()
    3. (使用 Swing/Timer 时)不要在 EDT(事件调度线程)上调用 Thread.sleep(n)

    ..你能在 JPanel 上画画吗?

    当然。为此,请覆盖 paintComponent(Graphics) 方法。您也可以扩展 JComponent 并执行相同的操作,但处理 JComponent 时有一些怪癖,这使得 JPanel 成为扩展的更好选择。

    另一方面,完全有另一种方法。

    • 创建一个BufferedImage,其大小可满足任何自定义图形的需求。
    • 将图片添加到ImageIcon
    • 将图标添加到JLabel
    • 将标签添加到 GUI。
    • 在每个Timer 操作上。
      • 调用image.getGraphics()获取绘图表面。
      • 复制您在paint()paintComponent() 中可能做过的事情
        1. (如果需要)删除所有以前的绘图。
        2. 绘制当前的自定义渲染。
      • 图像的Graphics 实例的dispose()
      • 致电label.repaint()

    【讨论】:

    • 我会尽快尝试,谢谢。另外,你能在 JPanel 上画画吗?您可以在任何 Swing 上绘制任何东西吗?我想使用 Canvas 以便可以在上面画画。
    • 这真的非常有用,(即使它没有解决我的问题)所以感谢您让我保持在千禧年的编码标准内!
    猜你喜欢
    • 2022-12-06
    • 1970-01-01
    • 1970-01-01
    • 2012-11-24
    • 1970-01-01
    • 1970-01-01
    • 2013-04-02
    • 2011-11-14
    • 2016-08-22
    相关资源
    最近更新 更多