在使用SwingWorker的Java线程池内等待任务。

huangapple go评论66阅读模式
英文:

Waiting for a task inside a thread pool using SwingWorker java

问题

我正在线程内模拟多个任务 - 当所有任务都应该报告给 GUI。所以我有一个唯一的表单,里面有4个面板,它们应该回复并执行不同的任务... 它们每个都应该继续轮询数据库并将内容报告给 GUI。在这个例子中,我只是让它向一个文本区域写入数字。

public class FormMain extends JFrame {
	private JButton btnStart;
	private JPanel _panelTop, _panelMid, _panelBot;

	private PanelFoo panelFooA, panelFooB, panelFooC, panelFooD;

	private final List<String> ugsRj = Arrays.asList("AB");
	private final List<String> ugsMg = Arrays.asList("CD", "EF");
	private final List<String> ugsBA = Arrays.asList("GH", "IJ", "KL");
	private final List<String> ugsPE = Arrays.asList("MN", "OP", "RS", "TU");

	private void initialize() {
		// 初始化代码...
	}

	public FormMain() {
		initialize();
		// 其他初始化和监听代码...
	}
}

public class ProgressListener implements PropertyChangeListener {
	private JProgressBar bar;
	// 其他代码...
}

public class PanelFoo extends JPanel {
	// 面板内部元素和方法...
}

public class WorkerDoSomething extends SwingWorker<Void, Void> {
	// 任务执行代码...
}

这是所有主要的代码部分。

英文:

I'm simulating multiple Tasks inside a thread - when all of them should report to a GUI. So I have a unique form that has 4 Panels inside which should reply and do different Tasks.. they should each keep pooling a database and reporting stuff to the GUI. In this example, I just made it to write numbers to a textArea.

public class FormMain extends JFrame {
	private JButton btnStart;
	private JPanel _panelTop, _panelMid, _panelBot;

	private PanelFoo panelFooA, panelFooB, panelFooC, panelFooD;

	private final List&lt;String&gt; ugsRj = Arrays.asList(&quot;AB&quot;);
	private final List&lt;String&gt; ugsMg = Arrays.asList(&quot;CD&quot;, &quot;EF&quot;);
	private final List&lt;String&gt; ugsBA = Arrays.asList(&quot;GH&quot;, &quot;IJ&quot;, &quot;KL&quot;);
	private final List&lt;String&gt; ugsPE = Arrays.asList(&quot;MN&quot;, &quot;OP&quot;, &quot;RS&quot;, &quot;TU&quot;);

	private void initialize() {
		this._panelTop = new JPanel();
		this._panelMid = new JPanel();
		this._panelBot = new JPanel();

		this.btnStart = new JButton(&quot;Start&quot;);
		this._panelBot.add(this.btnStart);

		this.panelFooA = new PanelFoo(this.ugsRj);
		this.panelFooB = new PanelFoo(this.ugsMg);
		this.panelFooC = new PanelFoo(this.ugsBA);
		this.panelFooD = new PanelFoo(this.ugsPE);
		_panelMid.setLayout(new BoxLayout(_panelMid, BoxLayout.X_AXIS));

		this._panelMid.add(this.panelFooA);
		this._panelMid.add(this.panelFooB);
		this._panelMid.add(this.panelFooC);
		this._panelMid.add(this.panelFooD);

	}

	public FormMain() {
		initialize();
		getContentPane().setLayout(new BorderLayout());
		setSize(800, 516);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		getContentPane().add(this._panelTop, BorderLayout.NORTH);
		getContentPane().add(this._panelMid, BorderLayout.CENTER);
		getContentPane().add(this._panelBot, BorderLayout.SOUTH);

		this.btnStart.addActionListener(new ActionListener() {
			@Override
			public void actionPerformed(final ActionEvent e) {
				final AuthenticationUser auth = new AuthenticationUser();
				auth.setUser(&quot;test&quot;);
				auth.setPassword(&quot;p@ss&quot;);
//
				final SwingWorker&lt;Void, Void&gt; worker = new SwingWorker&lt;Void, Void&gt;() {
					@Override
					protected Void doInBackground() throws Exception {
						final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
						w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
						final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
						w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
						final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
						w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
						final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
						w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));

						w1.execute();
						w2.execute();
						w3.execute();
						w4.execute();

						return null;
					}

				};
				worker.execute();
			}
		});

	}
}

public class ProgressListener implements PropertyChangeListener {
	private JProgressBar bar;

	ProgressListener() {
	}

	ProgressListener(final JProgressBar b) {
		this.bar = b;
		this.bar.setValue(0);
	}

