我有以下代码从套接字读取字符串并处理它。从套接字读取字符串后,我正在创建一个新线程来处理请求。这样,我可以让多个线程同时处理传入的请求。
public void process(Socket socket) throws Exception {
while (true) {
String request = readFromSocket(socket);
new Thread(() -> {
try {
System.out.println(request); // [edit]: I had omitted this line before as I thought it would be irrelevant
response = processRequest(request); // <-- request could get modified
} catch (Exception e) {
// TODO: Log error and print stack-trace
}
}).start();
}
}
这里的问题是,&#34;请求&#34;可以在新线程开始处理之前修改。我是多线程的新手,我阅读了一些涵盖基础知识的网站。我仍然不确定如何在这里解决问题。
使用synchronized来等待新线程完成处理请求将会破坏多线程的目的。一旦我收到请求,我想将它发送到一个新线程,以便它可以开始处理请求,而父线程可以读取下一个请求。
有关如何解决此问题的任何帮助?谢谢!
[在Luke的回答后编辑]
在我的场景中,请求将是文件名。您可以根据目录的大小获得多个请求。当我在一个文件较少的目录上尝试我的代码时,一切都很好。但是当我在一个包含数千个文件的目录上运行我的代码时,事情变得很奇怪。
在readFromSocket之后说,Thread-1有请求X并且Thread-2有请求Y.但是在processRequest()中,Thread-1开始处理请求Y.
答案 0 :(得分:3)
这里的问题是,&#34;请求&#34;可以在新线程开始处理之前修改。
Java语言不允许这样做。
在新线程处理之前,您不必担心request
被修改。因为Thread.start()
在request
变量的赋值与新线程中的processRequest(request)
方法调用之间建立了发生之前关系。
当一个语句调用Thread.start时,与该语句具有before-before关系的每个语句也与新线程执行的每个语句都有一个before-before关系。新线程可以看到导致创建新线程的代码的影响。
所以这个语句String request = readFromSocket(socket);
发生在new Thread(...).start();
之前以及新线程执行的任何代码。
同样String
是不可变的,所以一旦创建它就不会改变。你很安全。
话虽如此。最好使用线程池来处理请求。
ExecutorService executorService = Executors.newFixedThreadPool(100);
while (true) {
String request = readFromSocket(socket);
Runnable task = () -> {
processRequest(request);
};
executorService.submit(task);
}
同样,对ExecutorService.submit
的调用会在它之前发生的任何事情与任务中的任何语句之间创建一个先发生的关系。
答案 1 :(得分:1)
您的代码存在各种问题。
正如你自己想象的那样,在P产生新结果之前,一个线程P产生可能被消耗或不被另一个线程消耗的东西是不理想的。
因此,像你一样简单地存储价值是不好的做法。合理的答案是:使用设计的数据结构允许&#34;一个线程放入内容,其他线程取出&#34 ;;比如ArrayBlockingQueue。
然后:为一项工作创建一个线程,然后把它丢弃,这是浪费时间。
相反:创建ExecutorService,并将任务提交到该服务中。该服务可以基于池线程,可以重复使用;而不是扔掉。