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

发布于 2024-12-17 13:27:27 字数 5119 浏览 0 评论 0原文

我正在重写一些代码以提高线程安全性。我试图将数组作为参数传递,而不是将它们存储在类变量中。但是,当我的 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);
}
}

I am re-writing some code to improve thread safety. I am trying to pass arrays as arguments rather than storing them in class variables. But an error is being thrown where a button in my gui needs to update/reload the contents of a panel in my gui.

It looks like I need to somehow pass these arrays into the button or into ActionListener, but I do not know how to do that. Since the problem is buried in many thousands of lines of irrelevant code, I have re-created the relevant aspects in the code sample below. The only other problem with the code below is that for some reason it it not making the button visible. But otherwise, the code below accurately recreates the error that is being thrown in my application. Note that the code prints a message when the pane is reloaded with data in the two arrays.

Can anyone show me how to change the code below so that it is able to pass the arrays as arguments when the button is clicked to refresh the panel's contents?

The code is in three files as follows:

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 (THIS FILE CONTAINS THE LINE THAT THROWS THE ERROR MESSAGE IN 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);
}
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

[浮城] 2024-12-24 13:27:27

很难理解你想用这段代码实现什么目的。正如之前指出的,您收到的编译错误是因为您使用的变量不在范围内。

至于以您布置事物的方式为您提供操作侦听器对变量的可见性,您唯一的选择是将它们设置为 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");
}
}

话虽如此,为什么您认为将数组作为变量传递而不是将它们存储为实例变量将有助于提高线程安全性?不会的。如果要确保线程安全,则需要确保不能同时访问和修改数组。当您作为参数传递时,您仅复制对象引用,而不会复制实际的数组,因此代码不再是线程安全的。

如果数组在传入后需要修改,那么您需要在访问数组的各处使用公共锁来同步对数组的访问。但是,我会考虑复制该数组,以便您拥有自己的副本,并且您知道该副本不会被修改。那么你就不需要任何同步。

Its difficult to understand what your trying to achieve with this code. As pointed out before the compile error you are getting is because you are using a variable that isn't in scope.

As far as giving you action listener visibility to the variables with the way you have things laid out your only option is to make them instance variables in the JPanel.

However, I would consider not having JPanel implement actionListener. Instead create the listener for hRescaleButton as an anonymous inner class. This would better encapsulate your code and you wouldn't need to check the source of the event in the handler.

See the code below.

By declaring the parameters on the addComponentsToPane method final then your anonymous inner class action listener will be able to access them.

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");
}
}

With that said, Why do you assume passing the arrays as variables instead of storing them as instance variables will do anything to improve Thread Safety? It won't. If you want to ensure Thread Safety you need to ensure the arrays can't be accessed and modified concurrently. When you pass as an argument you are only copying object references the actual array isn't copied so the code isn't anymore thread safe.

If the arrays need to be modified after they are passed in then you need to synchronize access to the array using a common lock everywhere the array is accessed. However, I'd consider copying the array so you have your own copy that you know isn't modified. Then you wouldn't need any synchronization.

╭⌒浅淡时光〆 2024-12-24 13:27:27

首先分析显示以下问题:

您的错误是

gt; 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 似乎不是您对象的一部分。

我注意到您将它们作为构造函数参数传递给 Toolbar,但实际上您将它们保存在 Toolbar 类中,因此您永远无法将它们取回。

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

First analysis shows the following problems:

Your error is

gt; 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

And those variables, myDBL and mySTR don't seem to be part of your object.

I notice that you pass them in as constructor parameters to Toolbar, but you ever actually save them in your Toolbar class, so you can't ever get them back out.

I think you need to take a step back, identify what your objectives are, and go from there.

我不是你的备胎 2024-12-24 13:27:27

看起来您缺少revalidate,这将导致添加的组件不会出现。 API 设计很糟糕,但确实如此。请参阅 API 文档 容器.添加

另外,您还应该添加标准样板以从 main 启动 Swing 前端:

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

Looks like you are missing a revalidate, that will cause the added components to not appear. Bad API design, but true. See the API docs for Container.add.

Also you should add the standard boilerplate for starting a Swing front end from main:

public static void main(final String[] args) {
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            runEDT();
        }
    });
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文