	@Override
	public void propertyChange(final PropertyChangeEvent evt) {
		// Determine whether the property is progress type
		if (&quot;progress&quot;.equals(evt.getPropertyName())) {
			this.bar.setValue((int) evt.getNewValue());
		}
	}
}

public class PanelFoo extends JPanel {

	/**
	 * 
	 */
	private static final long serialVersionUID = -1400188281877395934L;
	private JLabel label;
	private JTextArea textArea;
	private JScrollPane scrollPanel;
	private JProgressBar progressBar;

	public PanelFoo(final List&lt;String&gt; listOfStates) {
		setLayout(new FlowLayout());
		setSize(180, 400);
		final ImageIcon icon = createImageIcon(&quot;/images/waiting-list.png&quot;, &quot;waiting start&quot;);
		this.label = new JLabel(listOfStates.get(0), icon, SwingConstants.HORIZONTAL);
		add(this.label);

		this.textArea = new JTextArea(&quot;Numbers: \n&quot;);
		this.textArea.setWrapStyleWord(true);
		this.scrollPanel = new JScrollPane(this.textArea);
		this.scrollPanel.setPreferredSize(new Dimension(150, 350));

		this.progressBar = new JProgressBar(0, 100);

		add(this.scrollPanel);
		add(this.progressBar);

		setVisible(true);
	}

	/** Returns an ImageIcon, or null if the path was invalid. */
	public ImageIcon createImageIcon(final String path, final String description) {
		if (path != null) {
			ImageIcon imageIcon = new ImageIcon(getClass().getResource(path));
			final Image image = imageIcon.getImage(); 
			final Image newimg = image.getScaledInstance(30, 30, Image.SCALE_SMOOTH);
			imageIcon = new ImageIcon(newimg, description);
			return imageIcon;

		} else {
			System.err.println(&quot;Couldn&#39;t find file: &quot; + path);
			return null;
		}
	}

	public final JLabel getLabel() {
		return this.label;
	}

	public final void setLabel(final JLabel label) {
		this.label = label;
	}

	public final JTextArea getTextArea() {
		return this.textArea;
	}

	public final void setTextArea(final JTextArea textArea) {
		this.textArea = textArea;
	}

	public final JProgressBar getProgressBar() {
		return this.progressBar;
	}

	public final void setProgressBar(final JProgressBar progressBar) {
		this.progressBar = progressBar;
	}

}

public class WorkerDoSomething extends SwingWorker&lt;Void, Void&gt; {

	private JTextArea txtArea;
	private JLabel label;
	private Random r = new Random();

	WorkerDoSomething() {
	}

	public WorkerDoSomething(final PanelFoo panelFooInstance) {
		this.txtArea = panelFooInstance.getTextArea();
		this.label = panelFooInstance.getLabel();
	}

	private Integer randomInt(final int min, final int max) {
		final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;

		return randomNumber;
	}

	@Override
	protected Void doInBackground() throws Exception {
		final Integer randomNumber = randomInt(10000000, 1000000000);
		long j;
		int progress = 0;
		final int onePerCent = randomNumber / 100;
		final int onePerMillion = onePerCent / 10;
		for (j = 0; j &lt;= randomNumber; j++) {
			if (j % onePerCent == 0) {
				progress = (int) j / onePerCent;
				setProgress(progress);
			}
			if (j % onePerMillion == 0) {
				publish(j);
			}
			//Thread.sleep(randomInt(1000, 5000));
		}
		

		return null;
	}

	private void publish(final long num) {
		this.txtArea.append(num + &quot;\n&quot;);
		this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
	}

}

This is the main GUI after all:

在使用SwingWorker的Java线程池内等待任务。

and this is the execution:

在使用SwingWorker的Java线程池内等待任务。

I just need to wait for a time on each WorkerDoSomething's task, adding that line (previously comment out): Thread.sleep(randomInt(1000, 5000));

but when I do.. the whole execution freezes, of course..because it uses a single thread to run all the tasks - I suppose.

在使用SwingWorker的Java线程池内等待任务。

Is there a solution for this?

Oh...I have to use java 1.8 in the business:

java version &quot;1.8.0_251&quot;
Java(TM) SE Runtime Environment (build 1.8.0_251-b08)
Java HotSpot(TM) 64-Bit Server VM (build 25.251-b08, mixed mode)

The whole project it's on my personnal git

--- first edit with debug perspective

在使用SwingWorker的Java线程池内等待任务。

答案1

得分: 2

执行不会冻结,因为Swing使用一个包含10个线程的线程池来运行工作线程。
如果您将以下部分注释掉,您会看到它正常工作:

if (j % onePerMillion == 0) {
    publish(j);
}

附注 - 为什么在actionPerformed方法中创建一个新的SwingWorker?
为什么不像这样简单地编写:

