程序在 Thread.sleep() 和 Timer 期间冻结

Posted

技术标签:

【中文标题】程序在 Thread.sleep() 和 Timer 期间冻结【英文标题】:Program freezes during Thread.sleep() and with Timer 【发布时间】:2011-12-10 14:56:31 【问题描述】:

原问题:

这个方法应该将显示在 JFrame 上的图像逐渐更改为另一个图像。但是,如果没有某种方法可以减慢它的速度,它似乎只是从一个图像变为新图像。为了减慢它的速度,我放入了一个 Thread.sleep(1000) 这样更改不会立即发生。然而,有了这条线,我的程序就完全冻结了。没有错误信息,什么都没有。谁能帮帮我?建议一个更好的方法来减慢它,或者如何解决这个问题。

澄清一下:int k 是变化的渐进步骤数。 k = 1 将是一个瞬间的变化。任何更大的变化都是渐进的。 int l 同时控制每张图片的显示比例。

public void morphImg(int width, int height, BufferedImage morphImage, int k) 
    //creates new image from two images of same size
    BufferedImage image2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    for (int i = 0; i < width; i++) 
        for (int j = 0; j < height; j++) 
            //get color from original image
            Color c = new Color(image.getRGB(i, j));

            //get colors from morph image
            Color c2 = new Color(morphImage.getRGB(i, j));

            for (int l = 1; l <= k; l++) 
                //gets colors at different stages
                int r = ((k-l)*c.getRed()/k) + (l*c2.getRed()/k);
                int g = ((k-l)*c.getGreen()/k) + (l*c2.getGreen()/k);
                int b = ((k-l)*c.getBlue()/k) + (l*c2.getBlue()/k);   
                Color newColor = new Color(r, g, b);
                //set colors of new image to average of the two images
                image2.setRGB(i, j, newColor.getRGB());

                //display new image
                try 
                    imageLabel.setIcon(new ImageIcon(image2));
                    Thread.sleep(1000);
                
                catch (InterruptedException e)
                    System.out.println("Exception caught.");
                
            
        
    

    //sets modified image as "original" for further manipulation
    setImage(image2);

更新的代码:使用计时器也会导致程序冻结...我没有使用它吗?

public void morphImg(int width, int height, BufferedImage morphImage, int k) 
    //creates new image from two images of same size
    final BufferedImage image2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
    for (int l = 1; l <= k; l++) 
        for (int i = 0; i < width; i++) 
            for (int j = 0; j < height; j++) 
                //get color from original image
                Color c = new Color(image.getRGB(i, j));

                //get colors from morph image
                Color c2 = new Color(morphImage.getRGB(i, j));

                //gets colors at different stages
                int r = ((k-l)*c.getRed()/k) + (l*c2.getRed()/k);
                int g = ((k-l)*c.getGreen()/k) + (l*c2.getGreen()/k);
                int b = ((k-l)*c.getBlue()/k) + (l*c2.getBlue()/k);   
                Color newColor = new Color(r, g, b);

                //set colors of new image to average of the two images
                image2.setRGB(i, j, newColor.getRGB());
                //display new image

                imageLabel.setIcon(new ImageIcon(image2));
                final Timer t = new Timer(500,null);
                t.setInitialDelay(500);
                t.start();
            
        
    

    //sets modified image as "original" for further manipulation
    setImage(image2);

【问题讨论】:

你知道你总共在睡觉 (width * height * k) 秒吗?对于 k 设置为 10 的 256x256 图像,您的代码需要 7 天才能运行。 【参考方案1】:

在事件调度线程上执行代码时,切勿使用 Thread.sleep()。

您应该使用 Swing Timer 来安排动画。

请参阅Swing tutorial 上的部分:

    Swing 中的并发 如何使用定时器

或者,如果您不想使用 Timer,则可以使用 SwingWorker(如并发教程中所述),然后在更改图像后只需 publish() 即可。然后你可以使用 Thread.sleep() 因为 SwingWorker 不在 EDT 上执行。

简单计时器示例:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
import javax.swing.Timer;

public class TimerTime extends JPanel implements ActionListener

    private JLabel timeLabel;
    private int count = 0;

    public TimerTime()
    
        timeLabel = new JLabel( new Date().toString() );
        add( timeLabel );

        Timer timer = new Timer(1000, this);
        timer.setInitialDelay(1);
        timer.start();
    

    @Override
    public void actionPerformed(ActionEvent e)
    
        //  Update the time

        timeLabel.setText( new Date().toString() );
        count++;

        //  Stop after 10 events have been generated

        if (count == 10)
        
            Timer timer = (Timer)e.getSource();
            timer.stop();
            System.out.println( "Timer stopped" );
        
    

    private static void createAndShowGUI()
    
        JFrame frame = new JFrame("TimerTime");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.add( new TimerTime() );
        frame.setLocationByPlatform( true );
        frame.pack();
        frame.setVisible( true );
    

    public static void main(String[] args)
    
        EventQueue.invokeLater( () -> createAndShowGUI() );
    

【讨论】:

感谢您的提示...如何设置计时器以短暂暂停循环?我对定时器几乎不熟悉...... 这就是为什么我给你一个教程链接。然后您也可以在论坛或网络上搜索示例。 我尝试使用计时器,遇到完全相同的冻结问题...我没有正确实施它吗? 这是我的猜测。要使用 Timer,您需要重组代码。你永远不会有一个循环。计时器是循环。计时器将维护和更新 l、i、j 变量。 Timer 在循环代码之外启动,而不是在代码内部。从一个简单的例子开始,以了解基础知识。正如我在第二个建议中所建议的那样,SwingWorker 方法将允许您保持循环结构和 Thread.sleep()。【参考方案2】:

