java面试(六)

1、单例模式

  • 懒汉、饿汉、双重校验锁、静态内部类
  • 代码分别如下
  • 懒汉模式
    线程不安全,延迟初始化,严格意义上不是不是单例模式
  • public class Singleton {  
        private static Singleton instance;  
        private Singleton (){}  
      
        public static Singleton getInstance() {  
        if (instance == null) {  
            instance = new Singleton();  
        }  
        return instance;  
        }  
    }
    

    饿汉模式
    线程安全,比较常用,但容易产生垃圾,因为一开始就初始化

     

    public class Singleton {  
        private static Singleton instance = new Singleton();  
        private Singleton (){}  
        public static Singleton getInstance() {  
        return instance;  
        }  
    }
    

    双重锁模式
    线程安全,延迟初始化。这种方式采用双锁机制,安全且在多线程情况下能保持高性能。

     

    public class Singleton {  
        private volatile static Singleton singleton;  
        private Singleton (){}  
        public static Singleton getSingleton() {  
        if (singleton == null) {  
            synchronized (Singleton.class) {  
            if (singleton == null) {  
                singleton = new Singleton();  
            }  
            }  
        }  
        return singleton;  
        }  
    }
    

    双重检查模式,进行了两次的判断,第一次是为了避免不要的实例,第二次是为了进行同步,避免多线程问题。由于singleton=new Singleton()对象的创建在JVM中可能会进行重排序,在多线程访问下存在风险,使用volatile修饰signleton实例变量有效,解决该问题。

    静态内部类单例模式

     

    public class Singleton { 
        private Singleton(){
        }
          public static Singleton getInstance(){  
            return Inner.instance;  
        }  
        private static class Inner {  
            private static final Singleton instance = new Singleton();  
        }  
    } 
    
    

    只有第一次调用getInstance方法时,虚拟机才加载 Inner 并初始化instance ,只有一个线程可以获得对象的初始化锁,其他线程无法进行初始化,保证对象的唯一性。目前此方式是所有单例模式中最推荐的模式,但具体还是根据项目选择。

  • 2、集合类的结构

  • Iterator、Collection(List、Set、Queue)、Map(HashTable、HashMap、LinkedHashMap、TreeMap)

3、HashMap底层结构,为什么长度是2的倍数,Put 的过程,get的过程,JDK 1.7 和 1.8 中 HashMap 的区别,为什么线程不安全

数组 + 链表。HashMap的主干是一个Entry数组。Entry是HashMap的基本组成单元,每一个Entry包含一个key-value键值对。

长度是2的倍数(原因):找索引时 key 的 hash 值与数组的长度值减 1 进行与运算,长度为 2 的倍数时能减少碰撞

Put 的过程:当我们往HashMap中put元素的时候,先根据key的hashCode重新计算hash值,根据hash值得到这个元素在数组中的位置(即下标), 但是当插入的Entry越来越多的时候,再完美的Hash函数也难免会出现下表索引冲突的情况,如果数组该位置上已经存放有其他元素了,那么在这个位置上的元素将以链表的形式存放,通过链表来解决下标冲突,主要是因为,HashMap数组中的每一个元素不仅仅是一个Entry对象,也是一个链表的头节点,每一个Entry对象通过上一个例子中的Next指针,指向它的下一个Entry节点。新加入的放在链头,最先加入的放在链尾(这种插入方法,也被称为“头插法”,至于为什么不是链表“尾插法”,主要是考虑到查询效率的问题,HashMap创建者认为,后插入的Entry被查询的可能性更大)。如果数组该位置上没有元素,就直接将该元素放到此数组中的该位置上。

get的过程:从HashMap中get元素时,首先计算key的hashCode,HashMap中要找到某个元素,需要根据key的hash值来求得对应数组中的位置。如何计算这个位置就是hash算法。找到数组中对应位置的某一元素后,然后通过key的equals方法在对应位置的链表中找到需要的元素。

JDK 1.7 和 1.8 中 HashMap 的区别:1.8 增加红黑树、头插变为尾插、扩容后元素位置要么在原位置,要么在原位置 + 扩容前旧容量

为什么线程不安全:扩容时链表可能形成闭环。

3、ConcurrentHashMap 和 HashMap 区别

*ConcurrentHashMap对整个桶数组进行了分割分段(Segment),然后在每一个分段上都用lock锁进行保护,相对于HashTable的syn关键字锁的粒度更精细了一些,并发性能更好,而HashMap没有锁机制,不是线程安全的。

*HashMap的键值对允许有null,但是ConCurrentHashMap都不允许

4、线程池工作流程

(1)线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。

(2)线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步

(3)线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略来处理这个任务

线程池饱和策略

这里提到了线程池的饱和策略,那我们就简单介绍下有哪些饱和策略:

AbortPolicy

为Java线程池默认的阻塞策略,不执行此任务,而且直接抛出一个运行时异常,切记ThreadPoolExecutor.execute需要try catch,否则程序会直接退出。

DiscardPolicy

直接抛弃,任务不执行,空方法

DiscardOldestPolicy

从队列里面抛弃head的一个任务,并再次execute 此task。

CallerRunsPolicy

在调用execute的线程里面执行此command,会阻塞入口

用户自定义拒绝策略(最常用)

实现RejectedExecutionHandler,并自己定义策略模式

5、线程池的类别和区别

(1)fixThreadPool  正规线程、定常的线程池

          我的理解这是一个有指定的线程数的线程池,有核心的线程,里面有固定的线程数量,响应的速度快。正规的并发线程,多用于服务器。固定的线程数由系统资源设置。

(2)CaCheThreadPool  缓存线程池

         只有非核心线程,最大线程数很大(Int.Max(values)),它会为每一个任务添加一个新的线程,这边有一个超时机制,当空闲的线程超过60s内没有用到的话,就会被回收。缺点就是没有考虑到系统的实际内存大小

(3)singleThreadPoll   单线程线程池

  看这个名字就知道这个家伙是只有一个核心线程,就是一个孤家寡人,通过指定的顺序将任务一个个丢到线程,都乖乖的排队等待执行,不处理并发的操作,不会被回收。确定就是一个人干活效率慢。

(4)ScheduledThreadPoll   周期性执行任务的线程池

         这个线程池就厉害了,是唯一一个有延迟执行和周期重复执行的线程池。它的核心线程池固定,非核心线程的数量没有限制,但是闲置时会立即会被回收

6、阻塞队列的类别和区别

ArrayBlockingQueue:一个由数组结构组成的有界阻塞队列。
·LinkedBlockingQueue:一个由链表结构组成的有界阻塞队列。
·PriorityBlockingQueue:一个支持优先级排序的无界阻塞队列。
·DelayQueue:一个使用优先级队列实现的无界阻塞队列。
·SynchronousQueue:一个不存储元素的阻塞队列。
·LinkedTransferQueue:一个由链表结构组成的无界阻塞队列。
·LinkedBlockingDeque:一个由链表结构组成的双向阻塞队列。

java面试 文章被收录于专栏

求职面试题&解析

全部评论

相关推荐

点赞 评论 收藏
分享
11-08 13:58
门头沟学院 Java
程序员小白条:竟然是蓝桥杯人才doge,还要花钱申领的offer,这么好的公司哪里去找
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务