Java 在标准输出流上并发写入
我正在编写一个应用程序,其中包括将相当大的数据块写入OutputStream(属于套接字)。让这有点复杂的是,通常有多个线程试图写入同一输出流。目前,我已经将其设计为将数据写入的OutputStream位于其自己的线程中。该线程包含一个队列(LinkedList),该队列轮询字节数组并尽快写入它们Java 在标准输出流上并发写入,java,sockets,queue,outputstream,Java,Sockets,Queue,Outputstream,我正在编写一个应用程序,其中包括将相当大的数据块写入OutputStream(属于套接字)。让这有点复杂的是,通常有多个线程试图写入同一输出流。目前,我已经将其设计为将数据写入的OutputStream位于其自己的线程中。该线程包含一个队列(LinkedList),该队列轮询字节数组并尽快写入它们 private class OutputStreamWriter implements Runnable { private final LinkedList<byte[]> c
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>我同意“不可信”,即并行写作几乎无济于事。相反,你应该关注生产方面,即你已经拥有的东西我需要一个过滤器来拦截慢速连接,我需要尽快关闭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吗?我不认为这会有帮助。瓶颈是网络带宽。