【问题标题】:How do I synchronize access to an array list accessed by multiple threads?如何同步访问由多个线程访问的数组列表?
【发布时间】:2011-11-09 20:53:59
【问题描述】:

我有一个 ArrayList,需要从我的 GUI 的多个组件中读取和写入。我已经大大减少了代码量,试图在下面这个简洁的代码段中说明问题。

父框架可能有许多内部框架,每个内部框架都需要它自己的此 ArrayList 实例。但是,特定内部框架的所有子组件都需要访问此 ArrayList 的同一实例,以便在特定内部框架的一个真正的 ArrayList 中维护添加和删除。对于此示例,ArrayList 中的所有数据都需要在内存中。不过,我稍后会添加代码以在每次内存发生更改时更新持久性数据文件。

这是我的简化代码段。谁能告诉我如何更改此代码,以便它为我提供我寻求的读/写访问权限?此外,任何相关文章的链接将不胜感激。

ParentFrame.java:

package testGlobalArrayList;

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;
import java.util.*;

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

public ParentFrame() {
    super("parent frame");
    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    this.setPreferredSize(new Dimension(600, 300));
    Panel p = new Panel();
    this.add(p, BorderLayout.SOUTH);
    desktop = new JDesktopPane();
    this.add(desktop, BorderLayout.CENTER);
    this.pack();
    this.setSize(new Dimension(600, 300));
    this.setLocationRelativeTo(null);
    final int DELTA = 40;
    int offset = DELTA;
        int ifWidth = 400;
        int ifHeight = 200;
        internalFrame = new JInternalFrame("internal frame", true, true, true, true);
        internalFrame.setLocation(offset, offset);
        offset += DELTA;

        JTabbedPane jtp = createTabbedPane();
        internalFrame.add(jtp);

        // want to make this ArrayList read/write accessible to every GUI component below this level            
        ArrayList<Integer> myArrayList= new ArrayList<Integer>();
        myArrayList.add(8);
        myArrayList.add(6);
        myArrayList.add(7);

        desktop.add(internalFrame);
        internalFrame.pack();
        internalFrame.setSize(new Dimension(ifWidth,ifHeight));
        internalFrame.setVisible(true);
    }
private JTabbedPane createTabbedPane() {
    JTabbedPane jtp = new JTabbedPane();
    jtp.setMinimumSize(new Dimension(600,300));
    createTab(jtp, "Tab1");
    createTab(jtp, "Tab2");
    return jtp;
    }
private void createTab(JTabbedPane jtp, String s) {
    if(s=="Tab1"){
        TestGUI myTimeSeriesGUI = new TestGUI();
        jtp.add(s,myTimeSeriesGUI);
    }
    else{jtp.add(s, new JLabel("TabbedPane " + s, JLabel.CENTER));}
    }
public static void main(String args[]) {
    ParentFrame myParentFrame = new ParentFrame();
    myParentFrame.setVisible(true);
    }
}

TestGUI.java:

package testGlobalArrayList;

import javax.swing.JPanel;
import java.awt.BorderLayout;
import java.util.ArrayList;
import javax.swing.Box;

public class TestGUI extends JPanel{

TestGUI(){
    Box verticalBox = Box.createVerticalBox();
    verticalBox.add(new TestPanel());
    verticalBox.add(new TestPanel());
    verticalBox.add(new TestPanel());
    this.add(verticalBox, BorderLayout.CENTER);
}
void anotherMethod(){
    // want to be able to add or delete records to same ArrayList here
    myArrayList.add(5);
    myArrayList.add(3);
    myArrayList.add(0);
    myArrayList.add(9);
}
}

TestPanel.java:

package testGlobalArrayList;

import java.awt.Color;
import java.util.Random;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;

public class TestPanel extends JPanel {
public TestPanel (){
    this.setBackground(getRandomColor());
    this.setBorder( new EtchedBorder() );
    this.setSize(150,20);
}
void anotherMethod(){
    //want to be able to add or delete records from same ArrayList here
    myArrayList.remove(1);
    myArrayList.remove(2);
    myArrayList.remove(3);
}
    private static Color getRandomColor(){
    Random rand = new Random();
    float r = rand.nextFloat();
    float g = rand.nextFloat();
    float b = rand.nextFloat();
    Color randomColor = new Color(r, g, b);
        return randomColor;
    }
}

【问题讨论】:

  • 不要if(s=="Tab1"),使用.equals()
  • @HannoBinder +1 它只能在这里正常工作,因为字符串文字是被实习的

标签: java arraylist add


【解决方案1】:

在构造函数中传递数组列表(并使其成为字段)

public class ParentFrame extends JFrame{
    private static final long serialVersionUID = 1L;
    JLayeredPane desktop;
    JInternalFrame internalFrame;
    private List<Integer> myArrayList;

    public ParentFrame() {
        super("parent frame");
        myArrayList= new ArrayList<Integer>();
        myArrayList.add(8);
        myArrayList.add(6);
        myArrayList.add(7);
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setPreferredSize(new Dimension(600, 300));
        Panel p = new Panel();
        this.add(p, BorderLayout.SOUTH);
        desktop = new JDesktopPane();
        this.add(desktop, BorderLayout.CENTER);
        this.pack();
        this.setSize(new Dimension(600, 300));
        this.setLocationRelativeTo(null);
        final int DELTA = 40;
        int offset = DELTA;
        int ifWidth = 400;
        int ifHeight = 200;
        internalFrame = new JInternalFrame("internal frame", true, true, true, true);
        internalFrame.setLocation(offset, offset);
        offset += DELTA;

        JTabbedPane jtp = createTabbedPane();
        internalFrame.add(jtp);




        desktop.add(internalFrame);
        internalFrame.pack();
        internalFrame.setSize(new Dimension(ifWidth,ifHeight));
        internalFrame.setVisible(true);
    }