    this.btnStart.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
        w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
        final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
        w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
        final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
        w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
        final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
        w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));

        w1.execute();
        w2.execute();
        w3.execute();
        w4.execute();
      }
    });
英文:

The execution is not freezing since Swing uses a thread pool of 10 threads to run the workers.
You can see it work properly if you comment out this part:

if (j % onePerMillion == 0) {
    publish(j);
}

PS - Why do you create a new SwingWorker in the actionPerformed method?
Why not simply write like this:

    this.btnStart.addActionListener(new ActionListener() {
      @Override
      public void actionPerformed(final ActionEvent e) {
        final WorkerDoSomething w1 = new WorkerDoSomething(panelFooA);
        w1.addPropertyChangeListener(new ProgressListener(panelFooA.getProgressBar()));
        final WorkerDoSomething w2 = new WorkerDoSomething(panelFooB);
        w2.addPropertyChangeListener(new ProgressListener(panelFooB.getProgressBar()));
        final WorkerDoSomething w3 = new WorkerDoSomething(panelFooC);
        w3.addPropertyChangeListener(new ProgressListener(panelFooC.getProgressBar()));
        final WorkerDoSomething w4 = new WorkerDoSomething(panelFooD);
        w4.addPropertyChangeListener(new ProgressListener(panelFooD.getProgressBar()));

        w1.execute();
        w2.execute();
        w3.execute();
        w4.execute();
      }
    });

答案2

得分: 2

你是否阅读了关于类SwingWorker的_javadoc_?以下是其中的一部分内容:

类 SwingWorker<T,V>
类型参数:
T - 此 SwingWorker 的 doInBackground 和 get 方法返回的结果类型
V - 此 SwingWorker 的 publish 和 process 方法执行中间结果的类型

你在 doInBackground() 方法中正确地调用了 publish() 方法,但你没有重写 process() 方法。请参考 具有中间结果的任务。假设你的 publish() 方法实际上应该是 process() 方法,那么这应该是 WorkerDoSomething 类的声明

public class WorkerDoSomething extends SwingWorker&lt;Void, Long&gt; {

而这应该是 process() 方法(而不是 publish() 方法)

protected void process(List&lt;Long&gt; nums) {
    this.txtArea.append(nums.get(nums.size() - 1) + &quot;\n&quot;);
    this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
}

但是这样做并不能解决你的问题。我不能解释为什么,但如果你在 doInBackground() 方法中的 Thread.sleep() 调用之前立即调用 setProgress() 方法,问题就会得到解决。我从 这个 网页上得到了移动 sleep 调用位置的想法,标题是 Java Swing How to - Put lengthy task in SwingWorker

以下是你的应用程序的重写。我有自由地重写其中的部分内容,但基本上你只需要关注 WorkerDoSomething 类。

import ...
//(以下省略)
英文:

Did you read the javadoc for class SwingWorker? Here is an excerpt:

> Class SwingWorker<T,V>
Type Parameters:
T - the result type returned by this SwingWorker's doInBackground and get methods
V - the type used for carrying out intermediate results by this SwingWorker's publish and process methods

You correctly call method publish() in your doInBackground() method, but you have not overridden method process(). Refer to Tasks that Have Interim Results. Assuming that your publish() method is really supposed to be the process() method, then this should be the declaration of class WorkerDoSomething

public class WorkerDoSomething extends SwingWorker&lt;Void, Long&gt; {

And this should be method process() (instead of method publish())

protected void process(List&lt;Long&gt; nums) {
    this.txtArea.append(nums.get(nums.size() - 1) + &quot;\n&quot;);
    this.txtArea.setCaretPosition(this.txtArea.getDocument().getLength());
}

But doing that will not solve your problem. I can't explain why but the problem is resolved if you change the location of the call to Thread.sleep() in method doInBackground(). If I place the call to method sleep() immediately before the call to setProgress(), then the GUI does not "freeze". I got the idea to move the location of the call to sleep from this Web page, entitled Java Swing How to - Put lengthy task in SwingWorker

Here is my rewrite of your application. I took the liberty to rewrite parts of it, but essentially you just need to pay attention to the WorkerDoSomething class.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.util.List;
import java.util.Random;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingWorker;
import javax.swing.WindowConstants;

public class FormMain implements Runnable, ActionListener {
    private static final int  PANELS = 4;
    private static final String  START = &quot;Start&quot;;

