如何从多个线程获取数据?

时间:2014-07-24 12:44:17

标签: java multithreading

我目前正在开发一款通过IP网络远程监控少数对象的应用。网络中的每个节点定期发送来自传感器的更新数据(电压,电流,温度等)。

我启动一个新的Thread来处理每个远程对象。但是我从线程传输数据时遇到了问题。

将数据传输到主线程的最佳方法是什么?我应该选择我拥有的或不同的东西吗?

2 个答案:

答案 0 :(得分:1)

解决此类问题的常见方法是将IO从处理中分离出来,因此在您的情况下,您可以从套接字读取一个线程并将数据传递给工作线程进行处理。

套接字IO

从套接字IO开始,由于Java 7 NIO API已经可用,这意味着您可以保留所需的线程,以防止阻止主线程进入操作系统。

您可以使用AsynchronousServerSocketChannelAsynchronousSocketChannel来监听传入的连接并开始阅读:

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类