您的位置 首页 java

「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

本文为 Java 面试题系列文章第四篇,之前文章可点击访问:

一、 「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」「BAT常问」40道Java基础面试题及详细答案整理汇总「开篇」

二、 「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」「BAT常问」40道Java基础面试题及详细答案整理汇总「1—8」

三、 「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」「BAT常问」40道Java基础面试题及详细答案整理汇总「9—16」

17.ConcurrentHashMap可以代替 Hashtable 吗?


hashTable虽然性能上比不上ConcurrentHashMap,但并不能完全被取代,两者的 迭代器 的一致性不同的,hash table的迭代器是强一致性的,而concurrenthashmap是弱一致的。

ConcurrentHashMap的get,clear,iterator 都是弱一致性的。 Doug Lea(大神 Doug Lea是JCP 中的一员) 也将这个判断留给用户自己决定是否使用ConcurrentHashMap。

ConcurrentHashMap与HashTable都可以用于 多线程 的环境,但是当Hashtable的大小增加到一定的时候,性能会急剧下降,因为迭代时需要被锁定很长的时间。因为ConcurrentHashMap引入了分割( Segment ation),不论它变得多么大,仅仅需要锁定map的某个部分,而其它的线程不需要等到迭代完成才能访问map。简而言之,在迭代的过程中,ConcurrentHashMap仅仅锁定map的某个部分,而Hashtable则会锁定整个map。

那么既然ConcurrentHashMap那么优秀,为什么还要有Hashtable的存在呢?ConcurrentHashMap能完全替代HashTable吗?

HashTable虽然性能上不如ConcurrentHashMap,但并不能完全被取代, 两者的迭代器的一致性不同的,HashTable的迭代器是强一致性的,而ConcurrentHashMap是弱一致的
ConcurrentHashMap的get,clear,iterator 都是弱一致性的。

那么什么是强一致性和弱一致性呢?

get方法是弱一致的,是什么含义?可能你期望往Concurrent HashMap 底层数据结构中加入一个元素后,立马能对get可见,但ConcurrentHashMap并不能如你所愿。换句话说,put操作将一个元素加入到底层数据结构后,get可能在某段时间内还看不到这个元素,若不考虑内存模型,单从代码逻辑上来看,却是应该可以看得到的。

我们结合java内存模型相关内容来分析下put/get方法。put方法我们只需关注Segment#put,get方法只需关注Segment#get,在继续之前,先要说明一下Segment里有两个volatile变量:count和table;HashEntry里有一个volatile变量:value。

总结

ConcurrentHashMap的弱一致性主要是为了提升效率,是一致性与效率之间的一种权衡。要成为强一致性,就得到处使用锁,甚至是全局锁,这就与Hashtable和同步的HashMap一样了。

「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

18.为什么说HashMap是线程不安全的呢?


HashMap 在并发执行 put 操作时会引起死循环,导致 CPU 利用率接近100%。因为多线程会导致 HashMap 的 Node 链表形成环形数据结构,一旦形成环形数据结构,Node 的 next 节点永远不为空,就会在获取 Node 时产生死循环。

19.在使用HashMap时,如何做到 线程安全


了解了 HashMap 为什么线程不安全,那现在看看如何线程安全的使用 HashMap。

这个无非就是以下三种方式:

Hashtable ConcurrentHashMap Synchronized Map

Hashtable例子代码:

「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

先稍微吐槽一下,为啥命名不是 HashTable 啊,看着好难受不管了就装作它叫HashTable 吧。因为已经不常用了,就简单说说吧。HashTable 源码中是使用synchronized来保证线程安全的,比如下面的 get 方法和 put 方法:

「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

所以当一个线程访问 HashTable 的同步方法时,其他线程如果也要访问同步方法,会被阻塞住。举个例子,当一个线程使用 put 方法时,另一个线程不但不可以使用 put 方法,连 get 方法都不可以,这样效率很低,现在基本不会使用它了。

ConcurrentHashMap

ConcurrentHashMap 于 Java 7 的,和8有区别,在8中 CHM 摒弃了 Segment(锁段)的概念,而是启用了一种全新的方式实现,利用 CAS 算法。

SynchronizedMap

synchronizedMap() 方法后会返回一个 SynchronizedMap 类的对象,而在 SynchronizedMap 类中使用了 synchronized 同步关键字来保证对 Map 的操作是线程安全的。

性能对比

这是要靠数据说话的时代,所以不能只靠嘴说 CHM 快,它就快了。写个测试用例,实际的比较一下这三种方式的效率(源码来源),下面的代码分别通过三种方式创建 Map 对象,使用 ExecutorService 来并发运行5个线程,每个线程添加/获取500K个元素。

「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

ConcurrentHashMap 性能是明显优于 Hashtable 和 SynchronizedMap 的,CHM 花费的时间比前两个的一半还少。

20.多并发情况下HashMap是否还会产生死循环?


看下了ConcurrentHashMap的源码,ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现。

在看很多博客在介绍ConcurrentHashMap之前,都说HashMap适用于单线程访问,这是因为HashMap的所有方法都没有进行锁同步,因此是线程不安全的,不仅如此,当多线程访问的时候还容易产生死循环。

建议参考博客文章《Java困惑》:多并发情况下HashMap是否还会产生死循环。讲的很详细,百度很好找到。

既然会产生死循环,为什么并发情况下,还是用ConcurrentHashMap。 jdk 好像有,但是Jdk8 已经修复了这个问题。

21.讲一讲TreeMap、HashMap、LindedHashMap的区别


