传递数组作为线程安全的参数时出现ActionListener错误

时间:2011-11-23 00:52:57

标签: java arrays swing thread-safety actionlistener

我正在重写一些代码以提高线程安全性。我试图将数组作为参数传递,而不是将它们存储在类变量中。但是我的gui中的按钮需要更新/重新加载gui中面板的内容时会抛出错误。

看起来我需要以某种方式将这些数组传递到按钮或ActionListener,但我不知道该怎么做。由于问题隐藏在成千上万行无关代码中,因此我在下面的代码示例中重新创建了相关方面。下面的代码唯一的另一个问题是,由于某种原因,它不会使按钮可见。但是,否则,下面的代码会准确地重新创建我的应用程序中抛出的错误。请注意,当窗格重新加载两个数组中的数据时,代码会打印一条消息。

有人可以告诉我如何更改下面的代码,以便在单击按钮刷新面板内容时能够将数组作为参数传递吗?

代码分为三个文件:

Parent.java

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Panel;
import javax.swing.JDesktopPane;
import javax.swing.JFrame;
import javax.swing.JInternalFrame;
import javax.swing.JLabel;
import javax.swing.JLayeredPane;
import javax.swing.JTabbedPane;

public class Parent extends JFrame{
private static final long serialVersionUID = 1L;
JLayeredPane desktop;
JInternalFrame internalFrame;

public Parent() {
    super("title goes here");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setPreferredSize(new Dimension(800, 400));
    double[] myDBL = {2.3,5.4,6.5,7.8,2.4,6.4,9.0,5.3,8.1};
    String[] mySTR = {"ko","lp","dk"};
    Panel p = new Panel();
    this.add(p, BorderLayout.SOUTH);
    desktop = new JDesktopPane();
    this.add(desktop, BorderLayout.CENTER);
    this.pack();
    this.setSize(new Dimension(800, 600));
    this.setLocationRelativeTo(null);
        int ifWidth = 600;
        int ifHeight = 300;
        internalFrame = new JInternalFrame("title", true, true, true, true);
        // create jtabbed pane
        JTabbedPane jtp = createTabbedPane(myDBL,mySTR);
        internalFrame.add(jtp);
        desktop.add(internalFrame);
        internalFrame.pack();
        internalFrame.setSize(new Dimension(ifWidth,ifHeight));
        internalFrame.setVisible(true);
}
private JTabbedPane createTabbedPane(double[] myDBL, String[] mySTR) {
    JTabbedPane jtp = new JTabbedPane();
    jtp.setMinimumSize(new Dimension(600,300));
    createTab(jtp, "Data",myDBL,mySTR);
    return jtp;
}
private void createTab(JTabbedPane jtp, String s,double[] myDBL, String[] mySTR) {
    if(s=="Data"){
        PanelGUI myTimeSeriesGUI = new PanelGUI(myDBL,mySTR);
        jtp.add(s,myTimeSeriesGUI);
    }
    else{jtp.add(s, new JLabel("TabbedPane " + s, JLabel.CENTER));}
}
public static void main(String args[]) {
    Parent myParentFrame = new Parent();
    myParentFrame.setVisible(true);
}
}

PanelGUI.java (这个文件包含了在ECLIPSE中发生错误消息的行。)

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class PanelGUI extends JPanel implements ActionListener{
private static final long serialVersionUID = 1L;
public int visiblePoints;
public int newStartingPoint;
ToolBar myToolBar;
int frameWidth = 600;
int frameHeight = 300;

public PanelGUI(double[] myDBL, String[] mySTR){
    newStartingPoint = 0;
    visiblePoints = 5000;
    addComponentsToPane(this, myDBL,mySTR);
}
public void addComponentsToPane(Container pane, double[] myDBL, String[] mySTR) {
    pane.removeAll();
    myToolBar=new ToolBar(myDBL,mySTR);
    pane.add(myToolBar);
    myToolBar.hRescaleButton.addActionListener(this);
    System.out.println("pane reloaded successfully");
}
public void actionPerformed(ActionEvent ae) {
    if(ae.getSource()==myToolBar.hRescaleButton){
        String str = JOptionPane.showInputDialog(null, "Number of milliseconds shown in window : ", "Horizontal Rescale", 1);
        if(str != null) {
            int numPoints = Integer.parseInt(str);
            JOptionPane.showMessageDialog(null, "You entered: "+numPoints+"ms = "+(numPoints/1000)+"sec.", "Horizontal Rescale", 1);
            this.removeAll();
            visiblePoints = numPoints;
            frameWidth = this.getWidth();
            frameHeight = this.getHeight();
            setSize(frameWidth,frameHeight);
            addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
            setSize(frameWidth,frameHeight);
        }
        else{JOptionPane.showMessageDialog(null, "You pressed cancel button.","Horizontal Rescale", 1);}
    }
}
}

