我试图实现一个非常简单的回送多线程服务器
我使用了使用newFixedThreadPool
创建的线程池,但看起来并发连接数固定为nThreads
(传递到newFixedThreadPool
)。例如,如果我将nThreads
设置为3,则连接的第四个客户端无法与服务器交互。
这很奇怪,因为文档说" 创建一个线程池,重用固定数量的线程在共享的无界队列中运行。&#34 ;
由于文档还说" 如果在所有线程都处于活动状态时提交了其他任务,它们将在队列中等待,直到线程可用。"我怀疑我的线程永远不会被释放"这样它们就再也无法重复使用了。
我认为这可能是一个愚蠢的错误,但我无法弄清楚错误是什么。这是我的客户端处理程序的run()
方法,我认为这与找到的代码here非常相似(下面的client
只是Socket
连接给客户):
@Override
public void run() {
try(BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))) {
String input;
while(true) {
input = readLine();
if(input == null)
break;
out.write(input);
out.newLine();
out.flush();
}
} catch(IOException e) {
System.err.println(e, "Error from socket IO.");
System.exit(1);
}
try {
client.close();
} catch(IOException e) {
System.err.println(e, "Error when closing socket.");
System.exit(1);
}
System.out.println("Client " + client.getInetAddress().getHostAddress() +
" closed connection.\n");
}
编辑1:我想我应该澄清一下,客户收到回复后会立即断开连接。所以线程很快就会出现。
编辑2:这里处理连接的代码(listen
是ServerSocket
):
while(true) {
Socket client = null;
try {
client = listen.accept();
} catch(IOException e) {
System.err.println(e, "Error when waiting for connection.");
System.exit(1);
}
System.out.println("New connection from client: " +
client.getInetAddress().getHostAddress() + "\n"
);
threadPool.execute(new ConnectionHandler(client));
}
这是线程池初始化(在构造函数中):this.threadPool = Executors.newCachedThreadPool();
。
编辑3:记录消息(当nThread = 3
时,所以第四个连接从未真正处理过):
New connection from client: 127.0.0.1
Client 127.0.0.1 closed connection.
New connection from client: 127.0.0.1
Client 127.0.0.1 closed connection.
New connection from client: 127.0.0.1
Client 127.0.0.1 closed connection.
New connection from client: 127.0.0.1
答案 0 :(得分:2)
我没有回答为什么第四个连接悬而未决,除了说这是一个有效的代码版本......
public class Test {
public static void main(String[] args) throws Exception {
ServerSocket listen = new ServerSocket(9999);
ExecutorService threadPool = Executors.newFixedThreadPool(3);
while(true) {
Socket client = null;
try {
client = listen.accept();
} catch(IOException e) {
System.err.println(e);
}
System.out.println("New connection from client: " +
client.getInetAddress().getHostAddress() + "\n"
);
threadPool.execute(new ConnectionHandler(client));
}
}
}
class ConnectionHandler implements Runnable {
private Socket client;
public ConnectionHandler(Socket client) {
this.client = client;
}
@Override
public void run() {
try(BufferedReader in = new BufferedReader(new InputStreamReader(client.getInputStream()));
BufferedWriter out = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()))) {
String input;
while(true) {
input = in.readLine();
if(input == null)
break;
out.write(input);
out.newLine();
out.flush();
}
} catch(IOException e) {
System.err.println("Error from socket IO.");
}
try {
client.close();
} catch(IOException e) {
System.err.println("Error when closing socket.");
}
System.out.println("Client " + client.getInetAddress().getHostAddress() +
" closed connection.\n");
}
}
我用telnet连接,回显了一些字符并断开连接。我这样做了5次。
New connection from client: 0:0:0:0:0:0:0:1
Client 0:0:0:0:0:0:0:1 closed connection.
New connection from client: 0:0:0:0:0:0:0:1
Client 0:0:0:0:0:0:0:1 closed connection.
New connection from client: 0:0:0:0:0:0:0:1
Client 0:0:0:0:0:0:0:1 closed connection.
New connection from client: 0:0:0:0:0:0:0:1
Client 0:0:0:0:0:0:0:1 closed connection.
New connection from client: 0:0:0:0:0:0:0:1
Client 0:0:0:0:0:0:0:1 closed connection.
唯一的区别是问题中的代码有行......
input = readLine();
我需要将其更改为
input = in.readLine();
有效。我想知道在未发布到问题的代码中是否包含静态变量或其他类型的恶作剧。
答案 1 :(得分:2)
我只是试图重现我的问题,我发现了问题(而且它确实非常愚蠢)。
在我的服务器端,我希望能够在"退出"时关闭服务器。在控制台中输入,所以我创建了一个 CREATE PROCEDURE [DBO].[TARGET_SIDE_PS]
AS
-- For error handling.
DECLARE @aERROR int
DECLARE @aCOUNT int
-- Start transaction.
BEGIN TRANSACTION
-- Drop target table.
IF OBJECT_ID('dbo.PML', 'U') IS NOT NULL
DROP TABLE dbo.PML;
-- Error catching
SELECT @aERROR = @@ERROR, @aCOUNT = @@ROWCOUNT
IF @aERROR<>0
BEGIN
-- Error : do what is needed.
--
ROLLBACK TRANSACTION
RETURN 1
END
SELECT *
INTO dbo.PML
FROM [SOURCELINKEDSERVER].[SOURCEDATABASE].[dbo].[PML]
-- Error catching
SELECT @aERROR = @@ERROR, @aCOUNT = @@ROWCOUNT
IF @aERROR<>0
BEGIN
-- Error : do what is needed.
--
ROLLBACK TRANSACTION
RETURN 2
END
IF @aCOUNT <= 0
BEGIN
-- No data: do what is needed.
--
PRINT 'NO DATA !!'
END
COMMIT TRANSACTION
RETURN 0
类(实现InputHandler
)来处理服务器端输入。它看起来像这样:
Runnable
我的服务器class InputHandler implements Runnable {
private Server server;
InputHandler(Server server) {
this.server = server;
}
@Override
public void run() {
String input = System.console().readLine();
if(input.toLowerCase().equals("exit"))
server.shutdown();
else
System.err.println("Invalid command. To exit enter \"exit\".");
}
}
实际上是这样的:
run()
基本上每次迭代都会创建一个新的private void loop() {
while(true) {
threadPool.execute(new InputHandler(this));
Socket client = null;
try {
client = listen.accept();
} catch(Exception e) {
System.err.println("Error when waiting for connection.");
System.exit(1);
}
System.out.println("New connection from client: " +
client.getInetAddress().getHostAddress() + "\n"
);
threadPool.execute(new Handler(client));
}
}
线程...(InputHandler
)。将此代码向上移动一行将修复它......
之前我没有提及threadPool.execute(new InputHandler(this));
,因为它似乎与我无关。可悲的是最终结果是它。
我很抱歉,我浪费了你们这些&#39;时间。我很抱歉。如果您愿意,请随意下载:)现在我将接受 slipperyseal 的回答。再次感谢您的帮助。