LinkedHashMap 可以保证HashMap集合有序,存入的顺序和取出的顺序一致。

TreeMap 实现SortMap接口,能够把它保存的记录根据键排序,默认是按键值的升序排序,也可以指定排序的比较器,当用Iterator遍历TreeMap时,得到的记录是排过序的。

HashMap 不保证顺序,即为无序的,具有很快的访问速度。 HashMap 最多只允许一条记录的键为Null;允许多条记录的值为 Null。 HashMap 不支持线程的同步。

我们在开发的过程中 使用HashMap比较多,在Map中在Map 中插入、删除和定位元素,HashMap 是最好的选择

但如果您要 按自然顺序或自定义顺序遍历键 ,那么 TreeMap会更好

如果 需要输出的顺序和输入的相同 ,那么 用LinkedHashMap 可以实现,它还可以按读取顺序来排列

「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

22.说说 Collection 包的结构,和Collections的区别在哪里?


Collection 是集合类的上级接口,子接口主要有Set、List 、Map。

Collecions 是针对集合类的一个帮助类, 提供了操作集合的工具方法,一系列静态方法实现对各种集合的搜索、排序线性、线程安全化等操作。

Collection

Collection 是单列集合

List

元素是有序的、可重复。
有序的 collection,可以对列表中每个元素的插入位置进行精确地控制。
可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。
可存放重复元素,元素存取是有序的。

List接口中常用类

Vector:线程安全,但速度慢,已被ArrayList替代。底层数据结构是数组结构。
ArrayList:线程不安全,查询速度快。底层数据结构是数组结构。
LinkedList:线程不安全。增删速度快。底层数据结构是列表结构。

Set

Set接口中常用的类

Set(集) 元素无序的、不可重复。
取出元素的方法只有迭代器。不可以存放重复元素,元素存取是无序的。

HashSet:线程不安全,存取速度快。它是如何保证元素唯一性的呢?依赖的是元素的hashCode方法和euqals方法。
TreeSet:线程不安全,可以对Set集合中的元素进行排序。它的排序是如何进行的呢?通过compareTo或者compare方法中的来保证元素的唯一性。元素是以二叉树的形式存放的。

Map

map是一个双列集合

Hashtable :线程安全,速度快。底层是 哈希表 数据结构。是同步的。不允许null作为键,null作为值。

Properties :用于配置文件的定义和操作,使用频率非常高,同时键和值都是字符串。是集合中可以和IO技术相结合的对象。

HashMap :线程不安全,速度慢。底层也是哈希表数据结构。是不同步的。允许null作为键,null作为值,替代了Hashtable。

LinkedHashMap : 可以保证HashMap集合有序。存入的顺序和取出的顺序一致。

TreeMap :可以用来对Map集合中的键进行排序

23.用到try catch finally时,如果try里有return,finally里边的代码还执行么?


肯定会执行。finally{}块的代码。
只有在try{}块中包含遇到 System.exit(0)
之类的导致Java虚拟机直接退出的语句才会不执行。

当程序执行try{}遇到return时,程序会先执行return语句,但并不会立即返回——也就是把return语句要做的一切事情都准备好,也就是在将要返回、但并未返回的时候,程序把执行流程转去执行finally块,当finally块执行完成后就直接返回刚才return语句已经准备好的结果。

「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

24.说说Excption与Error包结构,OOM你遇到过哪些情况?SOF你遇到过哪些情况?


Throwable是 Java 语言中所有错误或异常的超类。
Throwable包含两个子类: Error 和 Exception 。它们通常用于指示发生了异常情况。
Throwable包含了其线程创建时线程执行堆栈的快照,它提供了printStackTrace()等接口用于获取堆栈跟踪数据等信息。

Java将可抛出(Throwable)的结构分为三种类型:

被检查的异常(Checked Exception)。

运行时异常(RuntimeException)。

错误(Error)。

运行时异常RuntimeException

定义 : RuntimeException及其子类都被称为运行时异常。
特点 : Java编译器不会检查它 也就是说,当程序中可能出现这类异常时,倘若既”没有通过throws声明抛出它”,也”没有用try-catch语句捕获它”,还是会编译通过。

例如,除数为零时产生的ArithmeticException异常,数组越界时产生的IndexOutOfBoundsException异常,fail-fail机制产生的ConcurrentModificationException异常等,都属于运行时异常。

堆内存溢出 OutOfMemoryError(OOM)

除了程序计数器外,虚拟机内存的其他几个运行时区域都有发生OutOfMemoryError(OOM)异常的可能。

Java Heap 溢出。
一般的异常信息:java.lang.OutOfMemoryError:Java heap spacess。
java堆用于存储对象实例,我们只要不断的创建对象,并且保证GC Roots到对象之间有可达路径来避免垃圾回收机制清除这些对象,就会在对象数量达到最大堆容量限制后产生内存溢出异常。

堆栈溢出 StackOverflow (SOF)

StackOverflowError 的定义:
当应用程序递归太深而发生堆栈溢出时,抛出该错误。
因为栈一般默认为1-2m,一旦出现死循环或者是大量的递归调用,在不断的压栈过程中,造成栈容量超过1m而导致溢出。

栈溢出的原因

1、递归调用

2、大量循环或死循环

3、全局变量是否过多

4、数组、List、map数据过大

文章来源:智云一二三科技

文章标题:「BAT常问」40道Java基础面试题及详细答案整理汇总「17—24」

文章地址:https://www.zhihuclub.com/199667.shtml

关于作者: 智云科技

热门文章

网站地图