    private JFrame  frame;
    private PanelFoo[]  fooPanels;

    public FormMain() {
        fooPanels = new PanelFoo[PANELS];
        for (int i = 0; i &lt; PANELS; i++) {
            fooPanels[i] = new PanelFoo();
        }
    }

    @Override // java.awt.event.ActionListener
    public void actionPerformed(ActionEvent event) {
        String actionCommand = event.getActionCommand();
        switch (actionCommand) {
            case START:
                start();
                break;
            default:
                JOptionPane.showMessageDialog(frame,
                                              actionCommand,
                                              &quot;Unhandled&quot;,
                                              JOptionPane.WARNING_MESSAGE);
        }
    }

    @Override // java.lang.Runnable
    public void run() {
        createGui();
    }

    private JButton createButton(String text, int mnemonic, String tooltip) {
        JButton button = new JButton(text);
        button.setMnemonic(mnemonic);
        button.setToolTipText(tooltip);
        button.addActionListener(this);
        return button;
    }

    private JPanel createButtonsPanel() {
        JPanel buttonsPanel = new JPanel();
        buttonsPanel.add(createButton(START, KeyEvent.VK_S, &quot;Start your engines!&quot;));
        return buttonsPanel;
    }

    private void createGui() {
        frame = new JFrame();
        frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
        frame.add(createMainPanel(), BorderLayout.CENTER);
        frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
        frame.pack();
        frame.setLocationByPlatform(true);
        frame.setVisible(true);
    }

    private JPanel createMainPanel() {
        JPanel mainPanel = new JPanel(new GridLayout(0, 4, 10, 10));
        for (PanelFoo pf : fooPanels) {
            mainPanel.add(pf);
        }
        return mainPanel;
    }

    private void start() {
        for (PanelFoo pf : fooPanels) {
            pf.start();
        }
    }

    public static void main(String[] args) {
        EventQueue.invokeLater(new FormMain());
    }
}

class PanelFoo extends JPanel implements PropertyChangeListener {
    private static final long serialVersionUID = 4029040375048696554L;

    private JProgressBar  progressBar;
    private JTextArea  textArea;

    public PanelFoo() {
        super(new BorderLayout());
        textArea = new JTextArea(20, 10);
        JScrollPane scrollPane = new JScrollPane(textArea);
        add(scrollPane, BorderLayout.CENTER);
        progressBar = new JProgressBar(0, 100);
        add(progressBar, BorderLayout.PAGE_END);
    }

    public void appendText(String text) {
        textArea.append(text);
        textArea.append(&quot;\n&quot;);
    }

    @Override // java.beans.PropertyChangeListener
    public void propertyChange(PropertyChangeEvent evt) {
        if (&quot;progress&quot;.equals(evt.getPropertyName())) {
            int progress = (Integer) evt.getNewValue();
            progressBar.setValue(progress);            
        }
    }

    public void start() {
        WorkerDoSomething worker = new WorkerDoSomething(this);
        worker.addPropertyChangeListener(this);
        worker.execute();
    }
}

class WorkerDoSomething extends SwingWorker&lt;Void, Long&gt; {
    private PanelFoo  fooPanel;
    private Random r = new Random();

    public WorkerDoSomething(PanelFoo pf) {
        fooPanel = pf;
    }

    @Override
    protected Void doInBackground() throws Exception {
        Integer randomNumber = randomInt(10000000, 1000000000);
        long j;
        int progress = 0;
        final int onePerCent = randomNumber / 100;
        final int onePerMillion = onePerCent / 10;
        for (j = 0; j &lt;= randomNumber; j++) {
            if (j % onePerCent == 0) {
                try {
                    Thread.sleep(1000);
                }
                catch (InterruptedException xInterrupted) {
                    // Ignore
                }
                progress = (int) j / onePerCent;
                setProgress(progress);
            }
            if (j % onePerMillion == 0) {
                publish(j);
            }
        }
        return null;
    }

    @Override
    protected void process(List&lt;Long&gt; numbers) {
        Long number = numbers.get(numbers.size() - 1);
        fooPanel.appendText(String.valueOf(number));
    }

    private Integer randomInt(final int min, final int max) {
        final Integer randomNumber = this.r.nextInt((max - min) + 1) + min;
        return randomNumber;
    }
}

I would say that technically, the above answers your question, which was:
> Is there a solution for this?

I believe I have provided a solution, even if I can't explain why it works.

huangapple
  • 本文由 发表于 2020年9月19日 22:56:18
  • 转载请务必保留本文链接:https://go.coder-hub.com/63970091.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定