关于Java OutputStream 线程安全问题

今天偶尔发现java的输出流的线程安全问题

先看代码吧

 

  1. import java.io.File;  
  2. import java.io.FileInputStream;  
  3. import java.io.FileOutputStream;  
  4. import java.io.OutputStream;  
  5. import java.util.Random;  
  6. import java.util.concurrent.TimeUnit;  
  7.   
  8.   
  9. public class TestFileWrite {  
  10.   
  11.     public static void main(String[] args)throws Exception {  
  12.         // TODO Auto-generated method stub  
  13.         File f = new File("a"+System.currentTimeMillis());  
  14.         f.createNewFile();  
  15.         java.util.concurrent.CountDownLatch cwl = new java.util.concurrent.CountDownLatch(2);  
  16.         FileOutputStream fos = new FileOutputStream(f);  
  17.         new Thread(new WriteThread(cwl,fos,"bb")).start();  
  18.         new Thread(new WriteThread(cwl,fos,"aaasdfasfd")).start();  
  19.         cwl.countDown();  
  20.         cwl.countDown();  
  21.         TimeUnit.SECONDS.sleep(1);  
  22.         fos.flush();  
  23.         FileInputStream f1 = new FileInputStream(f);  
  24.         byte[] bytearray = new byte[1024];  
  25.         int n = f1.read(bytearray);  
  26.         f1.close();  
  27.         System.out.println(new String(bytearray,0,n));  
  28.     }  
  29.   
  30. }  
  31.   
  32. class WriteThread implements Runnable{  
  33.   
  34.     OutputStream os = null;  
  35.     String fSpe = null;  
  36.     java.util.concurrent.CountDownLatch cwl;  
  37.     public WriteThread(java.util.concurrent.CountDownLatch cwl,OutputStream os, String fSpe) {  
  38.         super();  
  39.         this.cwl = cwl;  
  40.         this.os = os;  
  41.         this.fSpe = fSpe;  
  42.     }  
  43.   
  44.   
  45.     @Override  
  46.     public void run() {  
  47. //      for(int i=0;i<10;i++)  
  48. //      {  
  49.             try {  
  50.                 cwl.await();  
  51.                 os.write((fSpe+"\r\n").getBytes());  
  52. //              TimeUnit.SECONDS.sleep(new Random().nextInt(10));  
  53.             } catch (Exception e) {  
  54.                 // TODO Auto-generated catch block  
  55.                 e.printStackTrace();  
  56.             }  
  57. //      }  
  58.           
  59.     }  
  60. }  

这里的输出结果很有意思,会相互覆盖,偶尔输出

 

aaasdfasfd

有时候输出

bb
dfasfd

输出第二种是因为第一bb \r\n把第一个字符串给覆盖掉了

观察Filetputtream的代码,发现很有意思,整个类是非线程安全的,不过在类的注释上没有标记这一点,最底层的写入是调用本地方法

 

  1. private native void writeBytes(byte b[], int off, int len, boolean append)  


此处的相互覆盖应该是底层没有对并发进行处理,导致两重生之大文豪个线程同时在字节流的同一位置进行写入,应该还会有更奇怪的输出

 

ab

dfasfd

不过这种输出的重现几率很小,理论上应该存在

 

再观察DataOutputStream 发现只有write(byte b[])会加锁,其他时候不会加锁,不知到为何,难道是为了提升性能故意把锁去掉了?

posted @ 2014-01-07 15:19  将夜  阅读(1451)  评论(0编辑  收藏  举报