【问题标题】:How to synchronize ImageIcon in ImageLoader如何在 ImageLoader 中同步 ImageIcon
【发布时间】:2021-12-23 21:31:21
【问题描述】:

我最近开始在 Java 中使用多线程,我遇到了一个问题,我认为是由于缺少同步。

这是我写的 ImageLoader:

package util;

import javax.swing.ImageIcon;
                                          
public class ImageLoader extends Thread {

    private String file;
    private ImageIcon icon;
    
    public ImageLoader(String file) {
        this.file = file;
    }
    
    @Override
    public void run() {
        
        ImageIcon icon = new ImageIcon(this.file);
        this.icon = icon;
        super.run();
    }
    
    public synchronized ImageIcon returnIcon() {
        return this.icon;
    }

}

我在我的 GUI 类中使用这个 ImageLoader:

package gui;

import java.awt.Dimension;
import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;

import util.ImageLoader;

public class HauptGui extends JComponent {

    public HauptGui() {
        initUI();
    }

    private void initUI() {
        
        int bilderAnzahl = 3;
        
        this.setLayout(new GridLayout(1, 1));
        
        JPanel  bilderPanel = new JPanel(new GridLayout(bilderAnzahl, 1));
        for (int i = 0; i < bilderAnzahl; i++) {
            JLabel jbl = new JLabel();
            ImageLoader loader = new ImageLoader("./Picture.jpg");
            loader.start();
            jbl.setIcon(loader.returnIcon());           
            jbl.setBorder(BorderFactory.createEtchedBorder());
            jbl.setPreferredSize(new Dimension(200, 50));
            bilderPanel.add(jbl);
        }
        
        JScrollPane scrPn = new JScrollPane(bilderPanel);
        
        this.add(scrPn);
        
    }
    
}

问题是ImageLoader的returnIcon-Method在Thread调用run-Method之前被调用,因此ImageIcon仍然为null。

如何同步?

【问题讨论】:

  • ImageIcon(String) 构造函数已经使用异步图像加载。因此,将其执行移至后台线程是没有意义的。

标签: java multithreading swing synchronization


【解决方案1】:

不,您的问题与同步无关,而与创建图像对象之前简单地请求有关。解决方案是在完成加载后在回调中获取图像对象。 SwingWorker 在这种情况下会很好地工作,您可以通过调用 .get() 在工作人员的 done 方法中从 SwingWorker 获取图像对象,或者您可以使用 PropertyChangeListener 进行回调。有关如何使用 SwingWorkers 的详细信息,请参阅Lesson: Concurrency in Swing

例如(代码未测试)

public class ImageLoader2 extends SwingWorker<BufferedImage, Void> {
    private String path = "";  /// String to resource path
    
    public BufferedImage doInBackground() throws Exception {
        return ImageIO.read(ImageLoader2.class.getResource(path));
    }
    
}

然后像这样运行它:

ImageLoader2 loader = new ImageLoader2();

loader.addPropertyChangeListener(pce -> {
    if (evt.getNewValue() == SwingWorker.StateValue.DONE) {
        try {
            BufferedImage img = loader.get();
            ImageIcon icon = new ImageIcon(img);
            
            // use icon here...
            
        } catch catch (InterruptedException | ExecutionException e) {
            e.printStackTrace();
            // handle exception here
        }
    }
});
loader.execute();

附注

  1. 您几乎应该从不扩展 Thread。
  2. 不要将图像作为文件获取,而是将其作为资源获取并使用ImageIO.read(...) 来执行此操作
  3. 资源路径是相对于类路径的,而不是相对于用户目录的,因此它可能与您用于将图像作为文件获取的路径不同。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-11-25
    • 2017-03-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多