ArrayList在Thread中不起作用

时间:2017-11-18 17:10:53

标签: java multithreading swing arraylist

我正在使用一个新的线程来搜索服务器,而不是在其他类如图形等方面造成任何滞后。我发现服务器ArrayList的.add()方法不起作用,即使servers.size()在新线程中也不起作用。你知道发生了什么以及如何解决这个问题吗?

ArrayList<String> findServers(int howMany){
    ArrayList<String> servers = new ArrayList<>();
    Main.chatGraphics.log("<font face='arial' color='yellow'>Searching for servers...</font>");
    Main.chatGraphics.msgInputTF.setText("Wait...");
    Main.chatGraphics.msgInputTF.setEnabled(false);
    new Thread(() -> {
        Socket newSocket;
        for (int i = 2; i < 254; i++){
            if (servers.size() >= howMany)
                break;
            try {
                newSocket = new Socket();
                InetSocketAddress isa = new InetSocketAddress("192.168.1." + i, Main.DEFAULT_PORT);
                if (isa.isUnresolved())
                    continue;
                newSocket.connect(isa, 10);
                servers.add(newSocket.getInetAddress().getHostAddress()); // DOESN'T WORK <<
            } catch (Exception e) {
                e.getStackTrace();
            }
        }
        if (servers.size() == 0) // DOESN'T WORK TOO <<
            Main.chatGraphics.log("<font face='arial' color='red'>No available servers</font>");

        Main.chatGraphics.msgInputTF.setEnabled(true);
        Main.chatGraphics.msgInputTF.setText("");
        Main.chatGraphics.msgInputTF.grabFocus();
    }).start();

    return servers;
}

另外,我对此代码有另一个问题:Main.chatGraphics.msgInputTF.setText("Wait...")不起作用。 setText方法仅在此方法中不起作用。我认为这是因为setEnabled(false)方法紧随其后,但我不确定。你能帮帮我这个吗?

2 个答案:

答案 0 :(得分:2)

来自Javadoc of ArrayList

  

请注意,此实施未同步。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。 (结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改。)这通常通过同步一些自然封装的对象来实现。名单。如果不存在此类对象,则应使用Collections.synchronizedList方法“包装”该列表。这最好在创建时完成,以防止意外地不同步访问列表:

List list = Collections.synchronizedList(new ArrayList(...));

您有多个线程同时访问此列表:调用方法的线程和新创建的线程。新创建的线程进行结构修改(它添加到列表中)。因此,您需要外部同步。

ArrayList换成synchronizedList

答案 1 :(得分:1)

根据您发布的代码,您的List<String>甚至不应该暴露给多个线程,而应该是工作线程本身的本地线程,并且只有在工作线程完成其工作后才会使用。我建议您使用SwingWorker<List<String>, Void> - 一个工作线程,一旦完成其工作,将在回调中返回String列表。所有后台工作都在worker的doInBackground()方法中完成,并且此方法中不应存在Swing代码。在Swing事件线程上调用worker的done()方法,因此Swing代码可以并且应该存在于此处。这些方面的东西可以工作,但如果它们没有,那么你可能在其他代码中没有显示的问题,并且需要进行进一步的调试(代码未编译或测试):

void findServers(int howMany) {
    // code run on the Swing event thread
    Main.chatGraphics.log("<font face='arial' color='yellow'>Searching for servers...</font>");
    Main.chatGraphics.msgInputTF.setText("Wait...");
    Main.chatGraphics.msgInputTF.setEnabled(false);

    // code run in background thread, that returns our List of interest
    new SwingWorker<List<String>, Void>() {

        @Override
        public List<String> doInBackground() throws Exception {

            // the List should be declared local within the worker
            List<String> servers = new ArrayList<>();
            Socket newSocket;
            for (int i = 2; i < 254; i++) {
                if (servers.size() >= howMany) {
                    break;
                }

                // don't catch exceptions wihin the worker. Do this in the
                // done() method.
                newSocket = new Socket();
                InetSocketAddress isa = new InetSocketAddress("192.168.1." + i,
                        Main.DEFAULT_PORT);
                if (isa.isUnresolved())
                    continue;
                newSocket.connect(isa, 10);
                servers.add(newSocket.getInetAddress().getHostAddress());
            }
            return servers;
        }

        @Override
        public void done() {
            try {
                // call the worker's get() method to retrieve the List
                // and to capture any exceptions
                List<String> servers = get();
                if (servers.size() == 0) {
                    Main.chatGraphics.log("<font face='arial' color='red'>No available servers</font>");
                }

                Main.chatGraphics.msgInputTF.setEnabled(true);
                Main.chatGraphics.msgInputTF.setText("");
                Main.chatGraphics.msgInputTF.grabFocus();


                // *** use servers in the GUI **here**


            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();

                // you will need more robust exception handling here
                // including extracting from this the true
                // underlying exception that was called
            }
        }
    }.execute();
}

有关详情,请阅读:Lesson: Concurrency in Swing

所有这一切的关键如下:

  • List再次是工作线程的本地
  • 你得到List并开始使用它不是从方法直接返回(注意我将findServers声明为void),但在回调中,这里是worker的done()方法只有在工人完成其操作后才会运行。
  • 再次注意避免从后台线程进行Swing调用。