Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/327.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 在标准输出流上并发写入_Java_Sockets_Queue_Outputstream - Fatal编程技术网

Java 在标准输出流上并发写入

Java 在标准输出流上并发写入,java,sockets,queue,outputstream,Java,Sockets,Queue,Outputstream,我正在编写一个应用程序,其中包括将相当大的数据块写入OutputStream(属于套接字)。让这有点复杂的是,通常有多个线程试图写入同一输出流。目前,我已经将其设计为将数据写入的OutputStream位于其自己的线程中。该线程包含一个队列(LinkedList),该队列轮询字节数组并尽快写入它们 private class OutputStreamWriter implements Runnable { private final LinkedList<byte[]> c

我正在编写一个应用程序,其中包括将相当大的数据块写入OutputStream(属于套接字)。让这有点复杂的是,通常有多个线程试图写入同一输出流。目前,我已经将其设计为将数据写入的OutputStream位于其自己的线程中。该线程包含一个队列(LinkedList),该队列轮询字节数组并尽快写入它们

private class OutputStreamWriter implements Runnable {

    private final LinkedList<byte[]> chunkQueue = new LinkedList<byte[]>();

    public void run() {
        OutputStream outputStream = User.this.outputStream;
        while (true) {
            try {
                if (chunkQueue.isEmpty()) {
                    Thread.sleep(100);
                    continue;
                }
                outputStream.write(chunkQueue.poll());
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
私有类OutputStreamWriter实现可运行{
private final LinkedList chunkQueue=新LinkedList();
公开募捐{
OutputStream OutputStream=User.this.OutputStream;
while(true){
试一试{
if(chunkQueue.isEmpty()){
睡眠(100);
继续;
}
write(chunkQueue.poll());
}捕获(例外e){
e、 printStackTrace();
}
}
}
}
这种设计的问题是,随着越来越多的写入操作的发生,越来越多的数据排队,写入速度也越来越慢。最初,当数据放入队列时,实际上会立即写入。大约15秒后,数据开始滞后;延迟从数据排队到数据实际写入的时间。随着时间的推移,这种延迟变得越来越长。这是非常明显的

解决这个问题的一种方法是某种ConcurrentOutputStream实现,它允许在不阻塞的情况下发送数据,这样就不会开始备份写操作(见鬼,这样就不需要队列了)。我不知道是否有这样的实现——我一直找不到——我个人认为甚至不可能编写一个


那么,有人对我如何重新设计这个有什么建议吗?

套接字的吞吐量是有限的;如果它比数据生成吞吐量慢,则必须缓冲数据,这是无法避免的。写“同时”一点帮助都没有


当排队数据超过某个极限时,可以考虑暂停数据生成,以减少内存消耗。

< P>我同意“不可信”,即并行写作几乎无济于事。相反,你应该关注生产方面,即你已经拥有的东西

  • 使用BlockingQueue而不是LinkedList

  • 使用队列的阻塞轮询操作,而不仅仅是100msl的盲睡眠,根据定义,这将平均浪费50%的时间。在很长一段时间内,这可能真的会增加


  • 我需要一个过滤器来拦截慢速连接,我需要尽快关闭DB连接,所以我最初使用Java管道,但当仔细观察它们的实现时,它是同步的,所以我最终使用一个小缓冲区创建了自己的QueueInputStream,并阻塞队列,以便在缓冲区满后将其放入队列中,它是无锁的,除了LinkedBlockingQueue使用的锁条件(借助于小缓冲区,它应该是便宜的)外,此类仅用于每个实例的单个生产者和消费者,并且您应该传递ExecutorService以开始将排队的字节流式传输到最终输出流:

    import java.io.IOException;
    import java.io.OutputStream;
    import java.util.concurrent.*;
    
    public class QueueOutputStream extends OutputStream
    {
      private static final int DEFAULT_BUFFER_SIZE=1024;
      private static final byte[] END_SIGNAL=new byte[]{};
    
      private final BlockingQueue<byte[]> queue=new LinkedBlockingDeque<>();
      private final byte[] buffer;
    
      private boolean closed=false;
      private int count=0;
    
      public QueueOutputStream()
      {
        this(DEFAULT_BUFFER_SIZE);
      }
    
      public QueueOutputStream(final int bufferSize)
      {
        if(bufferSize<=0){
          throw new IllegalArgumentException("Buffer size <= 0");
        }
        this.buffer=new byte[bufferSize];
      }
    
      private synchronized void flushBuffer()
      {
        if(count>0){
          final byte[] copy=new byte[count];
          System.arraycopy(buffer,0,copy,0,count);
          queue.offer(copy);
          count=0;
        }
      }
    
      @Override
      public synchronized void write(final int b) throws IOException
      {
        if(closed){
          throw new IllegalStateException("Stream is closed");
        }
        if(count>=buffer.length){
          flushBuffer();
        }
        buffer[count++]=(byte)b;
      }
    
      @Override
      public synchronized void write(final byte[] b, final int off, final int len) throws IOException
      {
        super.write(b,off,len);
      }
    
      @Override
      public synchronized void close() throws IOException
      {
        flushBuffer();
        queue.offer(END_SIGNAL);
        closed=true;
      }
    
      public Future<Void> asyncSendToOutputStream(final ExecutorService executor, final OutputStream outputStream)
      {
        return executor.submit(
                new Callable<Void>()
                {
                  @Override
                  public Void call() throws Exception
                  {
                    try{
                      byte[] buffer=queue.take();
                      while(buffer!=END_SIGNAL){
                        outputStream.write(buffer);
                        buffer=queue.take();
                      }
                      outputStream.flush();
                    } catch(Exception e){
                      close();
                      throw e;
                    } finally{
                      outputStream.close();
                    }
                    return null;
                  }
                }
        );
      }
    
    import java.io.IOException;
    导入java.io.OutputStream;
    导入java.util.concurrent.*;
    公共类QueueOutputStream扩展了OutputStream
    {
    私有静态final int DEFAULT_BUFFER_SIZE=1024;
    专用静态最终字节[]END_信号=新字节[]{};
    private final BlockingQueue=新建LinkedBlockingDeque();
    专用最终字节[]缓冲区;
    私有布尔闭合=假;
    私有整数计数=0;
    公共队列输出流()
    {
    该值(默认缓冲区大小);
    }
    公共QueueOutputStream(最终int-bufferSize)
    {
    if(bufferSize0){
    最终字节[]复制=新字节[计数];
    System.arraycopy(缓冲区,0,拷贝,0,计数);
    报价单(复印件);
    计数=0;
    }
    }
    @凌驾
    公共同步的无效写入(最终int b)引发IOException
    {
    如果(关闭){
    抛出新的IllegalStateException(“流已关闭”);
    }
    如果(计数>=缓冲区长度){
    flushBuffer();
    }
    缓冲区[计数++]=(字节)b;
    }
    @凌驾
    公共同步的无效写入(最终字节[]b,最终整数关闭,最终整数len)引发IOException
    {
    超级。注销(b、off、len);
    }
    @凌驾
    public synchronized void close()引发IOException
    {
    flushBuffer();
    排队报盘(结束信号);
    关闭=真;
    }
    公共未来asyncSendToOutputStream(最终执行器服务执行器,最终输出流OutputStream)
    {
    返回执行者。提交(
    新的可调用()
    {
    @凌驾
    public Void call()引发异常
    {
    试一试{
    byte[]buffer=queue.take();
    while(缓冲区!=结束信号){
    outputStream.write(缓冲区);
    buffer=queue.take();
    }
    outputStream.flush();
    }捕获(例外e){
    close();
    投掷e;
    }最后{
    outputStream.close();
    }
    返回null;
    }
    }
    );
    }
    
    这不是很有建设性。它有什么问题吗?顺便说一句,你正在同步修改到你的linkedlist吗?因为它在设计上不是线程安全的。还有,你在套接字输出上分层的是什么类型的输出流,你在推送多少数据?我只是在这里扔东西,但是,那又怎么样呢不是一个SocketChannel吗?我不认为这会有帮助。瓶颈是网络带宽。