如何在 ImageLoader 中同步 ImageIcon

Posted

技术标签:

【中文标题】如何在 ImageLoader 中同步 ImageIcon【英文标题】:How to synchronize ImageIcon in ImageLoader 【发布时间】: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) 构造函数已经使用异步图像加载。因此,将其执行移至后台线程是没有意义的。 【参考方案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();

附注

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

【讨论】:

以上是关于如何在 ImageLoader 中同步 ImageIcon的主要内容,如果未能解决你的问题,请参考以下文章

Android UI-开源框架ImageLoader的完美例子

Android UI-开源框架ImageLoader的完美例子

ImageLoader的使用

ImageLoader的使用

Image-Universal-Loader

ImageLoader作用 AAAA