我目前正在开发一款通过IP网络远程监控少数对象的应用。网络中的每个节点定期发送来自传感器的更新数据(电压,电流,温度等)。
我启动一个新的Thread来处理每个远程对象。但是我从线程传输数据时遇到了问题。
将数据传输到主线程的最佳方法是什么?我应该选择我拥有的或不同的东西吗?
答案 0 :(得分:1)
解决此类问题的常见方法是将IO从处理中分离出来,因此在您的情况下,您可以从套接字读取一个线程并将数据传递给工作线程进行处理。
套接字IO
从套接字IO开始,由于Java 7 NIO API已经可用,这意味着您可以保留所需的线程,以防止阻止主线程进入操作系统。
您可以使用AsynchronousServerSocketChannel和AsynchronousSocketChannel来监听传入的连接并开始阅读:
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
serverSocketChannel.accept(null, new AcceptAndReadHandler());
CompletionHandler s看起来像这样:
private class AcceptAndReadHandler implements CompletionHandler<AsynchronousSocketChannel,Void>
{
public void completed(AsynchronousSocketChannel channel, Void attribute)
{
serverSocketChannel.accept(null, this);
ReadHandler readHandler = new ReadHandler(channel);
channel.read(readHandler.getBuffer(), Void, readHandler)
}
}
public class ReadHandler implements CompletionHandler<Integer,Void>
{
private ByteBuffer buffer;
public ReadHandler(AsynchronousSocketChannel channel)
{
this.channel = channel;
this.buffer = ByteBuffer.allocate(1024);
}
public ByteBuffer getBuffer() { return this.buffer; }
public void completed(Integer read, Void attribute)
{
byte[] data = new byte[read];
buffer.get(data);
...
ReadHandler readHandler = new ReadHandler(channel);
channel.read(readHandler.getBuffer(), Void, readHandler).
}
}
ReadHandler
需要足够智能才能将收到的数据分段,因为在发送时,块中不会收到任何保证数据。
两种常见的方法是为每条消息添加其长度前缀或使用分隔符。您可能还想查看像Netty这样的API,我相信这样就可以让您不必编写逻辑来重构消息。
<强>处理强>
完成一条完整的消息后,您需要对其进行处理。最简单的方法是放入队列,这样你的代码可能看起来像:
public static void main(String[] args)
{
boolean running = true;
Runtime.getRuntime().addShutdownHook(() -> running = false);
BlockingQueue<byte[]> queue = new ArrayBlockingQueue<>();
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
serverSocketChannel.accept(null, new AcceptAndReadHandler(queue));
while (running)
{
byte[] data = queue.take();
...
}
}
为了实现此目的,您需要修改CompletionHandler以传递queue
,以便ReadHandler
可以添加它是data
。
如果你想多线程化处理,那么你应该使用Executor,如下所示:
public static void main(String[] args)
{
boolean running = true;
Runtime.getRuntime().addShutdownHook(() -> running = false);
Executor executor = Executors.newFixedThreadPool(10);
AsynchronousServerSocketChannel serverSocketChannel = AsynchronousServerSocketChannel.open().bind(new InetSocketAddress(5000));
serverSocketChannel.accept(null, new AcceptAndReadHandler(executor));
while (running)
{
...
}
}
同样,您需要修改CompletionHandler,这次您可以通过executor
提交处理ReadHandler
逻辑到。
这两种方法之间的重要区别在于,使用多线程示例并不能保证处理顺序。
答案 1 :(得分:0)
也许尝试将您的数据放入BlockingQueue并在主线程中获取数据。
示例:
public class Producer implements Runnable { //this class puts objects with data into BlockingQueue
private BlockingQueue<Object> queue;
public Producer(BlockingQueue<Object> q) {
this.queue = q;
}
@Override
public void run() {
//place fot your instruction
Object yourData = new Object();
}
try {
queue.put(yourData);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
这是从BlockingQueue
读取数据的类public class Consumer implements Runnable{
private BlockingQueue<Object> queue;
public Consumer(BlockingQueue<Object> q){
this.queue=q;
}
@Override
public void run() {
try{
Object date = queue.take()
// your operations witha data
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
或使用ExecutorService和Future类