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对象在不同线程中进行发送和接收操作。

  • 相关阅读:
    DVWA 黑客攻防演练(十)反射型 XSS 攻击 Reflected Cross Site Scripting
    DVWA 黑客攻防演练(九) SQL 盲注 SQL Injection (Blind)
    DVWA 黑客攻防演练(八)SQL 注入 SQL Injection
    DVWA 黑客攻防演练(七)Weak Session IDs
    DVWA 黑客攻防演练(六)不安全的验证码 Insecure CAPTCHA
    DVWA 黑客攻防演练(五)文件上传漏洞 File Upload
    工作流表结构设计
    Visual Studio 2019尝鲜----新建空项目体验
    《使用CSLA 2019:CSLA .NET概述》原版和机译文档下载
    .NET快速开发平台的在线预览
  • 原文地址:https://www.cnblogs.com/guangshan/p/4270264.html
Copyright © 2011-2022 走看看