[英]Share common data between two threads serving a Socket connection
我在SO上看到了很多類似的問題,但幾乎沒有任何人在圖片中有Socket
。 所以請花些時間閱讀這個問題。
我有服務器應用程序(使用ServerSocket
)偵聽請求,當客戶端嘗試連接時,創建新線程以服務客戶端(並且服務器返回到新請求的偵聽模式)。 現在,我需要根據其他客戶端發送到服務器的內容來響應一個客戶端。
例:
我對這整個“線程間通信”的事情不熟悉。 顯然,上面提到的情況聽起來很簡單,但我正在描述這一點以獲得提示,因為我將在客戶端之間交換大量數據,將服務器保持為中間狀態。
另外,如果我想將共享對象限制為10個特定客戶端,該怎么辦? 這樣,當第11個客戶端連接到服務器時,我創建了新的共享對象,該對象將用於在第11,第12,第13 ......到第20個客戶端之間交換數據。 等等每一組10個客戶端。
我嘗試了什么:(我猜是愚蠢的)
public
類,該對象應該作為public static
共享,以便我可以將其用作全局而不實例化它,如MyGlobalClass.SharedMsg
。 我知道存在明顯的鎖定問題,因為如果一個線程正在寫入一個對象,則其他線程在第一個線程完成寫入之前無法訪問它。
那么這個問題的理想方法是什么呢?
由於我創建線程來提供傳入連接請求的方式,我無法理解如何在線程之間共享相同的對象,因為如上所述使用Global對象不起作用。
以下是我如何監聽傳入連接並動態創建服務線程。
// Method of server class
public void startServer()
{
if (!isRunning)
{
try
{
isRunning = true;
while (isRunning)
{
try
{
new ClientHandler(mysocketserver.accept()).start();
}
catch (SocketTimeoutException ex)
{
//nothing to perform here, go back again to listening.
}
catch (SocketException ex)
{
//Not to handle, since I'll stop the server using SocketServer's close() method, and its going to throw SocketException anyway.
}
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
else
System.out.println("Server Already Started!");
}
和ClientHandler
類。
public class ClientHandler extends Thread
{
private Socket client = null;
private ObjectInputStream in = null;
private ObjectOutputStream out = null;
public ClientHandler(Socket client)
{
super("ClientHandler");
this.client = client;
}
//This run() is common for every Client that connects, and that's where the problem is.
public void run()
{
try
{
in = new ObjectInputStream(client.getInputStream());
out = new ObjectOutputStream(client.getOutputStream());
//Message received from this thread.
String msg = in.readObject().toString();
System.out.println("Client @ "+ client.getInetAddress().getHostAddress() +" Says : "+msg);
//Response to this client.
out.writeObject("Message Received");
out.close();
in.close();
client.close();
}
catch (Exception ex)
{
ex.printStackTrace();
}
}
}
我相信我正在創建動態線程來為每個連接的客戶端提供服務,使用Global對象共享相同的數據源是不可能的,因為上面的run()
主體對於每個連接的客戶端都是完全相同的,因此消費者和生產者都是同樣的方法。 我應該做什么修復,以便我可以為每個連接創建動態線程,並仍然共享相同的對象。
您可能需要一個隊列來在每個客戶端之間進行通信 每個隊列將是從一個客戶端推送到另一個客戶端的數據的“管道”。
您會像這樣使用它(偽代碼):
Thread 1:
Receive request from Client A, with message for Client B
Put message on back of concurrent Queue A2B
Respond to Client A.
Thread 2:
Receive request from Client B.
Pop message from front of Queue A2B
Respond to Client B with message.
您可能還希望它是通用的,因此您擁有許多客戶端(以及許多線程)可以寫入的AllToB隊列。
注意事項: ConcurrentLinkedQueue , ArrayBlockingQueue 。
如果要限制消息數,則ArrayBlockingQueue及其容量構造函數允許您執行此操作。 如果您不需要阻止功能,則可以使用方法offer
和poll
而不是put
和take
。
我不擔心共享隊列,這會使問題變得更加復雜。 如果您知道需要解決的內存使用問題,請執行此操作。
編輯:根據您的更新:
如果需要在所有動態創建的實例之間共享單個實例,您可以:
示例1:
public class ClientHandler extends Thread
{
public static final Map<ClientHandler, BlockingQueue<String>> messageQueues
= new ConcurrentHashMap<>();
<snip>
public ClientHandler(Socket client)
{
super("ClientHandler");
this.client = client;
// Note: Bad practice to reference 'this' in a constructor.
// This can throw an error based on what the put method does.
// As such, if you are to do this, put it at the end of the method.
messageQueues.put(this, new ArrayBlockingQueue<>());
}
// You can now access this in the run() method like so:
// Get messages for the current client.
// messageQueues.get(this).poll();
// Send messages to the thread for another client.
// messageQueues.get(someClient).offer(message);
幾個筆記:
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.