k 上的循环应该是最外层的循环。现在你正在调用 Thread.sleep k*width*height 次。

【讨论】:

这是真的……我已经改了。谢谢你的提示。然而,这并不能解决问题。【参考方案3】:

如果打算显示变形效果的渐进动画,下面是我使用TimerThread.sleep()所做的测试代码没有,使用OP给出的最新变形代码:

import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;

import javax.imageio.ImageIO;
import javax.swing.JApplet;
import javax.swing.JFrame;


class MorphComponent extends Component 

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    private BufferedImage bi;
    private URL imageSrc1;
    private URL imageSrc2;     
    public MorphComponent(URL imageSrc1, URL imageSrc2) 
        this.imageSrc1 = imageSrc1;
        this.imageSrc2 = imageSrc2;     
        try 
            BufferedImage img1 = ImageIO.read(imageSrc1);
            //BufferedImage img2 = ImageIO.read(imageSrc2);
            int w = img1.getWidth(null);
            int h = img1.getHeight(null);
            bi = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
            Graphics g = bi.getGraphics();
            g.drawImage(img1, 0, 0, null);

         catch (IOException e) 
            System.out.println("Image could not be read");
            System.exit(1);
        
    

    public Dimension getPreferredSize() 
        return new Dimension(bi.getWidth(null), bi.getHeight(null));
    


    public void paint(Graphics g) 
        Graphics2D g2d = (Graphics2D)g;
        g2d.setColor(Color.white);
        g2d.fillRect(0,0, getWidth(), getHeight());
        try 
            BufferedImage img1 = ImageIO.read(imageSrc1);
            BufferedImage img2 = ImageIO.read(imageSrc2);
            int w = img1.getWidth(null);
            int h = img1.getHeight(null);        
            bi = morphImg(g, img1, img2, w, h, 10);
         catch (IOException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
    

    private long start = System.currentTimeMillis();

    public BufferedImage morphImg(Graphics gp, BufferedImage originalImage, BufferedImage morphImage, int width, int height, int k) 
        //creates new image from two images of same size
        final BufferedImage image2 = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        for (int l = 1; l <= k; l++) 
            for (int i = 0; i < width; i++) 
                for (int j = 0; j < height; j++) 
                    long elapsed = System.currentTimeMillis() - start;
                    //get color from original image
                    Color c = new Color(originalImage.getRGB(i, j));

                    //get colors from morph image
                    Color c2 = new Color(morphImage.getRGB(i, j));

                    //gets colors at different stages
                    int r = ((k-l)*c.getRed()/k) + (l*c2.getRed()/k);
                    int g = ((k-l)*c.getGreen()/k) + (l*c2.getGreen()/k);
                    int b = ((k-l)*c.getBlue()/k) + (l*c2.getBlue()/k);   
                    Color newColor = new Color(r, g, b);

                    //set colors of new image to average of the two images
                    image2.setRGB(i, j, newColor.getRGB());

                    if( elapsed > 100 ) 
                        gp.drawImage(image2, 0, 0, null);
                        start = System.currentTimeMillis();
                        repaint();
                    

                
            
        
        return image2;
    



public class MorphImageApplet extends JApplet 

    /**
     * 
     */
    private static final long serialVersionUID = 1L;
    static String imageFileName1 = "image_1.jpg";
    static String imageFileName2 = "image_2.jpg";
    private URL imageSrc1;
    private URL imageSrc2;

    public MorphImageApplet () 
    

    public MorphImageApplet (URL imageSrc1, URL imageSrc2) 
        this.imageSrc1 = imageSrc1;
        this.imageSrc2 = imageSrc2;
    

    public void init() 
        try 
            imageSrc1 = new URL(getCodeBase(), imageFileName1);
            imageSrc2 = new URL(getCodeBase(), imageFileName2);
         catch (MalformedURLException e) 
        
        buildUI();
    

    public void buildUI() 
        final MorphComponent st = new MorphComponent(imageSrc1, imageSrc2);
        add("Center", st);
    

    public static void main(String s[]) 
        JFrame f = new JFrame("See Through Image");
        f.addWindowListener(new WindowAdapter() 
            public void windowClosing(WindowEvent e) System.exit(0);
        );
        URL imageSrc1 = null;
        URL imageSrc2 = null;
        try 
             imageSrc1 = ((new File(imageFileName1)).toURI()).toURL();
             imageSrc2 = ((new File(imageFileName2)).toURI()).toURL();
         catch (MalformedURLException e) 
        
        MorphImageApplet sta = new MorphImageApplet(imageSrc1, imageSrc2);
        sta.buildUI();
        f.add("Center", sta);
        f.pack();
        f.setVisible(true);
    

【讨论】:

以上是关于程序在 Thread.sleep() 和 Timer 期间冻结的主要内容,如果未能解决你的问题,请参考以下文章

我需要python time.sleep()在C#中等效

Java Thread Sleep 确切的特定时间量(包括某些进程)

程序在 Thread.sleep() 和 Timer 期间冻结

Thread.sleep 没有抛出 InterruptedException

java Thread.sleep卡死问题

Thread.sleep() 和 GUI