精华内容
下载资源
问答
  • 最常见的Java面试题及答案汇总(一)和Java高级面试题整理(附答案),值得拥有,真的值得拥有,真的值得拥有,真的值得拥有。
  • Java高级面试题

    2017-07-21 11:01:00
    2017最新的java高级面试题,涵盖了很多高级技能介绍。
  • 为了大家面试顺利通过,特整理了一些常用的知识点资料,希望大家下载后,用心阅读,通过面试找到心仪的offer;
  • 多次面试亲身总结,其中多个问题被阿里,京东,腾讯多次问到;涵盖了缓存,数据库,JVM,Java基础等多方面,帮助自己更好的补齐知识短板
  • 2021Java大厂面试题——大厂真题之拼多多-Java高级.pdf
  • 大厂真题之腾讯-Java高级.pdf
  • java高级面试题十大总结 java高级面试题十大总结 java高级面试题十大总结 java高级面试题十大总结
  • java高级面试题

    2012-05-13 14:36:22
    java高级面试题,希望能帮到大家。里面有一份答案,大家可以对照查看。答案不准确的地方,希望大家指出来。
  • 总结java高级面试题

    万次阅读 多人点赞 2019-05-10 16:25:39
    Extension ClassLoader:被称为扩展类加载器,负责加载Java的扩展类库,Java虚拟机的实现会提供一个扩展目录,该类加载器在此目录里面查找并加载Java类   AppClassLoader:被称为系统类加载器,负责加载应用...

    jvm结构原理,GC工作原理

    Jvm结构:

                       

    Jvm主要包括四个部分:

    1、类加载器(ClassLoad)

    在JVM启动时或者在类运行时将需要的class加载到JVM中。

     

    类加载时间与过程:

    类从被加载到虚拟机内存开始,在到卸载出内存为止,正式生命周期包括了:加载,验证,准备,解析,初始化,使用和卸载7个阶段。其中验证、准备、解析这个三个步骤被统称为连接(linking)。

                                           

    其中,加载、验证、准备、初始化和卸载这五个阶段的顺序是确定的   ,类的加载过程必须按照这种顺序按部就班的“开始”(仅仅指的是开始,而非执行或者结束,因为这些阶段通常都是互相交叉的混合进行,通常会在一个阶段执行的过程中调用或者激活另一个阶段),而解析阶段则不一定(它在某些情况下可以在初始化阶段之后再开始,这是为了支持java语言的运行时绑定)

     

    在以下几种情况下,会对未初始化的类进行初始化:

    1. 创建类的实例
    2. 对类进行反射调用的时候,如果类没有进行过初始化,则需要先触发其初始化
    3. 当初始化一个类的时候,发现其父类没有被初始化,则需要先初始化父类
    4. 当虚拟机启动的时候,用户需要指定一个执行的主类,虚拟机会先初始化这个主类

     

    类实例化和类初始化是两个不同概念:

    类实例化:是指创建一个类的实例对象的过程

    类初始化:是指类中各个类成员(被static修饰的成员变量)赋初始值的过程,是类生命周期的一个过程

     

    ClassLoader的等级加载机制

     

    Java默认提供的三个ClassLoader

    BootStrap ClassLoader:被称为启动类加载机制,是Java类加载层次中最顶层的类加载器,负责加载JDK中核心类库。

    Extension  ClassLoader:被称为扩展类加载器,负责加载Java的扩展类库,Java虚拟机的实现会提供一个扩展目录,该类加载器在此目录里面查找并加载Java类

     

    AppClassLoader:被称为系统类加载器,负责加载应用程序classpath目录下的所有jar和class文件。一版来说,Java应用的类都  是由它来完成加载的。可以通过ClassLoader.getSystemClassLoader()来获取它。

     

    ClassLoader加载类的原理:

               ClassLoader使用的是双亲委托机制来搜索加载类的,每一个ClassLoader实例都有一个父类加载器的引用(不是继承关系,是组合关系),虚拟机内置的类加载器(Bootstrap ClassLoader)本身没有父类加载器,但可以用作其它的ClassLoadre实例的父类加载器。当一个ClassLoader实例需要加载某个类时,它会试图亲自搜索某个类之前,先把这个任务委托给它的父类加载器,这个过程是有上之下一次检查的。首先由最顶层的类加载器Bootstrap ClassLoader试图加载,如果没有加载到,则把任务转交给Extension  ClassLoader试图加载,如果没有加载到,则转交给AppClassLoader进行加载,如果它也没有加载到话,则发挥给委托的发起者,有它到指定的文件系统或网络等URL中加载该类,如果都没有加载到此类,那么抛出ClassNotFoundException异常。否则将这个找到的类生成一个类的定义,并将它加载到内存中,最后返回这个类的内存中Class对象。

    类的加载器双亲委托模型:

                                                                   

     

    2、运行时数据区(内存区)

    是在jvm运行的时候操作所分配的内存区。运行时内存区主要分为5个区域:

                                                                       

    方法区(Methd Area):用于存储类结构信息的地方,包括常量池,静态变量,构造函数。虽然JVM规范把方法区描述为堆的一个逻辑部分,但它却有个别名(non-heap 非堆)

                                                           

    Java堆(Heap):存储java实例或者对象的地方。这块是GC的主要区域。从存储内容上可以看到Java堆和方法区是被java线程共享的。

     

    Java栈(Stack):java栈总是和线程关联在一起,每当创建一个线程时,jvm就会为这个线程创建一个对应的java栈。在这个Java栈中又会包含多个帧栈,每运行一个方法就创建一个帧栈,由于存储局部变量,操作栈,方法返回值等。每一个方法从调用直至执行完成的过程,就对应一个帧栈在Java栈中入栈和出栈的过程。所以java栈是私有。

     

    程序计数器(PC Register):用于保存当前线程执行的内存地址。由于JVN程序是多线程执行的(线程轮转切换),所以为了保证线程切换回来,还能回复到原先状态,就需要一个独立的计数器,记录之前中断的地方,可见程序计数器也是线程私有的。

     

    本地方法栈(Native Method Stack):和Java栈的作用差不多,只不过是为JVM使用到的native方法服务的。

     

    3、执行引擎

    负责执行class文件中包含的字节码指令

    4、本地接口

    主要是调用CC++实现的本地方法及返回结果

     

    JVM内存分配:

    Java虚拟机是一次性分配一块较大的内存空间,然后每次new时都在该空间上进行分配和释放,减少了系统调用的次数,节省了一定的开销,这有点类似于内存池的概念。有了这块空间,如何进行分配和回收就跟GC机制有关系了。

    Java一般内存申请有两种:静态内存和动态内存。很容易理解,编译时就能够确定的内存就是静态内存,即内存是固定的,系统一次性分配。比如int类型变量;动态内存分配就是在程序执行时才知道要分配的存储空间大小,比如java对象的内存空间。综上所述:java栈、程序计数器、本地方法栈都是线程私有的,线程生就生,线程灭就灭,栈中的栈帧随着方法的结束也会撤销,内存自然就跟着回收了。所以几个地方的内存分配和回收是确定的,不需要管。但是java堆和方法区则不一样,我们只有在程序运行期间才知道会创建哪些对象,所以这部分内存分配和回收是动态的。一般说的垃圾回收也是针对这部分的。

     

    垃圾检测和回收算法

             垃圾收集器一般必须完成两件事:检测出垃圾,回收垃圾。检测垃圾一般分为以下几种:

     

    引用计数法:给对象增加一个引用计数器,每当有地方引用这个对象时,就在计数器上加1,引用失效就减1

    可达性分析算法:以根集对象为起始点进行搜索,如果有对象不可达的话,即是垃圾对象。这里的根集一般包括java堆中引用的对象,方法区常量池的引用对象。

     

    总之,jvm在做垃圾回收的时候,会检查堆中的所有对象是否会被这些根集对象引用,不能够被引用的对象就会被垃圾收集器回收。一般回收有一下几种方法:

             1、标记-清除(Mark-sweep):分为两个阶段,标记和清楚。标记所有需要回收的对象,然后统一回收这个是最基础的算法,后续的收集算法 都是基于这个算法扩展的。

             不足:效率低;标记清楚之后会产生大量的碎片。

             2、复制(copying): 此算法把内存空间划分为两个相等的区域,每次只使用其中一个区域。垃圾回收时,遍历当前区域,把正在使用中的对象复制到另外区域中。此算法每次只处理正在使用中的对象,因此复制成本比较小,同时复制过去还能进行相应的内存整理,不会出现“内存碎片”问题。当然,此算法的缺点也是很明显,就是需要双倍内存。

             3、标记-整理(Mark-Compact):此算法结合了“标记-清除”和“复制”两个算法的有点。也是两个阶段,第一阶段从根节点开始标记所被引用的对象。第二阶段遍历整个堆,把存活对象“压缩”到堆的其中一块,按顺序排放。此算法避免了“复制”算法的空间问题

             4、分代收集算法:这是当前商业虚拟机常用的垃圾收集算法。分代的垃圾回收策率,是基于这样一个事实:不同的对象的生命周期不一样的。因此,不同生命周期的对象采取不同的收集方式,以便提高回收效率。

            

    为什么要运用分代垃圾回收策率?在java程序运行的过程中,会产生大量的对象,因每个对象所能承担的职责和功能不同,所以也有着不同的生命周期。有的对象生命周期较长,有的对象生命周期较短。试想,在不进行对象存活时间区分的情况下,每次垃圾回收都是对整个堆空间进行回收,那么消耗的时间相对比较长,而对于存活时间较长的对象进行扫描的工作是徒劳的。因此就引入了分治思想,所以分治思想就是因地制宜,将对象进行代划分,把不同的生命周期的对象放在不同的代上使用不同的垃圾回收方法。

     

    如果划分?将对象按其生命周期的不同划分成:年轻代(Young Generation)、老年代(Old Generation)、持久代(Permanent Generation)。其中持久代主要存放的是类信息,所以与java对象的回收关系不大,与回收信息相关的是年轻代,老年代。

     

    年轻代:是所有新对象产生的地方。年轻代被分为3个部分:Ender(出生)区和两个Survivor(幸存者)区(From和To)。当Ender区被对象填满时,就会执行Minor GC。并把所有存活下来的对象转移到其中一个Survivor区(假设为from区)。Minor GC同样会检查存活下来的对象,并把他们转移到另一个Survivor区(假设为to区)。这样在一段时间内总会有一个空的Survivor区。经过多次GC后,仍然存活下来的对象会被转移到老年代内存空间。

     

    年老带:在年轻代经过N次回收的仍然没有被清除的对象会放到老年代,可以说它们是久经沙场而不亡的一代,都是生命周期较长的对象。对于老年代和永久代,就不能再采用年轻代中那样搬移腾挪的回收算法,因为那些对于这些回收战场上的老兵来说是小儿科,通常会在老年代内存被占满时将会触发Full GC,回收整个堆内存。

     

    持久代:用于存放静态文件,比如Java类,方法等。持久代对垃圾回收没有显著的影响。

                                                                             

    我这里之所以最后讲分代,是因为分代里涉及了前面几种算法。年轻代:涉及了复制算法;年老代:涉及了标记-整理(Mark-Sweep的算法。

     

    Java线程池

    java中的ThreadPoolExecutor类

    ThreadPoolExecutor类继承了AbstractExecutorService类,并提供了四个构造器。构造器中分别有一下参数:

    1. corePoolSize:核心池的大小,在创建线程池后,默认情况下,线程池中并没有任何线程,而是等待有任务到来才创建线程去执行任务,除非调用了prestartAllCoreThreads()或者prestartCoreThread()方法来预创建线程。即在没有任务到来之前就创建corePoolSize个线程或者一个线程。默认情况下,在创建了线程池后,线程池中的线程数为0,当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。
    2. maximumPoolSize:线程池最大的线程数,表示在线程池中最多能创建多少个线程;
    3. keepAliveTime:表示线程没有任务执行时最多保持多久时间会终止。默认情况下,只有当线程池中的线程数大于corePoolSize时,keepAliveTime才会起作用,直到线程池中的线程数不大于corePoolSize。即当线程池中的线程数大于corePoolSize时,如果一个线程空闲时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,在线程池中的线程数不大于corePoolSize时,keepAliveTime参数也会起作用,知道线程池中的线程数为0。
    4. unit:参数keepAliveTime的时间单位,有7中取值,在TimeUnit类中有7中静态属性:
      1. TimeUnit.DAYS;               //天
      2. TimeUnit.HOURS;             //小时
      3. TimeUnit.MINUTES;           //分钟
      4. TimeUnit.SECONDS;           //秒
      5. TimeUnit.MILLISECONDS;      //毫秒
      6. TimeUnit.MICROSECONDS;      //微妙
      7. TimeUnit.NANOSECONDS;       //纳秒
    5. workQueue:一个阻塞队列,用来存储等待执行的任务,这个参数的选择也很重要,会对线程池的运行过程产生重大影响,一版来说阻塞队列有以下几种:
      1. ArrayBlockingQueue
      2. LinkedBlockingQueue
      3. SynchronousQueue

    ArrayBlockingQueue使用较少,一使用LinkedBlokingQueue和SynchronousQueue。线程池排队策率与BlockingQueue有关

    1. threadFactory:线程工厂,主要用来创建线程;
    2. hander:表示当拒绝处理任务时的策率,有一下四种取值:
      1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
      2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
      3. ThreadPoolExecutor.DiscardOlddestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
      4. ThreadPoolExecutor.CallerRunsPolicy:有调用线程处理该任务

     

    ThreadPoolExecutor继承了AbstractExecutorService抽象类,AbstractExecutorService抽象类实现了ExecutorService接口,ExecutorService接口继承了Executor接口。

     

    Executor:是一个顶层接口,在它里面只有一个方法execute(Runnable),返回值为void,参数为Runnable类型,其实就是为了执行传进来的任务;

    然后ExecutorService接口继承了Executor接口,并生命了一些方法:submit、invokeAll、invokeAny以及shutDown等。

    抽象类AbstractExecutorService实现了ExecutorService接口,基本实现了ExecutorService接口中所有的方法;

    然后ThreadPoolExecutor继承了抽象类AbstractExecutorService;

    在ThreadPoolExecutor类中有几个非常重要的方法:

    1. execute():实际上是Executor中声明的方法,在ThreadPoolExecutor进行了具体的实现,这个方法是ThreadPoolExecutor的核心方法,通过这个方法可以向线程池提交一个任务,交线程池去执行。
    2. submit():此方法是在ExecutorService中声明的方法,在AbstractExecutorService就已经有了具体的实现,在ThreadPoolExecutor中并没有对其进行重写,这个方法也是用来向线程池提交任务的,但是它和executor()方法不同,它能够返回任务执行的结果,去看submit()方法的方法实现,会发现它实际上还是调用的execute()方法,只不过它利用了Futrure来获取任务的执行结果
    3. shutDown():是用来关闭线程
    4. shutDownNow():是用来关闭所有线程

     

    深入剖析线程池实现的原理图

    深入解析一下线程池的具体实现原理,将从下面几个方面讲解:

    1. 线程池状态

    在ThreadPoolExecutor中定义了一个volatile变量,另外定义了几个static final变量表示线程池的各个状态:

    volatile in runSize;

    static final int Running = 0;

    static final int shutdown = 1;

    static final int stop = 2;

    static final int terminated=3;

     

    runSize:表示当前线程池状态,它是一个volatile变量用来保证线程之间的可见性

    下面几个static final int 变量表示runSize可能的几个取值

    当创建线程池后,初始时,线程池处于Running状态;

    如果调用了shutdown()方法,则线程池处于shutdown状态,此时线程池不能够接受新的任务,它会等待所有任务执行完毕;

    如果滴啊用shutdownNow()方法,则线程池处于stop状态,此时线程池不能接受新的任务,并且会尝试终止正在执行的任务;

    当线程池处于shutdown或者stop状态,并且所有工作线程已经销毁,任务缓存队列已经清空或执行结束后,线程池被设置为terminated状态

     

    1. 任务的执行

    ThreadPoolExecutor类中其他的一些比较重要的成员变量:

    private final BlockingQueue<Runnable> workQueue; //任务缓存队列,用来存放等待执行的任务

    private final ReentrantLock mainLock = new ReentrantLock();//线程池的主要状态锁,对线程池状态(比如线程池大小等)的改变都要使用这个锁

    private final HashSet<Worker> workers = new HashSet<Worker>(); //用来存放工作集

    private volatile long  keepAliveTime;    //线程存货时间

    private volatile boolean allowCoreThreadTimeOut;   //是否允许为核心线程设置存活时间

    private volatile int   corePoolSize;   //核心池的大小(即线程池中的线程数目大于这个参数时,提交的任务会被放进任务缓存队列)

    private volatile int   maximumPoolSize;   //线程池最大能容忍的线程数

    private volatile int   poolSize;       //线程池中当前的线程数

    private volatile RejectedExecutionHandler handler; //任务拒绝策略

    private volatile ThreadFactory threadFactory;   //线程工厂,用来创建线程

    private int largestPoolSize;   //用来记录线程池中曾经出现过的最大线程数

    private long completedTaskCount;   //用来记录已经执行完毕的任务个数

     

    任务提交给线程池之后处理的策略,主要有一下四点:

    如果当前线程池中线程数目小于corePoolSize,则每来一个线程,就会创建一个线程去执行这个任务;

    如果当前线程池中线程数据大于等于corePoolSize,则没来一个任务,会尝试将其添加到任务缓存队列当中,若添加成功,则该任务会等待空闲线程将其取出去执行;若添加失败(一版来说是任务缓存队列已满),则会尝试尝试创建新的线程去执行这个任务;

    如果当前线程池中的线程数目达到maximumPoolSize,则会采取任务拒绝策率进行处理;

    如果线程池中的线程数量大于corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止,直至线程池中的线程数据不大于corePoolSize;如果允许核心池中的线程设置存活时间,那么核心池中的线程空闲时间超过keepAliveTime,线程也会被终止

     

    1. 线程池中的线程初始化

    默认情况下,创建线程池之后,线程池中是没有线程的,需要提交任务之后才会创建线程。在实际中如果需要线程池创建之后立即创建线程,可以通过下面两种方法:

    prestartCoreThread():初始化一个核心线程

    prestartAllCoreThread():初始化所有核心线程

     

    1. 任务缓存队列及排队策略

    任务缓存队列,即workQueue,它用来存放等待执行的任务

    workQueue的类型为BlockingQueue<Runnable>,通常可以取下面三种类型:

    ArrayBlockingQueue:基于数组的先进先出队列,此队列创建时必须制定大小

    LinkedBlockingQueue:基于链表的先进先出队列,如果创建时没有指定此队列的大小,那么默认为Integer.MAX_VALUE

    synchronousQueue:这个队列比较特殊,他不会保存提交的任务,而是将直接新建的一个线程来执行新的任务。

    1. 任务拒绝策略

    当线程池的任务缓存队列已满并且线程池中的线程数目达到maximumPoolSize,如果还有任务到来就会采取任务拒绝策略,通常通过一下四种策略:

    1. ThreadPoolExecutor.AbortPolicy:丢弃任务并抛出RejectedExecutionException异常
    2. ThreadPoolExecutor.DiscardPolicy:也是丢弃任务,但是不抛出异常
    3. ThreadPoolExecutor.DiscardOlddestPolicy:丢弃队列最前面的任务,然后重新尝试执行任务(重复此过程)
    4. ThreadPoolExecutor.CallerRunsPolicy:有调用线程处理该任务

     

    1. 线程池的关闭

    ThreadPoolExecutor提供了两个方法,用于线程池的关闭,分别为shutDown()和shutDownNow(),其中:

    shutDown():不会立即终止线程池,而是要等所有任务缓存队列中的任务都执行完后才终止,但再也不会执行新的任务

    shutDownNow():立即终止线程池,并尝试打断正在执行的任务,并且清空任务缓存队列,返回尚未执行的任务

    1. 线程池容量的动态调动

    ThreadPoolExecutor提供了动态调用线程池容量大小的方法:setCorePoolSize()和setMaximumPoolSize(),

    setCorePool():设置核心池大小

    setMaximumPoolSize():设置线程池最大能创建的线程数目大小

    当上述参数从小变大时,ThreadPoolExecutor进行线程赋值,还可能立即创建新的线程来执行任务

    使用示例

    package ThreadPoolExecutor;

     

    public class MyTask implements Runnable {

        private int taskNum;

       

        public MyTask(int num) {

            this.taskNum = num;

        }

     

        @Override

        public void run() {

             System.out.println("正在执行task"+taskNum);

             try {

                Thread.currentThread().sleep(4000);

            } catch (InterruptedException e) {

                // TODO Auto-generated catch block

                e.printStackTrace();

            }

             System.out.println("task" + taskNum + "执行完毕");

        }

     

    }

     

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ArrayBlockingQueue;

    import java.util.concurrent.ThreadPoolExecutor;

    import java.util.concurrent.TimeUnit;

     

    public class Test {

        public static void main(String[] args) {

            ThreadPoolExecutor executor = new ThreadPoolExecutor(5, 10, 200, TimeUnit.MILLISECONDS,

                    new ArrayBlockingQueue<Runnable>(5));

            for(int i = 0 ; i < 15 ; i ++){

                MyTask mytask = new MyTask(i);

                executor.execute(mytask);

                 System.out.println("线程池中线程数目:"+executor.getPoolSize()+",队列中等待执行的任务数目:"+

                         executor.getQueue().size()+",已执行玩别的任务数目:"+executor.getCompletedTaskCount());

            }

            executor.shutdown();

        }

    }

    执行结果:

    正在执行task0

    线程池中线程数目:1,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    线程池中线程数目:2,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task1

    线程池中线程数目:3,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task2

    线程池中线程数目:4,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task3

    线程池中线程数目:5,队列中等待执行的任务数目:0,已执行玩别的任务数目:0

    正在执行task4

    线程池中线程数目:5,队列中等待执行的任务数目:1,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:2,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:3,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:4,已执行玩别的任务数目:0

    线程池中线程数目:5,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    线程池中线程数目:6,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    正在执行task10

    正在执行task11

    线程池中线程数目:7,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    线程池中线程数目:8,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    线程池中线程数目:9,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    正在执行task13

    正在执行task12

    线程池中线程数目:10,队列中等待执行的任务数目:5,已执行玩别的任务数目:0

    正在执行task14

    task0执行完毕

    正在执行task5

    task1执行完毕

    正在执行task6

    task2执行完毕

    正在执行task7

    task3执行完毕

    正在执行task8

    task4执行完毕

    正在执行task9

    task11执行完毕

    task10执行完毕

    task13执行完毕

    task14执行完毕

    task12执行完毕

    task5执行完毕

    task6执行完毕

    task9执行完毕

    task8执行完毕

    task7执行完毕

     

    从执行结果可以看出,当线程池中的线程的数目大于5时,便将任务放入缓存队列里面,当任务缓存队列满了之后,便创建新的线程。如果上面程序中,将for循环中改成执行20个任务,就会抛出任务拒绝异常了;

     

    在java doc中,并不建议直接使用ThreadPoolExecutor直接创建线程池,而是使用Executor类中提供的几个静态方法来创建线程池:

    Executor.newCachedThread()://创建一个缓冲池,缓冲池大小为Integer.MAX_VALUE

    Executor.newSingleThreadExecutor()://创建容量为1的缓冲池

    Executor.newFixedThreadPool()://创建固定容量的缓冲池

     

    以下是这个三个静态方法的具体实现:

    public static ExecutorService newFixedThreadPool(int nThreads) {

        return new ThreadPoolExecutor(nThreads, nThreads,

                                      0L, TimeUnit.MILLISECONDS,

                                      new LinkedBlockingQueue<Runnable>());

    }

    public static ExecutorService newSingleThreadExecutor() {

        return new FinalizableDelegatedExecutorService

            (new ThreadPoolExecutor(1, 1,

                                    0L, TimeUnit.MILLISECONDS,

                                    new LinkedBlockingQueue<Runnable>()));

    }

    public static ExecutorService newCachedThreadPool() {

        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                      60L, TimeUnit.SECONDS,

                                      new SynchronousQueue<Runnable>());

    }

     

    从它们的具体实现来看,他们实际上也是调用了ThreadPoolExecutor,只不过参数都已经配置好了,

     

    newFixedThreadPool创建的线程池corePoolSize和maximumPoolSize值是相等的,它使用的LinkedBlockingQueue;

    newSingleThreadPool将corePoolSize和maximumPoolSize的值都设置为1,也使用的LinkedBlockingQueue;

    newCachedThreadPool将corePoolSize设置为0,将maximumPoolSize设置为Integer.MAX_VALUE,使用SynchronousQueue,也就是说来了任务就创建线程运行,当线程空闲超过60秒,就销毁线程

     

    实际工作中,如果Executors提供的三种静态方法如果能够满足要求,就尽量使用它提供的三个方法,因为自己手动配置ThreadPoolExecutor的参数有点麻烦,要根据实际任务的类型和数据量来进行配置。如果ThreadPoolExecutor达不到要求,可以自己继承ThreadPoolExecutor类进行重写

     

    如何合理配置线程池的大小

    一般需要根据任务的类型来配置线程池大小;

    如果是CPU密集型任务,就需要压榨CPU,参考值可以设为Ncpu+1

    如果是IO密集型任务,参考值可以设为2*Ncpu

    当然,这只是一个参考值,具体的设置还需要根据实际情况进行调整,比如可以先将线程池大小设置为参考值,再观察任务运行情况和系统负载、资源利用率来进行适当调整。

     

    Java四种线程池

    线程池的优势:

    1. 重用存在的线程,减少对象的创建,消亡的内存开销,性能佳
    2. 可有效控制最大并发线程数,提高系统资源使用率,同时避免过多资源竞争,避免堵塞
    3. 提供定时执行,定期执行,单线程,并发数控制功能。

    Executors提供的四种线程池:

    1. newCachedThreadPool:创建一个可缓存线程池,如果线程池长度超过处理需求,可灵活回收空线程,若无可回收的线程,则新建线程

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

     

    public class CachedThreadPool {

             public static void main(String[] args) {

                       ExecutorService cachedThreadPool = Executors.newCachedThreadPool();            

                       for(int i = 0 ; i < 10 ; i++ ){

                                final int index = 1;

                                try {

                                         Thread.sleep(index * 1000);

                                } catch (InterruptedException e) {

                                         // TODO Auto-generated catch block

                                         e.printStackTrace();

                                }                          

                                cachedThreadPool.execute(new Runnable(){

                                         @Override

                                         public void run() {

                                                   System.out.println(index);

                                         }                                   

                                });

                       }

                       cachedThreadPool.shutdown();//立即终止线程池

             }

    }

    线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不是每次新增线程

     

    1. newFixedThreadPool:创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

     

    public class FixedThreadPool {

             public static void main(String[] args) {

                       ExecutorService fixedThreadPool  = Executors.newFixedThreadPool(3);

                       for(int i = 0 ; i < 10 ; i ++){

                                final int index = 1;

                                fixedThreadPool.execute(new Runnable(){

                                         @Override

                                         public void run() {

                                                   System.out.println(index);

                                                   try {

                                                            Thread.sleep(2000);

                                                   } catch (InterruptedException e) {

                                                            // TODO Auto-generated catch block

                                                            e.printStackTrace();

                                                   }

                                         }

                                });

                       }       

    fixedThreadPool.shutdown();

             }

    }

    因为线程池大小为3,每个任务输出index后sleep2秒,所以每两秒打印三个数字;

     

    1. newScheduledThreadPool:创建一个定长线程池,支持定时及周期性任务执行

    package ThreadPoolExecutor;

     

    import java.util.concurrent.Executors;

    import java.util.concurrent.ScheduledExecutorService;

    import java.util.concurrent.TimeUnit;

     

    public class ScheduledThreadPool {

             public static void main(String[] args) {

                       ScheduledExecutorService  scheduledThreadPool = Executors.newScheduledThreadPool(5);

                       scheduledThreadPool.schedule(new Runnable(){

                                @Override

                                public void run() {

                                         System.out.println("这是一个延迟线程");

                                }

                               

                       }, 3, TimeUnit.SECONDS);// 表示延迟3秒执行

                      

                       scheduledThreadPool.scheduleAtFixedRate(new Runnable(){

                                @Override

                                public void run() {

                                         System.out.println("delay 1 seconds, and excute every 3 seconds");

                                }

                               

                       }, 1, 2, TimeUnit.SECONDS);// 表示延迟1秒后每3秒执行一次

                      

                       //scheduledThreadPool.shutdown();

             }

    }

     

    1. newSingleThreadPool:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定的顺序(FIFO,LIFO)执行

     

    package ThreadPoolExecutor;

     

    import java.util.concurrent.ExecutorService;

    import java.util.concurrent.Executors;

     

    public class singleThreadExecutor {

      public static void main(String[] args) {

          ExecutorService singleThreadExecutor = Executors.newSingleThreadExecutor();

          for(int i = 0 ; i < 10; i++){

              final int index = 1;

              singleThreadExecutor.execute(new Runnable(){

     

                  @Override

                  public void run() {

                      System.out.println(index);

                      try {

                          Thread.sleep(2000);

                      } catch (InterruptedException e) {

                          // TODO Auto-generated catch block

                          e.printStackTrace();

                      }

                  }

                 

              });

          }

          singleThreadExecutor.shutdown();

      }

    }

    结果依次输出,相当于顺序执行各个任务

     

    线程池的作用:

    线程池是为了限制系统中执行线程的数量

             根据系统环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其它线程排队等候。一个任务执行完毕,再从队列中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

             为什么要用线程池:

    1. 减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务
    2. 可以根据系统的承受能力,调整线程池中工作线程的数目,防止因为消耗过多的内存,增加服务器的压力(每个线程大约需要1MB的内存,线程开的越多,消耗内存越大,最后导致司机);

    Java里面线程池的顶级接口是Executor,但是严格意义上将Executor并不是一个线程池,而是一个执行线程的工具。真正的线程池接口是ExecutorService.

             比较重要的几个类:

             Executor:真正的线程池接口

             ScheduledExecutorService:能和Timer/TimerTask类似,解决那些需要任务重复执行的问题

             ThreadPoolService:ExecutorService默认实现类

             ScheduledExecutorPoolExecutor:继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

    要配置一个线程池是复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是最优的,因此在Executors类里面提供了一个静态工厂,生成一些常用的线程池。

     

    1. newSingleThreadExecutor

    创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程在串行执行所有的任务。如果这个唯一的线程因为异常信息中断,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

    1. newFixedThreadPool

    创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程

    1. newCachedThreadPool

    创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么回收空闲(60秒不执行)的线程,当任务增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

    1. newScheduledThreadPool

    创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

     

    一个任务通过execute(Runnable)方法被添加到线程池,任务就是一个Runnable类型的对象,任务的执行方法就是Runnable类型对象的run()方法

    当一个任务通过execute(Runnable)方法欲添加到线程池时:

    如果此时线程池中的数据小于corePoolSize,即使线程池中的线程处于空闲状态,也要创建新的线程来处理被添加的任务。

    如果此时线程池中的数据等于 corePoolSize,但是缓冲队列workQueue未满,那么任务被放入缓冲队列

    如果此时线程池中的数据大于 corePoolSize,缓冲队列workQueue已满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。

    如果此时线程池中的数量大于 corePoolSize,缓冲队列workQueue已满,并且线程池中的数量等于maximumPoolSize,那么通过handler所指定的策略来处理此任务。

     

    处理任务的优先级:

    核心线程corePoolSize,任务队列workQueue,最大线程maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    展开全文
  • 拼多多一面 首先自我介绍 参加过哪些项目 并发编程三要素? 实现可见性的方法有哪些?...一面面试题答案: 拼多多二面 自我介绍 什么是工厂模式? 如何实现单链表的增删操作? 让我说意思JVM的分为哪几块

    拼多多一面

    首先自我介绍
    参加过哪些项目
    并发编程三要素?
    实现可见性的方法有哪些?
    多线程的价值?
    创建线程的三种方式的对比?
    画出线程的状态流转图
    常用的并发工具类有哪些?
    CyclicBarrier 和 CountDownLatch 的区别
    CAS 的问题:1、CAS 容易造成 ABA 问题2、不能保证代码块的原子性3、CAS 造成 CPU 利用率增加
    ReadWriteLock 是什么
    一面面试题答案

    拼多多二面

    自我介绍
    什么是工厂模式?
    如何实现单链表的增删操作?
    让我说意思JVM的分为哪几块?
    由于提及到了内存溢出,面试官问我内存溢出和内存泄漏的区别?
    这里问完我就去问了数据库,4大特性是啥,举个例子?
    数据的隔离级别有啥,每个隔离级别举个例子?
    分布式锁的实现知道到哪些?
    你更推荐哪一种锁?Redis的持久化操作有哪些?
    如何利用redis处理热点数据
    sleep 方法和 wait 方法有什么区别?
    synchronized 和 ReentrantLock 的区别
    线程 B 怎么知道线程 A 修改了变量
    如果你提交任务时,线程池队列已满,这时会发生什么
    线程类的构造方法、静态块是被哪个线程调用的
    二面面试题答案

    拼多多三面

    照例自我介绍和项目介绍;
    上来就让我手撕一个单例模式
    让我讲了讲代码是啥啥意思?
    由于我的项目中提及到JVM,所以给我出了一个场景题,垃圾会收器中,标记清除多次后,由于采用的是标记清除算法,那么你觉得可能会出现什么问题?
    这里提及到了full gc,问我,哪些情况会产生full GC,哪些情况产生minor GC?
    除了你项目中的内存溢出问题,你还知道哪些关于内存溢出内存泄漏的?
    然后给我出了一个动态规划的手写代码题,说来写个代码吧。
    三面面试答案

    拼多多HR面

    自我介绍一下吧
    你为什么选择拼多多?
    你最大的优点和缺点是什么
    谈谈你对公司加班的看法
    谈谈你的朋友对你的评价是什么
    平时怎么去学习的?
    你遇到的比较难应付的事情?
    你感觉做的比较成功的事情?
    想象一下你几十年后是怎样的生活?
    觉得最痛苦的一段时间
    工作节奏你觉得可以接受吗?
    你有什么想问的?

    以上就是我拼多多面试的题目 基本上我都清晰的记录了。

    总结

    我个人认为,如果你想靠着背面试题来获得心仪的offer,用癞蛤蟆想吃天鹅肉形容完全不过分。想必大家能感受到面试越来越难,想找到心仪的工作也是越来越难,高薪工作羡慕不来,却又对自己目前的薪资不太满意,工作几年甚至连一个应届生的薪资都比不上,终究是错付了,错付了自己没有去提升技术。

    这些面试题分享给大家的目的,其实是希望大家通过大厂面试题分析自己的技术栈,给自己梳理一个更加明确的学习方向,当你准备好去面试大厂,你心里有底,大概知道面试官会问多广,多深,避免面试的时候一问三不知。

    大家可以把Java基础,JVM,并发编程,MySQL,Redis,Spring,Spring cloud等等做一个知识总结以及延伸,再去进行操作,不然光记是学不会的,这里我也提供一些脑图分享给大家:

    希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!

    1624222327049)]

    希望你看完这篇文章后,不要犹豫,抓紧学习,复习知识,准备在明年的金三银四拿到心仪的offer,加油,打工人!

    领取资料只需要点击这里即可免费获取全部资料!

    展开全文
  • 最新Java高级面试题

    千次阅读 2021-06-08 16:40:25
    面试准备 不论是校招还是社招都避免不了各种面试、笔试,如何去准备这些东西就显得格外重要。 运筹帷幄之后,决胜千里之外!不打毫无准备的仗,我觉得大家可以先从下面几个方面来准备面试: 1. 自我介绍。(介绍自己...

    面试准备

    不论是校招还是社招都避免不了各种面试、笔试,如何去准备这些东西就显得格外重要。 运筹帷幄之后,决胜千里之外!不打毫无准备的仗,我觉得大家可以先从下面几个方面来准备面试:

    1. 自我介绍。(介绍自己的项目经历以及一些特长而不是简单自我介绍喜好等)

    2. 自己面试中可能涉及哪些知识点、那些知识点是重点。

    3. 面试中哪些问题会被经常问到、面试中自己该如何回答。

    4. 自己的简历该如何写。

    “80%的offer掌握在20%的人手中” 这句话也不是不无道理的。决定你面试能否成功的因素中实力固然占有很大一部 分比例,但是如果你的心态或者说运气不好的话,依然无法拿到满意的 offer。运气暂且不谈,就拿心态来说,千万 不要因为面试失败而气馁或者说怀疑自己的能力,面试失败之后多总结一下失败的原因,后面你就会发现自己会越来 越强大。

    另外,大家要明确的很重要的几点是:

    1. 写在简历上的东西一定要慎重,这可能是面试官大量提问的地方;

    2. 将自己的项目经历完美的展示出来非常重要。

    并发编程共享模型篇

    • 并发编程概览
    • 进程与线程
    • Java线程
    • 共享模型之管程
    • 共享模型之内存
    • 共享模型之无锁
    • 共享模型之不可变
    • 共享模型之工具

    共享模型之管程

    原理之 Monitor(锁)

    原理之伪共享

    模式篇—正确姿势

    • 同步模式之保护性智停
    • 同步模式之Blking
    • 同步模式之顺
    • 序控制
    • 异步模式之生产者消费者
    • 异步模式之工作线程.
    • 终止模式之两阶段终止模式
    • 线程安全单例
    • 享元模式

    同步模式之保护性暂停

    应用篇—结合实际

    • 效率
    • 限制
    • 互斥
    • 同步和异步
    • 缓存
    • 分治
    • 统筹
    • 定时

    缓存更新策略

    原理篇—了然于胸

    • 指令级并行原理
    • CPU缓存结构原理
    • volatile原理
    • fnal原理
    • Monitor原理
    • synchronized原理
    • synchronized原理进阶
    • wait notify原理
    • join原理
    • park unpark原理
    • AQS原理
    • ReentrantLock原理
    • 读写锁原理
    • Semaphore原理
    • CourentHashMap,原理
    • LnkedlockingQueue原理
    • ConcurenL nkedQueue原理

    指令级并行原理

    CPU 缓存结构原理

    CPU 缓存一致性

    独家面经总结,超级精彩

    本人面试腾讯,阿里,百度等企业总结下来的面试经历,都是真实的,分享给大家!

    image

    image

    image

    image

    Java面试准备

    准确的说这里又分为两部分:

    1. Java刷题
    2. 算法刷题

    Java刷题:此份文档详细记录了千道面试题与详解;

    image

    image

    为两部分:

    1. Java刷题
    2. 算法刷题

    Java刷题:此份文档详细记录了千道面试题与详解;

    [外链图片转存中…(img-gP9nonVu-1623141614981)]

    [外链图片转存中…(img-bojX0q0C-1623141614981)]

    以上所有文档已经打包好,只需要动动手指点击【转发+关注】,然后点击即可免费获取

    展开全文
  • 2018最新最全java高级工程师面试题,2018最新最全java高级工程师面试题2018最新最全java高级工程师面试题,2018最新最全java高级工程师面试题 十几个文档
  • Spring已经是我们Java Web开发必不可少的一个框架,其大大简化了我们的开发,提高了开发者的效率。同时,其源码对于开发者来说也是宝藏,从中我们可以学习到非常优秀的设计思想以及优雅的命名规范,但因其体系庞大、...

    前言

    Spring已经是我们Java Web开发必不可少的一个框架,其大大简化了我们的开发,提高了开发者的效率。同时,其源码对于开发者来说也是宝藏,从中我们可以学习到非常优秀的设计思想以及优雅的命名规范,但因其体系庞大、设计复杂对于刚开始阅读源码的人来说是非常困难的。所以在此之前首先你得下定决心,不管有多困难都得坚持下去;其次,最好先把设计模式掌握熟练;然后在开始阅读源码时一定要多画UML类图和时序图,多问自己为什么要这么设计?这样设计的好处是什么?还有没有更好的设计?当然,晕车是难免的,但还是那句话,一定要持之以恒
    image.png

    初识Redis

    Redis和memcached的区别

    Redis常见的异常以及解决方案

    1.缓存穿透
    2.缓存雪崩
    3.缓存预热
    4.缓存降级

    分布式环境下常见的应用场景

    Redis集群

    以及:

    如何解决 Redis 的并发竞争 Key 问题?
    如何保证缓存与数据库双写时的数据一致性?

    整理不易,觉得有用的可以帮忙转发一下这篇文章,让更多有需要的人看到

    读者福利

    由于篇幅过长,就不展示所有面试题了,感兴趣的小伙伴

    关注+点赞后,点击这里获取完整面试题(含答案)!

    35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

    35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

    35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

    更多笔记分享

    [外链图片转存中…(img-lbwLAonn-1620570648601)]

    更多笔记分享

    35K成功入职:蚂蚁金服面试Java后端经历!「含面试题+答案」

    展开全文
  • java高级工程师-面试题及答案.docx
  • 不容错过的Java高级面试题

    千次阅读 2020-12-30 15:45:05
    又到跨年之际,想必在这一年技术成长颇多的猿友们为备战金三银四而蠢蠢欲动了吧。...面试题基于Java全栈,参照个人简历的技术栈由浅至深询问。故建议猿友们在简历上写自己hold得住的技能,切莫画蛇添.
  • Java高级面试题小结

    2020-08-29 10:49:56
    本文主要介绍了一些在Java面试过程中的一些高级面试题,包括对部分名词的释义及代码解析,具有一定收藏价值,需要的朋友可以参考下
  • 据说凭自己能力回答对7个以上问题的同学,能得到见到神龙,并给你进入阿里巴巴的正确指引,不信来战!!
  • Java面试题大全(备战2021)

    千次下载 2020-12-01 22:25:02
    这本面试手册包含了Java基础、Java集合、JVM、Spring、Spring Boot、Spring Cloud、Mysql、Redis、RabbitMQ、Dubbo、Netty、分布式及架构设计等方面的技术点。内容难度参差,满足初中高级Java工程师的面试需求。
  • JAVA高级面试题——2019

    万次阅读 多人点赞 2019-07-13 16:28:02
    实现 Java list,要求实现 list 的 get(), add(), remove() 三个功能函数,不能直接使用 ArrayList、LinkedList 等 Java 自带高级类(阿里面试题)**加粗样式** 数据结构与算法精选面试题[添加链接描述]...
  • JAVA高级面试题汇总

    千次阅读 2019-04-04 22:44:33
    JAVA面试中问及HIBERNATE与 MYBATIS的对比,在这里做一下总结 hibernate以及mybatis都有过学习,在 java面试 中也被提及问道过,在项目实践中也应用过,现在对hibernate和mybatis做一下对比,便于大家更好的理解...
  • 亲自收集的java高级程序员面试题
  • 【面试题】-java高级面试题汇总

    千次阅读 2019-05-20 18:44:45
    花大笔墨整理的一些java高级程序猿必会的面试题,其中有很多都是大厂面试题. 目录 1.spring IOC原理 2.spring bean生命周期 3.spring aop原理 4.spring mvc原理 5.Mybatis原理 6.servlet原理 7.netty原理 8....
  • java高级面试题2019

    千次阅读 多人点赞 2019-05-16 09:36:25
    1、HashMap实现原理 ...6、Java集合面试题及答案总结 7、Java代码优化(上) 8、Java代码优化(下) 9、史上最有用的java面试题整理 10、线程池工作原理,任务拒接策略有哪几种 11、虚拟机JVM 组...
  • 2019年一线互联网公司Java高级面试题总结 面试很有用!
  • 大厂真题之携程-Java高级.pdf
  • 10道Java高级必备的Netty面试题 10道Java高级必备的Netty面试题 10道Java高级必备的Netty面试题 10道Java高级必备的Netty面试题
  • 2018java面试题

    2018-01-30 10:22:27
    压缩包里包含了面试过程中经常问到的知识点,还包含了同事面试时遇到的面试题,并且有详细的答案,各大框架的搭建步骤等等,面试必备~
  • 常见Java高级面试题

    2018-12-26 11:45:17
    常见的Java高级面试题,掌握它,你能pass掉70%的对手
  • java高级面试题及答案

    千次阅读 2021-06-22 11:03:34
    java线程同步都有哪几种方式,在 Java 中 CycliBarriar 和 CountdownLatch 有什么区别? cas的原理,变量要用哪个关键字修饰,volatile实现的原理。 你在多线程环境中遇到的常见的问题是什么?你是怎么解决它的? ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 65,173
精华内容 26,069
关键字:

java高级面试题

java 订阅