    private void createTab(JTabbedPane jtp, String s) {
        if(s=="Tab1"){
            TestGUI myTimeSeriesGUI = new TestGUI(myArrayList);
            jtp.add(s,myTimeSeriesGUI);
        }
        else{jtp.add(s, new JLabel("TabbedPane " + s, JLabel.CENTER));}
    }
}

TestGui 变成

public class TestGUI extends JPanel{
private List<Integer> myArrayList;

TestGUI(List<Integer> myArrayList){
    Box verticalBox = Box.createVerticalBox();
    verticalBox.add(new TestPanel(myArrayList));
    verticalBox.add(new TestPanel(myArrayList));
    verticalBox.add(new TestPanel(myArrayList));
    this.add(verticalBox, BorderLayout.CENTER);
    this.myArrayList = myArrayList;
}
void anotherMethod(){
    // want to be able to add or delete records to same ArrayList here
    myArrayList.add(5);
    myArrayList.add(3);
    myArrayList.add(0);
    myArrayList.add(9);
}

和测试面板

public class TestPanel extends JPanel {
private ArrayList<Integer> myArrayList;
public TestPanel (ArrayList<Integer> myArrayList){
    this.setBackground(getRandomColor());
    this.setBorder( new EtchedBorder() );
    this.setSize(150,20);
    this.myArrayList = myArrayList;
}
void anotherMethod(){
    //want to be able to add or delete records from same ArrayList here
    myArrayList.remove(1);
    myArrayList.remove(2);
    myArrayList.remove(3);
}

【讨论】:

  • +1 给我一个例子。我想我已经在我的实际应用程序中这样做了,但是你的代码帮助我理解了。在回复这篇文章之前,我将稍微处理一下我的代码。
【解决方案2】:

您需要使用控制器来维护这些资源。对于 Java UI,您可以随时使用通过单例方法访问的控制器。这应该充当您应用的交通警察。

【讨论】:

  • 谢谢。你介意告诉我如何修改我的代码来完成这个吗?我发布这个是因为我已经研究了一段时间,我需要更具体的东西。文章链接也将不胜感激。
  • 如果您想要每篇文章 1,000 篇,请查看 MVC 和单例。 MVC 是一个好主意,但实现起来总是很麻烦,因为当您考虑它时,用户界面就是违反封装的完美示例,因为它的全部目标是向世界展示模型的内部结构.所以我会在你的位置上做的是使用你的工厂方法主要实例化控制器,然后让控件为你构建你的屏幕。任何超越屏幕本身的状态信息都应该只传递给控制器​​,控制器会派发数据
  • 对于单例,假设您有一个名为 MainController 的类。在里面你会有一个变量 static MainController myController;现在您编写一个工厂方法,例如: static public synchronized MainController getInstance(){ if(myController==null){ myController = new MainController(); } 返回我的控制器;然后你将你的构造函数设为私有,这样只有你可以构造它。
【解决方案3】:

您需要将对全局列表(或封装此全局列表的对象)的引用传递给每个组件。在构造函数中传递它是最简单的方法:

ParentFrame.java:

    List<Integer> myArrayList= new ArrayList<Integer>();
    myArrayList.add(8);
    myArrayList.add(6);
    myArrayList.add(7);

    JTabbedPane jtp = createTabbedPane(myArrayList);
    internalFrame.add(jtp);

...

private JTabbedPane createTabbedPane(List<Integer> list) {
    JTabbedPane jtp = new JTabbedPane();
    jtp.setMinimumSize(new Dimension(600,300));
    createTab(jtp, "Tab1", list);
    createTab(jtp, "Tab2", list);
    return jtp;
}
private void createTab(JTabbedPane jtp, String s, List<Integer> list) {
    if ("Tab1".equals(s)){
        TestGUI myTimeSeriesGUI = new TestGUI(list);
        ...

TestGUI.java:

private List<Integer> list;
TestGUI(List<Integer> list) {
    this.list = list;
    ...

【讨论】:

  • 谢谢。但是你能用我的代码给我一个例子吗?或者也许是文章的链接。我很难确切地知道你的意思。
  • +1。谢谢你。在进一步回复这篇文章之前,我将检查我的实际代码一段时间。
【解决方案4】:

您可以使用服务定位器模式(这也可以帮助您解决持久化到磁盘的要求)。

有点像

public ServiceLocator() {
  private static ArrayHandler handler=new ArrayHandler();

  public static IArrayHandler getArrayHandler() {
    return handler;
  }

}

然后您可以创建 ArrayHandler 来处理创建和跟踪您的数组。因此,在您的代码中,您可以使用以下内容获取数组:

ServiceLocator.getArrayHandler().getArray("myarray");

这样,当您需要“不再有变量”时,您无需传递参数并更改方法或构造函数签名。无论如何,这是我喜欢并且感觉舒适的模式。 YMMV。干杯。

【讨论】:

    猜你喜欢
    • 2020-05-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多