【问题标题】:Thread hanging when calling LinkedList.add调用 LinkedList.add 时线程挂起
【发布时间】:2019-11-12 15:12:17
【问题描述】:

所以我正在编写两个 ServerSocket。一个监听 8085 端口上的 HTTP 请求并将字节输入保存到静态 LinkedList 中,另一个监听端口 8086 并在静态 LinkedList 中返回所有结果。

问题是当将数据从 ServerSocket:8085 保存到 LinkedList 时,线程挂起,我不知道为什么。

这是主要的 Listener 类:

import java.net.ServerSocket;
import java.nio.charset.Charset;
import java.util.*;

public class Listener {

    public static LinkedList<byte[]> Calls = new LinkedList<>();

    public static void main(String[] args) {
        Thread callback = new Thread(new ThreadListener());
        callback.start();

        while (true) {
            try (var listener = new ServerSocket(8086)) {
                System.out.println("Listening on 8086...");
                try (var client = listener.accept()) {
                    StringBuffer response = new StringBuffer();
                    response.append("HTTP/1.1 200 OK\r\n\r\n");
                    Iterator<byte[]> iterator = Calls.iterator();
                    while (iterator.hasNext()) {
                        response.append(new String(iterator.next(), Charset.forName("UTF-8")) + "\r\n");
                        iterator.remove();
                    }
                    client.getOutputStream().write(response.toString().getBytes("UTF-8"));
                    client.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }


    }
}

这是 ThreadListener 类:

import java.io.IOException;
import java.net.ServerSocket;
import java.util.Date;

public class ThreadListener implements Runnable {
    @Override
    public void run() {
        while (true) {
            try (var listener = new ServerSocket(8085)) {
                System.out.println("Listening on 8085...");
                try (var socket = listener.accept()) {
                    if (!socket.getInetAddress().getHostAddress().equals("127.0.0.1")) {
                        System.out.println("Not localhost");
                    } else {
                        System.out.println("Its us!");
                    }
                    Listener.Calls.add(socket.getInputStream().readAllBytes());
                    System.out.println("Result collected");
                    Date today = new Date();
                    String httpResponse = "HTTP/1.1 200 OK\r\n\r\n" + today;
                    socket.getOutputStream().write(httpResponse.getBytes("UTF-8"));
                    socket.close();
                }

            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}

至于我的测试,我尝试调用 127.0.0.1:8085,我得到一个 ERR_CONNECTION_RESET,我在控制台中的所有内容如下:

Listening on 8085...
Listening on 8086...
Its us!

Process finished with exit code -1 (I killed the app after 2 mins)

事实上,“它就是我们!”消息已打印,但“已收集结果!”在 LinkedList.add 之后没有导致我认为 LinkedList.add 是挂起线程的那个。

问候

编辑:没有人打电话给 8085(或 8086),我是在浏览器上手动进行的。我通过创建一个方法来调用而不是直接调用 LinkedList.add 解决了同步问题:

    public static synchronized void addElementsToList(byte[] bytes) {
        Calls.add(bytes);
    }

这确实有效,但调用 8085 套接字每次都会重置连接。

【问题讨论】:

  • 除了同步问题,谁在写8085服务器?写在那里的应用程序在完成写入后是否关闭流?如果它是一个程序,也显示它的代码。如果不是,请解释它是如何完成的。请edit问题添加信息,不要使用cmets,除非告诉我们你更新了。
  • 更新了@RealSkeptic
  • 您的同步方法有误。不要使用同步的静态方法。它们在类对象上同步,如果您曾经使用过waitnotify,可能会导致问题。此外,如果您只在写入时同步,而在读取时忽略同步,则同步毫无意义。您的迭代器在运行时会更改 Calls。这很糟糕。您应该改用阻塞队列。

标签: java multithreading serversocket


【解决方案1】:

您使用浏览器创建请求的测试机制在这里可能也无济于事,因为InputStream.readAllBytes()

阻塞,直到所有剩余字节都被读取并检测到流结束,或者抛出异常。此方法不会关闭输入流。

来自the documentation。具体来说,浏览器保持连接打开,因为它期待一些响应。您的服务器正在尝试从连接中读取所有内容直到连接关闭。 Catch 22(又名死锁)。

尝试使用telnetlocalhost:8085 建立连接并从客户端关闭连接

telnet 127.0.0.1 8085

^D

^D 字面意思是 [CTRL] 和 [D] 键(例如:注销)

【讨论】:

  • 正确,切换到socket.getInputStream().readNBytes(socket.getInputStream().available()) 解决了这个问题。
  • 太棒了。我想知道在同一个问题上是否有多个不同答案的徽章;-)
  • @TomasBreuer 那个方法(使用available)是错误的。这意味着您只读取本地缓冲的字节,但是如果您发送比本地缓冲区更大的长通信,available() 将不会反映完整的文本。请仔细阅读available() 的文档。
【解决方案2】:

LinkedList 不是 synchornizedas highlighted in the documentation

您可以手动处理同步、使用同步列表或并发列表。可能还有其他一些方法,但现在,请保持简单。

public static LinkedList<byte[]> Calls = Collections.synchronizedList(new LinkedList<>());

// or

public static LinkedList<byte[]> Calls = new CopyOnWriteArrayList<>();

【讨论】:

  • 添加同步方法有效,我现在仍在努力解决连接重置问题。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-08-28
  • 1970-01-01
  • 2020-03-13
  • 1970-01-01
  • 2013-08-18
相关资源
最近更新 更多