简体   繁体   English

如何使用多线程从套接字读取请求

[英]How to read request from socket using multi-threading

I have the following piece of code that reads a string from the socket and processes it. 我有以下一段代码,该代码从套接字读取一个字符串并进行处理。 After reading the string from the socket, I am creating a new thread to process the request. 从套接字读取字符串后,我将创建一个新线程来处理请求。 This way, I can have multiple threads concurrently processing the incoming requests. 这样,我可以让多个线程同时处理传入的请求。

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

The problem here is, "request" could get modified before a new thread starts processing it. 这里的问题是,“请求”可能在新线程开始处理它之前被修改。 I am new to multi-threading and I read few sites covering the basics. 我是多线程技术的新手,我读了很少的有关基础知识的网站。 I am still not sure of how to solve the problem here. 我仍然不确定如何解决这里的问题。

Using synchronized to wait until the new thread finishes processing the request would kill the purpose of multithreading. 使用sync等待新线程完成对请求的处理将杀死多线程的目的。 Once I get the request, I want to send it to a new thread so that it can start processing the request, while the parent thread can read the next request. 收到请求后,我想将其发送到新线程,以便它可以开始处理该请求,而父线程可以读取下一个请求。

Any help on how I can solve this problem? 我如何解决此问题有帮助吗? Thanks! 谢谢!

[edit after Luke's answer] [卢克回答后编辑]

In my scenario, the request would be a file name. 在我的情况下,请求将是一个文件名。 You could get several requests based on the size of a directory. 根据目录的大小,您可能会收到几个请求。 When I tried my code on a directory with fewer files, everything was fine. 当我在文件较少的目录上尝试代码时,一切都很好。 But when I ran my code against a directory with thousands of files, things got weird. 但是,当我针对包含数千个文件的目录运行代码时,事情变得很奇怪。

Say after readFromSocket, Thread-1 has request X and and Thread-2 has request Y. But inside processRequest(), Thread-1 starts processing request Y. 在readFromSocket之后说,线程1具有请求X,线程2具有请求Y。但是在processRequest()中,线程1开始处理请求Y。

The problem here is, "request" could get modified before a new thread starts processing it. 这里的问题是,“请求”可能在新线程开始处理它之前被修改。

No this is not permitted by the Java language. 不可以,Java语言不允许这样做。

You don't need to worry about request being modified before it's processed by a new thread. 您无需担心在新线程处理request之前对其进行修改。 Because, Thread.start() establish a happens-before relationship between the assignment to the request variable and the processRequest(request) method call in the new thread. 因为, Thread.start() request变量的分配与新线程中的processRequest(request)方法调用之间建立了事前关系。

When a statement invokes Thread.start, every statement that has a happens-before relationship with that statement also has a happens-before relationship with every statement executed by the new thread. 当一条语句调用Thread.start时,与该语句具有事前发生关系的每个语句也与新线程执行的每个语句都具有事前发生关系。 The effects of the code that led up to the creation of the new thread are visible to the new thread. 导致创建新线程的代码效果对于新线程是可见的。

So this statement String request = readFromSocket(socket); 所以这条语句String request = readFromSocket(socket); happens before new Thread(...).start(); 发生在new Thread(...).start(); and whatever code executed by the new thread. 以及新线程执行的任何代码。

Also String is immutable, so it won't change once it's created. 而且String是不可变的,因此创建后就不会更改。 You are safe. 你很安全

That being said. 话虽如此。 You are better off using a thread pool for handling requests. 您最好使用线程池来处理请求。

ExecutorService executorService = Executors.newFixedThreadPool(100);

while (true) {
    String request = readFromSocket(socket);
    Runnable task = () -> {
        processRequest(request);
    };
    executorService.submit(task);
}

Similarly, the call to ExecutorService.submit creates a happens-before relationship between whatever happens before it and any statements in the task. 同样,对ExecutorService.submit的调用会在事件发生之前与任务中的任何语句之间创建先发生后关系。

There are various problems with your code. 您的代码存在各种问题。

As you figured yourself, it isn't ideal that one thread P produces something that might be consumed, or not consumed by another thread before P produces a new result. 如您所知,在P产生新结果之前,一个线程P产生可能被另一线程消耗或未被另一线程消耗的东西并不理想。

Thus simply storing the value like you do is bad practice. 因此,像您一样简单地存储值是不好的做法。 The reasonable answer is: use a data structure that is designed to allow for "one thread puts something in, other threads take things out"; 合理的答案是:使用旨在允许“一个线程放入某些东西,其他线程取出一些东西”的数据结构; like the ArrayBlockingQueue . 就像ArrayBlockingQueue一样

Then: it is a huge waste of time to create a thread for one job, to then throw it away. 然后:这是一个巨大的时间来创建一个线程一个作业浪费,然后把它扔掉。

Instead: create an ExecutorService , and submit tasks into that service. 而是:创建一个ExecutorService ,然后将任务提交到该服务中。 The service can be based on a pool of threads, that get re-used; 该服务可以基于可重复使用的线程 instead of thrown away. 而不是扔掉。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM