分类: Java学习

  • 百度移动端一面

    多态的3个条件:继承、方法重写、父类引用指向子类对象

    软引用(SoftReference)和弱引用(WeakReference):活到内存不足和下一次垃圾回收

    sleep和wait区别

  • JVM垃圾回收

    可达性分析算法

    以 GC Roots 为起始点进行搜索,可达的对象都是存活的,不可达的对象可被回收。Java 虚拟机使用该算法来判断对象是否可被回收,GC Roots 一般包含以下内容:

    • 虚拟机栈中局部变量表中引用的对象
    • 本地方法栈中 JNI 中引用的对象
    • 方法区中类静态属性引用的对象
    • 方法区中的常量引用的对象

    安全点(Safe Point)与安全区域(Safe Region)

    而主动式中断的思想是当垃圾收集需要中断线程的时候,不直接对线程操作,仅仅简单地设置一 个标志位,各个线程执行过程时会不停地主动去轮询这个标志,一旦发现中断标志为真时就自己在最 近的安全点上主动中断挂起。轮询标志的地方和安全点是重合的,另外还要加上所有创建对象和其他 需要在Java堆上分配内存的地方,这是为了检查是否即将要发生垃圾收集,避免没有足够内存分配新 对象。由于轮询操作在代码中会频繁出现,这要求它必须足够高效。HotSpot使用内存保护陷阱的方式, 把轮询操作精简至只有一条汇编指令的程度。

    记忆集(Remembered Set)与卡表(Card Table)

    Serial(新生代)/Serial Old(老生代) 收集器

    它只会使用一个处理器或一条收集线程去完成垃圾收集工作,更重要的是强调在它进行垃圾收集时,必须暂停其他所有工作线程,直到它收集结束。

    Serial/Serial Old收集器运行示意图

    ParNew(新生代) 收集器

    ParNew收集器实质上是Serial收集器的多线程并行版本,除了同时使用多条线程进行垃圾收集之外,其余的行为包括Serial收集器可用的所有控制参数、收集算法、Stop The World、对象分配规则、回收策略等都与Serial收集器完全一致,在实现上这两种收集器也共用了相当多的代码。除了Serial收集器外,目前只有它能与CMS 收集器配合工作。

    ParNew/Serial Old收集器运行示意图

    Parallel Scavenge(新生代)收集器

    Parallel Scavenge收集器也是一款新生代收集器,它同样是基于标记-复制算法实现的收集器,也是 能够并行收集的多线程收集器。Parallel Scavenge收集器的特点是它的关注点与其他收集器不同,CM S等收集器的关注点是尽可能 地缩短垃圾收集时用户线程的停顿时间,而Parallel Scavenge收集器的目标则是达到一个可控制的吞吐 量(Throughput)。所谓吞吐量就是处理器用于运行用户代码的时间与处理器总消耗时间的比值。

  • BIO, NIO与AIO的区别

    IO的方式通常分为几种,同步阻塞的BIO、同步非阻塞的NIO、异步非阻塞的AIO。

    一、BIO

     在JDK1.4出来之前,我们建立网络连接的时候采用BIO模式,需要先在服务端启动一个ServerSocket,然后在客户端启动Socket来对服务端进行通信,默认情况下服务端需要对每个请求建立一堆线程等待请求,而客户端发送请求后,先咨询服务端是否有线程响应,如果没有则会一直等待或者遭到拒绝请求,如果有的话,客户端线程会等待请求结束后才继续执行。

    二、NIO

    NIO本身是基于事件驱动思想来完成的,其主要想解决的是BIO的大并发问题: 在使用同步I/O的网络应用中,如果要同时处理多个客户端请求,或是在客户端要同时和多个服务器进行通讯,就必须使用多线程来处理。也就是说,将每一个客户端请求分配给一个线程来单独处理。这样做虽然可以达到我们的要求,但同时又会带来另外一个问题。由于每创建一个线程,就要为这个线程分配一定的内存空间(也叫工作存储器),而且操作系统本身对线程的总数也有一定的限制。如果客户端的请求过多,服务端程序可能会因为不堪重负而拒绝客户端的请求,甚至服务器可能会因此而瘫痪。
    
    NIO基于Reactor,当socket有流可读或可写入socket时,操作系统会相应的通知应用程序进行处理,应用再将流读取到缓冲区或写入操作系统。  也就是说,这个时候,已经不是一个连接就要对应一个处理线程了,而是有效的请求,对应一个线程,当连接没有数据时,是没有工作线程来处理的。

    BIO与NIO一个比较重要的不同,是我们使用BIO的时候往往会引入多线程,每个连接一个单独的线程;而NIO则是使用单线程或者只使用少量的多线程,多个连接共用一个线程。

      NIO的最重要的地方是当一个连接创建后,不需要对应一个线程,这个连接会被注册到多路复用器上面,所以所有的连接只需要一个线程就可以搞定,当这个线程中的多路复用器进行轮询的时候,发现连接上有请求的话,才开启一个线程进行处理,也就是一个请求一个线程模式。
    
      在NIO的处理方式中,当一个请求来的话,开启线程进行处理,可能会等待后端应用的资源(JDBC连接等),其实这个线程就被阻塞了,当并发上来的话,还是会有BIO一样的问题。

      HTTP/1.1出现后,有了Http长连接,这样除了超时和指明特定关闭的http header外,这个链接是一直打开的状态的,这样在NIO处理中可以进一步的进化,在后端资源中可以实现资源池或者队列,当请求来的话,开启的线程把请求和请求数据传送给后端资源池或者队列里面就返回,并且在全局的地方保持住这个现场(哪个连接的哪个请求等),这样前面的线程还是可以去接受其他的请求,而后端的应用的处理只需要执行队列里面的就可以了,这样请求处理和后端应用是异步的.当后端处理完,到全局地方得到现场,产生响应,这个就实现了异步处理。

    三、AIO

     与NIO不同,当进行读写操作时,只须直接调用API的read或write方法即可。这两种方法均为异步的,对于读操作而言,当有流可读取时,操作系统会将可读的流传入read方法的缓冲区,并通知应用程序;对于写操作而言,当操作系统将write方法传递的流写入完毕时,操作系统主动通知应用程序。  即可以理解为,read/write方法都是异步的,完成后会主动调用回调函数。  在JDK1.7中,这部分内容被称作NIO.2,主要在java.nio.channels包下增加了下面四个异步通道:

    AsynchronousSocketChannel
    AsynchronousServerSocketChannel
    AsynchronousFileChannel
    AsynchronousDatagramChannel
    其中的read/write方法,会返回一个带回调函数的对象,当执行完读取/写入操作后,直接调用回调函数。

    BIO是一个连接一个线程。

    NIO是一个请求一个线程。

    AIO是一个有效请求一个线程。

    先来个例子理解一下概念,以银行取款为例:

    同步 : 自己亲自出马持银行卡到银行取钱(使用同步IO时,Java自己处理IO读写);
    异步 : 委托一小弟拿银行卡到银行取钱,然后给你(使用异步IO时,Java将IO读写委托给OS处理,需要将数据缓冲区地址和大小传给OS(银行卡和密码),OS需要支持异步IO操作API);
    阻塞 : ATM排队取款,你只能等待(使用阻塞IO时,Java调用会一直阻塞到读写完成才返回);
    非阻塞 : 柜台取款,取个号,然后坐在椅子上做其它事,等号广播会通知你办理,没到号你就不能去,你可以不断问大堂经理排到了没有,大堂经理如果说还没到你就不能去(使用非阻塞IO时,如果不能读写Java调用会马上返回,当IO事件分发器会通知可读写时再继续进行读写,不断循环直到读写完成)
    Java对BIO、NIO、AIO的支持:

    Java BIO : 同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。

    Java NIO : 同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

    Java AIO(NIO.2) : 异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理,

    BIO、NIO、AIO适用场景分析:

    BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。

    NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。

    AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

    另外,I/O属于底层操作,需要操作系统支持,并发也需要操作系统的支持,所以性能方面不同操作系统差异会比较明显。

    在高性能的I/O设计中,有两个比较著名的模式Reactor和Proactor模式,其中Reactor模式用于同步I/O,而Proactor运用于异步I/O操作。

    一般来说I/O模型可以分为:同步阻塞,同步非阻塞,异步阻塞,异步非阻塞IO

    同步阻塞IO:在此种方式下,用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行。JAVA传统的IO模型属于此种方式!

    同步非阻塞IO:在此种方式下,用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费。JAVA的NIO就属于同步非阻塞IO。

    异步阻塞IO:此种方式下是指应用发起一个IO操作以后,不等待内核IO操作的完成,等内核完成IO操作以后会通知应用程序,这其实就是同步和异步最关键的区别,同步必须等待或者主动的去询问IO是否完成,那么为什么说是阻塞的呢?因为此时是通过select系统调用来完成的,而select函数本身的实现方式是阻塞的,而采用select函数有个好处就是它可以同时监听多个文件句柄,从而提高系统的并发性!

    异步非阻塞IO:在此种模式下,用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了。Java AIO属于这种异步非阻塞模型。
    ————————————————
    版权声明:本文为CSDN博主「涂有」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/ty497122758/article/details/78979302

  • 排序算法学习

    package com.dominic;
    import edu.princeton.cs.algs4.StdRandom;
    public class Sort {
        private static boolean less(Comparable x, Comparable y)
        { return x.compareTo(y) < 0; }
        private static void exchange(Comparable[] a, int i, int j)
        {    Comparable t = a[i]; a[i] = a[j]; a[j] = t;}
        public static void show(Comparable[] a){
            for (Comparable comparable : a)
                System.out.print(comparable + " ");
            System.out.println();
        }
        public static boolean isSorted(Comparable[] a){
            for (int i = 1;i < a.length;i++)
                if(less(a[i],a[i-1]))
                    return false;
                return true;
        }
        public static void selection(Comparable[] a){
            int N = a.length;
            for (int i = 0; i < N; i++) {
                int min = i;
                for (int j = i + 1; j < N; j++)
                    if (less(a[j], a[min])) min = j;
                    exchange(a,i,min);
            }
        }
        public static void insertion(Comparable[] a){
            int N = a.length;
            for (int i=1;i<N;i++){
                for (int j = i;j>0&&less(a[j],a[j-1]);j++)
                    exchange(a,j,j-1);
            }
        }
        public static void shell(Comparable[] a){
            int N = a.length;
            int h = 1;
            while (h < N/3)
                h = 3*h+1;
            while (h >= 1){
                for (int i = h;i < N;i++){
                    for(int j = i;j>=h && less(a[j],a[j-h]);j-=h)
                        exchange(a,j,j-h);
                }
                h = h/3;
            }
        }
        public static void merge(Comparable[] a){
            Comparable[] aux = new Comparable[a.length];
            _merge_sort(a, aux,0, a.length-1);
        }
        private static void _merge_sort(Comparable[] a, Comparable[] aux, int lo, int hi){
            if (hi <= lo) return;
            int mid = lo + (hi - lo)/2;
            _merge_sort(a, aux, lo, mid);
            _merge_sort(a, aux, mid+1, hi);
            _merge(a, aux, lo, mid, hi);
        }
        private static void _merge(Comparable[] a, Comparable[] aux, int lo, int mid, int hi){
            int i = lo, j = mid + 1;
            if (hi + 1 - lo >= 0) System.arraycopy(a, lo, aux, lo, hi + 1 - lo);
            for (int k = lo; k <= hi; k++){
                if      (i>mid)               a[k]=aux[j++];
                else if (j>hi)                a[k]=aux[i++];
                else if (less(aux[j],aux[i])) a[k]=aux[j++];
                else                          a[k]=aux[i++];
            }
        }
        public static void quick(Comparable[] a){
            StdRandom.shuffle(a);
            _quick_sort(a, 0, a.length-1);
        }
        private static void _quick_sort(Comparable[] a, int lo, int hi){
            if (hi <= lo) return;
            int j = _partition(a, lo, hi);
            _quick_sort(a, lo, j-1);
            _quick_sort(a, j+1, hi);
        }
        private static int _partition(Comparable[] a, int lo, int hi){
            int i = lo, j = hi + 1;
            Comparable v = a[lo];
            while(true){
                while(less(a[++i],v)) if(i==hi) break;
                while(less(v,a[--j])) if(j==lo) break;
                if(i >= j) break;
                exchange(a, i, j);
            }
            exchange(a, lo, j);
            return j;
        }
        public static void quick3way(Comparable[] a){
            StdRandom.shuffle(a);
            _quick3way_sort(a, 0, a.length-1);
        }
        private static void _quick3way_sort(Comparable[] a, int lo, int hi){
            if (hi<=lo) return;
            int lt = lo, i = lo + 1, gt = hi;
            Comparable v = a[lo];
            while(i <= gt){
                int cmp = a[i].compareTo(v);
                if (cmp<0) exchange(a, lt++, i++);
                else if (cmp > 0) exchange(a, i, gt--);
                else i++;
            }
            _quick3way_sort(a, lo, lt-1);
            _quick3way_sort(a, gt+1, hi);
        }
    }
    
  • Java关键字

    下面列出了 Java 关键字。这些保留字不能用于常量、变量、和任何标识符的名称。

    类别关键字说明
    访问控制private私有的
    protected受保护的
    public公共的
    类、方法和变量修饰符abstract声明抽象
    class
    extends扩充,继承
    implements实现(接口)
    interface接口
    native本地,原生方法(非 Java 实现)
    new新,创建
    static静态
    strictfp严格,精准
    synchronized线程,同步
    transient短暂
    volatile易失
    程序控制语句break跳出循环
    case定义一个值以供 switch 选择
    continue继续
    default默认
    do运行
    else否则
    for循环
    if如果
    instanceof实例
    return返回
    switch根据值选择执行
    while循环
    错误处理assert断言表达式是否为真
    catch捕捉异常
    finally有没有异常都执行
    throw抛出一个异常对象
    throws声明一个异常可能被抛出
    try捕获异常
    包相关import引入
    package
    基本类型boolean布尔型
    byte字节型
    char字符型
    double双精度浮点
    float单精度浮点
    int整型
    long长整型
    short短整型
    变量引用super父类,超类
    this本类
    void无返回值
    保留关键字goto是关键字,但不能使用
    const是关键字,但不能使用
    null