通过不同的线程访问变量和摆动组件

这个问题与我在这里问的问题有些关系。 现在,我有一个类“Controller”,它由main方法和所有swing组件组成。 有一个名为“VTOL”的类,它由一个名为“altitude”的变量组成(我现在已声明此变量为volatile)。

这是一个由在后台运行的线程组成的类:

import java.util.logging.Level; import java.util.logging.Logger; /** * * @author Vineet */ public class Gravity extends Thread { String altStr; double alt; Controller ctrl = new Controller(); @Override public void run() { while (true) { alt=VTOL.altitude; System.out.println(alt); alt = alt-0.01; VTOL.altitude= (int) alt; altStr=new Integer(VTOL.altitude).toString(); ctrl.lblAltitude.setText(altStr); try { Thread.sleep(10); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } } 

首先,我最初面临的问题是我无法更新“高度”的值,它在整个程序执行过程中保持为0。 所以我宣称它是不稳定的(我不知道它是否是一个好习惯)

其次,在Controller类中有一个名为“lblAltitude”的jLabel,我希望在这个post中更新它的值,但不知怎的,这不会发生。 我怎样才能做到这一点?

解决方案是使用SwingPropertyChangeSupport对象,使高度成为此支持对象的“绑定”属性,以使您的GUI监听器到此模型类,从而通知GUI高度变化。

例如,

 import java.beans.PropertyChangeListener; import javax.swing.event.SwingPropertyChangeSupport; public class Gravity implements Runnable { public static final String ALTITUDE = "altitude"; private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this); private volatile double altitude; @Override public void run() { while (true) { double temp = altitude + 10; setAltitude(temp); // fires the listeners try { Thread.sleep(10); } catch (InterruptedException e) { e.printStackTrace(); } } } public double getAltitude() { return altitude; } public void setAltitude(double altitude) { Double oldValue = this.altitude; Double newValue = altitude; this.altitude = newValue; // this will be fired on the EDT since it is a SwingPropertyChangeSupport object swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue); } public void addPropertyChangeListener(PropertyChangeListener listener) { swingPcSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { swingPcSupport.removePropertyChangeListener(listener); } } 

有关更完整的可运行示例:

 import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import javax.swing.*; import javax.swing.event.SwingPropertyChangeSupport; public class GravityTestGui extends JPanel { private static final long ALT_SLEEP_TIME = 400; private static final double ALT_DELTA = 5; JLabel altitudeLabel = new JLabel(" "); private Gravity gravity = new Gravity(ALT_SLEEP_TIME, ALT_DELTA); public GravityTestGui() { add(new JLabel("Altitude:")); add(altitudeLabel); gravity.addPropertyChangeListener(new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent pcEvt) { if (Gravity.ALTITUDE.equals(pcEvt.getPropertyName())) { String altText = String.valueOf(gravity.getAltitude()); altitudeLabel.setText(altText); } } }); new Thread(gravity).start(); } private static void createAndShowGui() { GravityTestGui mainPanel = new GravityTestGui(); JFrame frame = new JFrame("GravityTest"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.getContentPane().add(mainPanel); frame.pack(); frame.setLocationByPlatform(true); frame.setVisible(true); } public static void main(String[] args) { SwingUtilities.invokeLater(new Runnable() { public void run() { createAndShowGui(); } }); } } class Gravity implements Runnable { public static final String ALTITUDE = "altitude"; private SwingPropertyChangeSupport swingPcSupport = new SwingPropertyChangeSupport(this); private volatile double altitude; private long sleepTime; private double delta; public Gravity(long sleepTime, double delta) { this.sleepTime = sleepTime; this.delta = delta; } @Override public void run() { while (true) { double temp = altitude + delta; setAltitude(temp); // fires the listeners try { Thread.sleep(sleepTime); } catch (InterruptedException e) { e.printStackTrace(); } } } public double getAltitude() { return altitude; } public void setAltitude(double altitude) { Double oldValue = this.altitude; Double newValue = altitude; this.altitude = newValue; // this will be fired on the EDT since it is a SwingPropertyChangeSupport object swingPcSupport.firePropertyChange(ALTITUDE, oldValue, newValue); } public void addPropertyChangeListener(PropertyChangeListener listener) { swingPcSupport.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { swingPcSupport.removePropertyChangeListener(listener); } } 

无论何时修改Swing组件,都需要确保在Event Dispatch Thread(即EDT)中发生此事件。

第三种方法是让您的Swing组件了解模型VTOL。

在Gravity中,您将更新VTOL.altitude,然后在组件上调用repaint。 例如

 while (true) { VTOL.altitude -= 0.01; VTOL.makeAnyOtherChangesHereAsWell(); controller.repaint(); // sleep, break etc. left as an exercise for the reader } 

然后,在paintComponent()方法中(或者可能在所有绘制调用中的其他地方,它有一点点机会它需要在其他地方…),你知道它在EDT上运行

 // update my widgets from the VTOL model - may want this in a method String altStr=new Integer(VTOL.altitude).toString(); this.lblAltitude.setText(altStr); // may be more, eg ... this.lblFuelSupply.setText(VTOL.getFuelSupply()); super.paintComponent(); // now go draw stuff... 

这比SwingPropertyChangeSupport更紧密耦合,但耦合是在非常相关的类之间进行的,因此它是“合理的”,并且在某些方面这可能更“清晰”。 并且事件调度队列将结合多个重新绘制,因此这不像它首次出现时那样低效。 如果多个线程正在更新内容并排队多个重绘(),则只有最后一个重绘()实际上做了任何事情。

缺点是,如果您的GUI具有大量的小部件,并且每次更新所有这些小部件都会变得有点慢。 但是现在处理器的速度非常快。