ToolBar.java

import javax.swing.JToolBar;
import javax.swing.JButton;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.awt.Dimension;

public class ToolBar extends JPanel {
private static final long serialVersionUID = -2749251105543480474L;
static final private String RESET_HSCALE = "HorizontalRescale";
JButton hRescaleButton;

public ToolBar(double[] myDBL, String[] mySTR) {
    //Create the toolbar.
    JToolBar toolBar = new JToolBar();
    addButtons(toolBar);
    toolBar.setFloatable(false);
    toolBar.setRollover(true);
    //Lay out the main panel.
    setPreferredSize(new Dimension(this.getWidth(), 40));
    add(toolBar, BorderLayout.PAGE_START);
}
protected void addButtons(JToolBar toolBar) {
    hRescaleButton = new JButton("Reset Horizontal Scale");
    hRescaleButton.setActionCommand(RESET_HSCALE);
    hRescaleButton.setToolTipText("Reset horizontal scale.");
    toolBar.add(hRescaleButton);
}
}

3 个答案:

答案 0 :(得分:1)

很难理解您尝试使用此代码实现的目标。正如您在编译错误之前所指出的那样,是因为您正在使用不在范围内的变量。

至于为你提供动作监听器对变量的可见性,你有唯一的选择,就是在JPanel中创建实例变量。

但是,我认为没有JPanel实现actionListener。而是将hRescaleButton的侦听器创建为匿名内部类。这样可以更好地封装您的代码,您无需在处理程序中检查事件的来源。

请参阅下面的代码。

通过在addComponentsToPane方法final上声明参数,您的匿名内部类动作侦听器将能够访问它们。

import java.awt.Container;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

public class PanelGUI extends JPanel {
  private static final long serialVersionUID = 1L;
  public int visiblePoints;
  public int newStartingPoint;
  ToolBar myToolBar;
  int frameWidth = 600;
  int frameHeight = 300;

public PanelGUI(double[] myDBL, String[] mySTR){
    newStartingPoint = 0;
    visiblePoints = 5000;
    addComponentsToPane(this, myDBL,mySTR);
}

public void addComponentsToPane(Container pane, final double[] myDBL, final String[] mySTR) {
    pane.removeAll();
    myToolBar=new ToolBar(myDBL,mySTR);
    pane.add(myToolBar);
    myToolBar.hRescaleButton.addActionListener(new ActionListener(){
        public void actionPerformed(ActionEvent ae) {

            String str = JOptionPane.showInputDialog(null, "Number of milliseconds shown in window : ", "Horizontal Rescale", 1);
            if(str != null) {
                int numPoints = Integer.parseInt(str);
                JOptionPane.showMessageDialog(null, "You entered: "+numPoints+"ms = "+(numPoints/1000)+"sec.", "Horizontal Rescale", 1);
                this.removeAll();
                visiblePoints = numPoints;
                frameWidth = this.getWidth();
                frameHeight = this.getHeight();
                setSize(frameWidth,frameHeight);
                addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
                setSize(frameWidth,frameHeight);
              }
              else{
                  JOptionPane.showMessageDialog(null, "You pressed cancel button.","Horizontal Rescale", 1);
              }
      }});
      System.out.println("pane reloaded successfully");
}
}

话虽如此,为什么你假设将数组作为变量传递而不是存储它们作为实例变量将做什么来改善线程安全?它不会。如果要确保线程安全,则需要确保无法同时访问和修改阵列。当您作为参数传递时,您只复制对象引用,不会复制实际数组,因此代码不再是线程安全的。

如果数组在传入后需要进行修改,则需要在访问数组的任何位置使用公共锁同步对数组的访问。但是,我会考虑复制数组,以便您拥有自己的副本,而这些副本是您不知道的。那你就不需要任何同步了。

答案 1 :(得分:0)

首先分析显示以下问题:

您的错误是

$> javac Parent.java
.\PanelGUI.java:38: cannot find symbol
symbol  : variable myDBL
location: class PanelGUI
            addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
                                     ^
.\PanelGUI.java:38: cannot find symbol
symbol  : variable mySTR
location: class PanelGUI
            addComponentsToPane(this,myDBL,mySTR);//THIS IS WHERE THE ERROR IS
                                           ^
2 errors

这些变量myDBL和mySTR似乎不是你对象的一部分。

我注意到你将它们作为构造函数参数传递给工具栏,但实际上你已经将它们保存在工具栏类中,所以你不能让它们退出。

我认为你需要退后一步,确定你的目标是什么,然后从那里开始。

答案 2 :(得分:0)

您似乎错过了revalidate,这会导致添加的组件无法显示。糟糕的API设计,但确实如此。请参阅Container.add的API文档。

此外,您还应添加标准样板,以便从main开始Swing前端:

public static void main(final String[] args) {
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            runEDT();
        }
    });
}