我正在使用一个新的线程来搜索服务器,而不是在其他类如图形等方面造成任何滞后。我发现服务器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)
方法紧随其后,但我不确定。你能帮帮我这个吗?
答案 0 :(得分:2)
请注意,此实施未同步。如果多个线程同时访问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
所有这一切的关键如下:
void
),但在回调中,这里是worker的done()
方法只有在工人完成其操作后才会运行。