比如另开一个线程响应panel的重绘事件?

解决方案 »

  1.   

    这个多线程重绘,是你创新的么?还是?Java中,GUI线程只有一条,而且从并发安全性上而言,是不允许其它线程随意去访问Graphics的。所以多线程GUI系统,都是只多线程处理各类数据,或者各自处理图形缓冲,而最终负责输出到屏幕的,只有GUI线程。
    不知道楼主你的期望是什么?
      

  2.   


    绘图过程可以绘制到 BufferedImage 上,主线程只负责最后的合成及屏幕输出。
      

  3.   

    “绘图过程可以绘制到 BufferedImage 上”还是需要在线程里执行绘图过程啊
      

  4.   


    楼主听说过 双缓冲 之类的技术么?类似但是不完全一样。如我1楼所说,你要能区分出 GUI主线程 和 其它线程。GUI主线程负责控制容器的输出,这个是独占的。而其它线程可以将所需绘制内容先绘制到各自的BufferedImage上,这期间,BufferedImage是由各自线程所独占的。我并没有说线程不能执行绘图,我只是强调GUI主线程的独占性。最后GUI主线程定期将Buffered的内容合成输出到主容器上(约等于屏幕),这样GUI的工作量就很小了。
      

  5.   


    对的,先把要再GUI显示的,绘制到BufferedImage,最后一次更新到界面。
    如果界面对应的BufferedImage比较多,到可以用多线程来绘制BufferedImage。
    但是主的GUI线程只有1个。
      

  6.   

    楼上几位的方法,我看得不是很明白我要在panel上绘图,假如重绘时调用paint(),我另开一个线程,在paint()中向线程发送对象及重绘事件,在线程中重绘,这样做有什么问题吗?
      

  7.   

    一个 C# 多线程重绘界面的例子http://topic.csdn.net/u/20120805/01/58230777-2767-4337-A8F8-A3725E7DB4B1.html
      

  8.   


    看过你的例子了,这个Java也支持。不过直接这么用,应该是不能解决你的问题的:javax.swing.SwingUtilities.invokeLater(doRun);它原理上仍然是借助GUI主线程,如果所Invoke的doRun运算很复杂,仍然会阻塞GUI线程。
    public class TestInvokeLater extends JFrame {    /**
         * 负责绘图
         */
        private Runnable doBusyDraw = new Runnable() {
            public void run() {
                try {
                    Thread.sleep(5000); // 每次绘图执行花 5 秒
                } catch (InterruptedException e) {
                }
                System.out.println("BusyDraw done.");
            }
        };    /**
         * 负责定时刷新
         */
        private Thread refresher = new Thread() {
            public void run() {
                try {
                    while (true) {
                        SwingUtilities.invokeLater(doBusyDraw);
                        Thread.sleep(1000); // 每秒执行 1 次绘图
                    }
                } catch (InterruptedException e) {
                }
            }
        };
        
        public TestInvokeLater() {
            this.setSize(800, 600);
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);        JButton btnBusyDraw = new JButton("StartBusyDraw");
            btnBusyDraw.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    refresher.start();
                    System.out.println("Thread started...");
                }
            });        this.add(btnBusyDraw);
        }    /**
         * 启动主函数
         */
        public static void main(String[] args) {
            TestInvokeLater wnd = new TestInvokeLater();
            wnd.setVisible(true);
        }
    }
      

  9.   

    “它原理上仍然是借助GUI主线程,如果所Invoke的doRun运算很复杂,仍然会阻塞GUI线程。”
    我不想用GUI主线程绘图,就想另开一个线程执行绘图操作,难到就没有办法了吗?
      

  10.   


    可以,不要访问控件,仅操作Graphics。
    import java.awt.*;
    import java.awt.event.*;
    import java.util.*;import javax.swing.*;public class MultiThreadDraw extends JFrame {    /**
         * 窗体构造函数
         */
        public MultiThreadDraw() {
            this.setSize(800, 600);
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);
            JButton btnStartThreads = new JButton("StartManyThread");
            btnStartThreads.addActionListener(new ActionListener() {
                public void actionPerformed(ActionEvent e) {
                    Graphics g = ((JComponent) e.getSource()).getParent().getGraphics();
                    System.out.println(g.getClass());
                    for (int i = 0; i < 50; i++) {
                        new DrawWorker(g).start();
                    }
                    System.out.println("Thread started...");
                }
            });
            this.getContentPane().add(btnStartThreads);
        }    /**
         * 启动主函数
         */
        public static void main(String[] args) {
            MultiThreadDraw wnd = new MultiThreadDraw();
            wnd.setVisible(true);
        }    private class DrawWorker extends Thread {
            private Graphics g;        public DrawWorker(Graphics g) {
                this.g = g;
            }        public void run() {
                Random rand = new Random();
                while (true) {
                    g.setColor(new Color(rand.nextInt()));
                    g.drawString(String.valueOf(rand.nextInt()), rand.nextInt(800), rand.nextInt(600));
                    g.drawLine(rand.nextInt(800), rand.nextInt(600), rand.nextInt(800), rand.nextInt(600));
                }
            }
        }
    }
    不过还是推荐用BufferedImage,不要全部都直接绘制容器的Graphics
      

  11.   

    非常感谢楼上的哥们!哥们给的代码运行起来很精彩!
    for (int i = 0; i < 50; i++) {
          new DrawWorker(g).start();
     }
    我的理解不知对不对?
    for循环每次都新建一个线程,线程里有个while(true)循环会一直运行,直至程序退出
    建第1个线程,第1个线程开始运行,现在有1个线程在运行
    建第2个线程,第2个线程开始运行,现在有2个线程在运行
    建第3个线程,第3个线程开始运行,现在有3个线程在运行
    建第4个线程,第4个线程开始运行,现在有4个线程在运行
    建第5个线程,第5个线程开始运行,现在有5个线程在运行
    ........
      

  12.   


    理解正确。其实你可以打开 任务管理器,然后在“查看”->“选择列”中增加 线程数。然后多点几次按钮,可以看到线程暴涨注意:线程数越多,线程自身切换的代价越高,系统速度越慢。最后如果是几百个线程跟GUI线程抢占CPU资源的话,界面也会卡的。
      

  13.   

    有啊,工作线程绘制到BufferedImage上,主线程只负责输出。一般来说都比较推荐这种:
    import java.awt.*;
    import java.awt.event.*;
    import java.awt.image.*;
    import java.util.*;import javax.swing.*;public class BufferedImageGUI extends JFrame implements Runnable {    private volatile BufferedImage boardDrawing;    private volatile BufferedImage boardDisplaying;    /**
         * 窗体构造函数
         */
        public BufferedImageGUI() {
            this.setSize(800, 600);
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);        boardDrawing = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
            boardDisplaying = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
        }    public void paint(Graphics g) {
            g.drawImage(boardDisplaying, 0, 0, null);
        }    public void run() {
            Random rand = new Random();
            while (true) {
                // 复杂的绘制过程
                Graphics g = boardDrawing.getGraphics();
                g.clearRect(0, 0, boardDrawing.getWidth(), boardDrawing.getHeight());
                for (int i = 0; i < 500; i++) {
                    g.setColor(new Color(rand.nextInt()));
                    g.drawString(String.valueOf(rand.nextInt()), rand.nextInt(800), rand.nextInt(600));
                }
                
                // 切换前景与背景工作区
                BufferedImage tmp = boardDisplaying;
                boardDisplaying = boardDrawing;
                boardDrawing = tmp;
                this.repaint();
                
                // Sleep
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }    /**
         * 启动主函数
         */
        public static void main(String[] args) {
            BufferedImageGUI wnd = new BufferedImageGUI();
            wnd.setVisible(true);
            new Thread(wnd).start();
        }
    }
      

  14.   


    楼主听说过 双缓冲 之类的技术么?类似但是不完全一样。如我1楼所说,你要能区分出 GUI主线程 和 其它线程。GUI主线程负责控制容器的输出,这个是独占的。而其它线程可以将所需绘制内容先绘制到各自的BufferedImage上,这期间,BufferedImage是由各自线程所独占的。我并没有说线程不能执行绘图,我只是强调GUI主线程的独占性。最后GUI主线程定期将Buffered的内容合成输出到主容器上(约等于屏幕),这样GUI的工作量就很小了。
    你好,你说的意思我懂了,但是我有个问题还是不明白:假如我最终输出到屏幕上的图像是由3部分组成(或者更多的部分组成的),比如说是个游戏,首先要绘制地图(创建一个线程将地图绘制在自己线程内的BufferedImage1),然后还有一个障碍物同样一个线程绘制到自己的BufferedImage2上,还有一个游戏人物角色也创建一个线程绘制到自己的BufferedImage3上,最后将BufferedImage1,BufferedImage2,BufferedImage,3按1、2、3的优先次序绘制到主线程的BufferedImage的上,然后再输出,但是问题是,线程的执行需要得到处理机,所以绘制操作会有迟延或者其他误差,然而我的游戏是动态更新画面的,下一帧绘制的会不会出现是上一刻游戏的地图和当前游戏中的障碍物和下一时刻游戏中人物的形态呢?