zoukankan      html  css  js  c++  java
  • [原创]java:Stream、Socket等源码分析

    一、对于java启动之后的线程的说明

      java在启动后会有几个特殊线程:

      1、main线程,主线程

      2、JVM线程,虚拟机的线程

      3、GC垃圾回收线程,是个守护线程

      4、EDT&Toolkit

      5、在启动图形界面时会自动创建两个线程,用于接收事件之前阻塞界面

        AWT-Shutdown与AWT-EventQueue-0,所以在触发按钮事件时,所有的操作都是在AWT-EventQueue-0线程中进行的,而不是在主线程中。

        在AWT.setVisible之后这两个线程会开辟出来,setVisible之后的代码还是在当前线程中运行的。

    二、InputStream的read操作。

      int read(),如果因为已经到达流末尾而没有可用的字节,则返回值 -1。在输入数据可用、检测到流末尾或者抛出异常前,此方法一直阻塞。 

      要使用int作为返回值也是因为返回值-1的缘故,这样就不会和byte中的-1冲突了。

      那么什么时候才算流末尾呢?

      不同子类read操作不同,流末尾的判别也不同。

      ByteArrayInputStream:

        public synchronized int read() {
            return (pos < count) ? (buf[pos++] & 0xff) : -1;
        }

      因为此类是使用已有缓冲区创建的,所以在读到缓冲区结尾时即返回-1,流末尾即超过缓冲区,不存在线程阻塞。

      FileInputStream:

    public native int read() throws IOException;

      使用其他语言实现,很大可能是使用C语言来实现的,C语言读到文件末尾会返回EOF标记,处理为-1返回给调用方。

      BufferedInputStream:

      因为仅仅是加了缓冲区的InputStream,所以read操作还是调用的其他InputStream类的read。

      PipedInputStream:稍微复杂点

      使用了两个线程,所以PipedInputStream与PipedOutputStream一定要使用两个线程来创建,否则容易阻塞当前线程。

      在in<0时死循环,在连接未关闭时,只有在接收到数据时才会把in在其他线程中置为0,此时可以跳出此循环,可以继续往下执行。连接关闭时会返回-1。

    public synchronized int read()  throws IOException {
            if (!connected) {
                throw new IOException("Pipe not connected");
            } else if (closedByReader) {
                throw new IOException("Pipe closed");
            } else if (writeSide != null && !writeSide.isAlive()
                       && !closedByWriter && (in < 0)) {
                throw new IOException("Write end dead");
            }
    
            readSide = Thread.currentThread();
            int trials = 2;
            while (in < 0) {
                if (closedByWriter) {
                    /* closed by writer, return EOF */
                    return -1;
                }
                if ((writeSide != null) && (!writeSide.isAlive()) && (--trials < 0)) {
                    throw new IOException("Pipe broken");
                }
                /* might be a writer waiting */
                notifyAll();
                try {
                    wait(1000);
                } catch (InterruptedException ex) {
                    throw new java.io.InterruptedIOException();
                }
            }
            int ret = buffer[out++] & 0xFF;
            if (out >= buffer.length) {
                out = 0;
            }
            if (in == out) {
                /* now empty */
                in = -1;
            }
    
            return ret;
        }

    三、Reader的read操作

      Reader类中有一个lock对象,表示当前对象,在Reader的所有操作用都会有一句:synchronized (lock),即锁定当前对象,所以在阻塞时,也不允许其他线程对该对象的其他任何操作,包括关闭等。

    protected Reader() {
            this.lock = this;
        }

      BufferedReader:

        public int read(char cbuf[], int off, int len) throws IOException {
            synchronized (lock) {
                ensureOpen();
                if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                    ((off + len) > cbuf.length) || ((off + len) < 0)) {
                    throw new IndexOutOfBoundsException();
                } else if (len == 0) {
                    return 0;
                }
    
                int n = read1(cbuf, off, len);
                if (n <= 0) return n;
                while ((n < len) && in.ready()) {
                    int n1 = read1(cbuf, off + n, len - n);
                    if (n1 <= 0) break;
                    n += n1;
                }
                return n;
            }
        }

      而在read1中,则是调用了fill()方法,去填充缓冲区:

        private void fill() throws IOException {
            int dst;
            if (markedChar <= UNMARKED) {
                /* No mark */
                dst = 0;
            } else {
                /* Marked */
                int delta = nextChar - markedChar;
                if (delta >= readAheadLimit) {
                    /* Gone past read-ahead limit: Invalidate mark */
                    markedChar = INVALIDATED;
                    readAheadLimit = 0;
                    dst = 0;
                } else {
                    if (readAheadLimit <= cb.length) {
                        /* Shuffle in the current buffer */
                        System.arraycopy(cb, markedChar, cb, 0, delta);
                        markedChar = 0;
                        dst = delta;
                    } else {
                        /* Reallocate buffer to accommodate read-ahead limit */
                        char ncb[] = new char[readAheadLimit];
                        System.arraycopy(cb, markedChar, ncb, 0, delta);
                        cb = ncb;
                        markedChar = 0;
                        dst = delta;
                    }
                    nextChar = nChars = delta;
                }
            }
    
            int n;
            do {
                n = in.read(cb, dst, cb.length - dst);
            } while (n == 0);
            if (n > 0) {
                nChars = dst + n;
                nextChar = dst;
            }
        }

      可以看到在n==0时,do会死循环,也就是说,在未从流中读到数据时,此线程会通过死循环阻塞。在流中读到0个字节的数据与读到-1是不同的,-1表示流结尾。

      readLine方法中:

    String readLine(boolean ignoreLF) throws IOException {
            StringBuffer s = null;
            int startChar;
    
            synchronized (lock) {
                ensureOpen();
                boolean omitLF = ignoreLF || skipLF;
    
            bufferLoop:
                for (;;) {
    
                    if (nextChar >= nChars)
                        fill();
                    if (nextChar >= nChars) { /* EOF */
                        if (s != null && s.length() > 0)
                            return s.toString();
                        else
                            return null;
                    }
                    boolean eol = false;
                    char c = 0;
                    int i;
    
                    /* Skip a leftover '
    ', if necessary */
                    if (omitLF && (cb[nextChar] == '
    '))
                        nextChar++;
                    skipLF = false;
                    omitLF = false;
    
                charLoop:
                    for (i = nextChar; i < nChars; i++) {
                        c = cb[i];
                        if ((c == '
    ') || (c == '
    ')) {
                            eol = true;
                            break charLoop;
                        }
                    }
    
                    startChar = nextChar;
                    nextChar = i;
    
                    if (eol) {
                        String str;
                        if (s == null) {
                            str = new String(cb, startChar, i - startChar);
                        } else {
                            s.append(cb, startChar, i - startChar);
                            str = s.toString();
                        }
                        nextChar++;
                        if (c == '
    ') {
                            skipLF = true;
                        }
                        return str;
                    }
    
                    if (s == null)
                        s = new StringBuffer(defaultExpectedLineLength);
                    s.append(cb, startChar, i - startChar);
                }
            }
        }

      同样是调用了fill(),不同的是readLine在未读到/r与/n之前都会一直阻塞。

      CharArrayReader:

      直接通过现有缓冲区读数据,不存在阻塞等问题。

      InputStreamReader:

      使用StreamDecoder进行read。

      PipedReader:

      同样是使用两个线程进行操作,同PipedInputStream。

    public synchronized int read(char cbuf[], int off, int len)  throws IOException {
            if (!connected) {
                throw new IOException("Pipe not connected");
            } else if (closedByReader) {
                throw new IOException("Pipe closed");
            } else if (writeSide != null && !writeSide.isAlive()
                       && !closedByWriter && (in < 0)) {
                throw new IOException("Write end dead");
            }
    
            if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                ((off + len) > cbuf.length) || ((off + len) < 0)) {
                throw new IndexOutOfBoundsException();
            } else if (len == 0) {
                return 0;
            }
    
            /* possibly wait on the first character */
            int c = read();
            if (c < 0) {
                return -1;
            }
            cbuf[off] =  (char)c;
            int rlen = 1;
            while ((in >= 0) && (--len > 0)) {
                cbuf[off + rlen] = buffer[out++];
                rlen++;
                if (out >= buffer.length) {
                    out = 0;
                }
                if (in == out) {
                    /* now empty */
                    in = -1;
                }
            }
            return rlen;
        }

      StringReader:

      使用现有字符串进行操作,所以也不存在阻塞。

        public int read(char cbuf[], int off, int len) throws IOException {
            synchronized (lock) {
                ensureOpen();
                if ((off < 0) || (off > cbuf.length) || (len < 0) ||
                    ((off + len) > cbuf.length) || ((off + len) < 0)) {
                    throw new IndexOutOfBoundsException();
                } else if (len == 0) {
                    return 0;
                }
                if (next >= length)
                    return -1;
                int n = Math.min(length - next, len);
                str.getChars(next, next + n, cbuf, off);
                next += n;
                return n;
            }
        }

      因为上边这些都是通过while死循环来进行锁死的,所以在使用到这些的时候建议放在一个单独的线程中,以免影响程序正常运行。

    四、DatagramSocket的receive与send

      receive方法在接收到数据报前一直阻塞。

    public synchronized void receive(DatagramPacket p) throws IOException {
            synchronized (p) {
                if (!isBound())
                    bind(new InetSocketAddress(0));
                if (connectState == ST_NOT_CONNECTED) {
                    SecurityManager security = System.getSecurityManager();
              ……
              ……
                }
        if (connectState == ST_CONNECTED_NO_IMPL) {
                    boolean stop = false;
              ……
              ……
                }
                getImpl().receive(p);
          }
    }

      两个锁,一个锁当前对象,一个锁DatagramPacket。阻塞线程的操作是交给getImpl().receive(p)来进行的。

      对于send()方法:

    public void send(DatagramPacket p) throws IOException  {
            InetAddress packetAddress = null;
            synchronized (p) {

      只锁了DatagramPacket,所以在不使用同一个DatagramPacket来发送和接收数据的情况下,可以使用同一个DatagramSocket对象在不同线程中进行发送和接收操作。

  • 相关阅读:
    XAML学习笔记之Layout(五)——ViewBox
    XAML学习笔记——Layout(三)
    XAML学习笔记——Layout(二)
    XAML学习笔记——Layout(一)
    从0开始搭建SQL Server 2012 AlwaysOn 第三篇(安装数据,配置AlwaysOn)
    从0开始搭建SQL Server 2012 AlwaysOn 第二篇(配置故障转移集群)
    从0开始搭建SQL Server 2012 AlwaysOn 第一篇(AD域与DNS)
    Sql Server 2012 事务复制遇到的问题及解决方式
    Sql Server 2008R2升级 Sql Server 2012 问题
    第一次ACM
  • 原文地址:https://www.cnblogs.com/guangshan/p/4270264.html
Copyright © 2011-2022 走看看