企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
演示Swing与线程一起使用存在的问题。不断点击Bad按钮会抛出异常(没有使用事件分配线程),但不断点击Good按钮不会抛出异常(使用了事件分配线程)。 ```java import java.awt.EventQueue; import java.util.Random; import javax.swing.*; /** * 该程序演示了与事件并行运行的线程 * * @author Cay Horstmann * @version 1.24 2015-06-21 */ public class SwingThreadTest { public static void main(String[] args) { EventQueue.invokeLater(() -> { JFrame frame = new SwingThreadFrame(); frame.setTitle("SwingThreadTest"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.setVisible(true); }); } } /** * 该框架有两个按钮,用于填充来自单独线程的组合框。 * “Good”按钮使用事件队列,“Bad”按钮直接修改组合框 * * @author Administrator */ class SwingThreadFrame extends JFrame { public SwingThreadFrame() { final JComboBox<Integer> combo = new JComboBox<>(); combo.insertItemAt(Integer.MAX_VALUE, 0); combo.setPrototypeDisplayValue(combo.getItemAt(0)); combo.setSelectedIndex(0); JPanel panel = new JPanel(); JButton goodButton = new JButton("Good"); goodButton.addActionListener(even -> new Thread(new GoodWorkerRunnable(combo)).start()); panel.add(goodButton); JButton badButton = new JButton("Bad"); badButton.addActionListener(event -> new Thread(new BadWorkerRunnable(combo)).start()); panel.add(badButton); panel.add(combo); add(panel); pack(); } } /** * 此可运行项通过随机添加和删除数字来修改组合框。 * 这可能会导致错误,因为组合框方法不同步并且工作线程和事件分发线程都访问了组合框。 */ class BadWorkerRunnable implements Runnable { private JComboBox<Integer> combo; private Random generator; public BadWorkerRunnable(JComboBox<Integer> aCombo) { combo = aCombo; generator = new Random(); } @Override public void run() { try { while (true) { int i = Math.abs(generator.nextInt()); if (i % 2 == 0) { //在非事件分配线程中接触Swing组件是会出问题的 combo.insertItemAt(i, 0); } else if (combo.getItemCount() > 0) { combo.removeItemAt(i % combo.getItemCount()); } Thread.sleep(1); } } catch (InterruptedException e) { } } } /** * 此可运行项通过随机添加和删除数字来修改组合框。 * 为了确保组合框未损坏,编辑操作将转发到事件分发线程。 * * @author Administrator */ class GoodWorkerRunnable implements Runnable { private JComboBox<Integer> combo; private Random generator; public GoodWorkerRunnable(JComboBox<Integer> aCombo) { combo = aCombo; generator = new Random(); } @Override public void run() { try { while (true) { //事件分配线程 EventQueue.invokeLater(() -> { int i = Math.abs(generator.nextInt()); if (i % 2 == 0) { //只能在事件分配线程中接触Swing组件 combo.insertItemAt(i, 0); } else if (combo.getItemCount() > 0) { combo.removeItemAt(i % combo.getItemCount()); } }); Thread.sleep(1); } } catch (InterruptedException e) { } } } ``` 不断点击Bad按钮抛出异常的原因:当把一个元素插入组合框时,组合框将产生一个事件来更新显示。然后,显示代码开始运行,读取组合框当前的值并准备显示这个值。但是,工作器线程依然保持运行,有时候会造成组合框的值数目减少。但是显示代码依然认为组合框的值数目比实际的多,于是会访问不存在的值,触发ArrayIndexOutOfBounds异常。 <br/> 针对上面的这种情况,可以采用给组合框加锁来避免。但是,这样消耗更多的时间,而且程序员常被同步命令搞昏了头,经常编写出容易造成死锁的程序。所以这种方案并不可取。