关于一些基础的Java问题的解答(三)
11. HashMap和ConcurrentHashMap的区别
在JDK1.5中,伟大的Doug Lea给我们带来了concurrent包,从此我们有线程安全的ConcurrentHashMap用了。
- public V put(K key, V value) {
- return putVal(key, value, false);
- }
- final V putVal(K key, V value, boolean onlyIfAbsent) {
- if (key == null || value == null) throw new NullPointerException();
- int hash = spread(key.hashCode());
- int binCount = 0;
- for (Node<K,V>[] tab = table;;) {
- Node<K,V> f; int n, i, fh;
- if (tab == null || (n = tab.length) == 0)
- tab = initTable();
- else if ((f = tabAt(tab, i = (n - 1) & hash)) == null) {
- if (casTabAt(tab, i, null,
- new Node<K,V>(hash, key, value, null)))
- break; // no lock when adding to empty bin
- }
- else if ((fh = f.hash) == MOVED)
- tab = helpTransfer(tab, f);
- else {
- V oldVal = null;
- synchronized (f) {
- if (tabAt(tab, i) == f) {
- if (fh >= 0) {
- binCount = 1;
- for (Node<K,V> e = f;; ++binCount) {
- K ek;
- if (e.hash == hash &&
- ((ek = e.key) == key ||
- (ek != null && key.equals(ek)))) {
- oldVal = e.val;
- if (!onlyIfAbsent)
- e.val = value;
- break;
- }
- Node<K,V> pred = e;
- if ((e = e.next) == null) {
- pred.next = new Node<K,V>(hash, key,
- value, null);
- break;
- }
- }
- }
- else if (f instanceof TreeBin) {
- Node<K,V> p;
- binCount = 2;
- if ((p = ((TreeBin<K,V>)f).putTreeVal(hash, key,
- value)) != null) {
- oldVal = p.val;
- if (!onlyIfAbsent)
- p.val = value;
- }
- }
- }
- }
- if (binCount != 0) {
- if (binCount >= TREEIFY_THRESHOLD)
- treeifyBin(tab, i);
- if (oldVal != null)
- return oldVal;
- break;
- }
- }
- }
- addCount(1L, binCount);
- return null;
- }
来看一些关键的代码:在第2行,方法检测了key和value是否为空,如果为空则抛出NullPointerException(看来线程安全的key和value都必须非空,和HashTable一样)。然后在第9行构造了一个Node对象,这个对象代表的是键值对节点(内部有next指针指向下一个节点)的实体。在ConcurrentHashMap内部维护了一个Node对象的数组,它大小是2的指数,且是volatile的具有原子可见性,数组索引是key经过哈希函数得出的哈希值:
- /**
- * The array of bins. Lazily initialized upon first insertion.
- * Size is always a power of two. Accessed directly by iterators.
- */
- transient volatile Node<K,V>[] table;
看回putVal方法,从第12行的注释我们可以看出,如果是往一个空的索引位置放入一个新的Node节点,则不需要加锁。再看到方法第18行,我们发现这里有个临界区,此时处理的是往一个已有节点的索引位置加入新的节点情况,那么在链表完成之前很明显我们不应该让其他新节点干扰我们的工作,因此此处为索引头的Node对象加了锁,但此时别的索引位置是不加锁的。
看完了put方法,我们再来看看get方法:
- public V get(Object key) {
- Node<K,V>[] tab; Node<K,V> e, p; int n, eh; K ek;
- int h = spread(key.hashCode());
- if ((tab = table) != null && (n = tab.length) > 0 &&
- (e = tabAt(tab, (n - 1) & h)) != null) {
- if ((eh = e.hash) == h) {
- if ((ek = e.key) == key || (ek != null && key.equals(ek)))
- return e.val;
- }
- else if (eh < 0)
- return (p = e.find(h, key)) != null ? p.val : null;
- while ((e = e.next) != null) {
- if (e.hash == h &&
- ((ek = e.key) == key || (ek != null && key.equals(ek))))
- return e.val;
- }
- }
- return null;
- }
可以看到由于volatile关键字保证了原子可见性,get方法是完全没有加锁的。
- HashMap允许key和value为空值,ConcurrentHashMap不允许
- HashMap的put和remove不加锁,不是线程安全的,而ConcurrentHashMap加锁,是线程安全的
- 两者的get方法都没有加锁,但HashMap的Node数组不具有volatile关键字
- /**
- * Stripped-down version of helper class used in previous version,
- * declared for the sake of serialization compatibility
- */
- static class Segment<K,V> extends ReentrantLock implements Serializable {
- private static final long serialVersionUID = 2249069246763182397L;
- final float loadFactor;
- Segment(float lf) { this.loadFactor = lf; }
- }
12. TreeMap、HashMap、LinkedHashMap的区别
LinkedHashMap:
- public class LinkedHashMap<K,V>
- extends HashMap<K,V>
- implements Map<K,V>
其与HashMap最大的不同在于内部维护了一个用于遍历的双向链表,遍历的时候能够保持元素插入的顺序:
- /**
- * The head (eldest) of the doubly linked list.
- */
- transient LinkedHashMap.Entry<K,V> head;
- /**
- * The tail (youngest) of the doubly linked list.
- */
- transient LinkedHashMap.Entry<K,V> tail;
TreeMap:
- /**
- * The comparator used to maintain order in this tree map, or
- * null if it uses the natural ordering of its keys.
- *
- * @serial
- */
- private final Comparator<? super K> comparator;
- private transient Entry<K,V> root;
13. Collection包结构,与Collections的区别
Collection:
- 按元素插入顺序存储的List
- 不允许元素重复的Set
- 先进先出的Queue
- 后进先出的Stack
Collections:
14. try catch finally,try里有return,finally还执行么?
- public class Test {
- public static void main(String[] args) {
- try {
- System.out.println("try");
- return;
- } catch (Exception e) {
- } finally {
- System.out.println("finally");
- }
- }
- }
结果:
15. Excption与Error包结构,OOM遇到的情况
- Error是无法处理的异常,比如OutOfMemoryError,一般发生这种异常,JVM会选择终止程序。因此我们编写程序时不需要关心这类异常。
- Exception,也就是我们经常见到的一些异常情况,比如NullPointerException、IndexOutOfBoundsException,这些异常是我们可以处理的异常。
对于unchecked exception(非检查异常),也称运行时异常(RuntimeException),比如常见的NullPointerException、IndexOutOfBoundsException,java编译器不要求必须进行异常捕获处理或者抛出声明,由程序员自行决定。(可以处理,也可以不处理)
对于checked exception(检查异常),也称非运行时异常(运行时异常以外的异常就是非运行时异常),java编译器强制程序员必须进行捕获处理,比如常见的IOExeption和SQLException。对于非运行时异常如果不进行捕获或者抛出声明处理,编译都不会通过。
关于OOM:
- 堆溢出:堆是JVM存放对象实例的地方,如果我们产生的对象过多,JVM又没有及时的GC,就会突破最大堆容量限制从而发生OOM
- 操作栈或本地方法栈溢出:如果线程在拓展栈时无法申请到足够的内存,也会发生OOM
- 方法区溢出:方法区存储了JVM中的常量,静态变量和类信息等信息,一个类如果要被垃圾收集器回收,判定条件是很苛刻的,因此在经常动态生成大量Class也可能发生OOM
关于一些基础的Java问题的解答(三)的更多相关文章
- 关于一些基础的Java问题的解答(一)
学习一门语言基础是非常重要的,因此本文总结了一些常见的Java基础问题的解答,希望可以帮到大家. 1. 九种基本数据类型的大小,以及他们的封装类. 9种基本数据类型 基本类型 包装类型 大小 bool ...
- 关于一些基础的Java问题的解答(七)
31. 反射的作用与原理 简单的来说,反射机制其实就是指程序在运行的时候能够获取自身的信息.如果知道一个类的名称或者它的一个实例对象, 就能把这个类的所有方法和变量的信息(方法名,变量名,方法,修饰符 ...
- 关于一些基础的Java问题的解答(六)
26. ThreadPool用法与优势 ThreadPool即线程池,它是JDK1.5引入的Concurrent包中用于处理并发编程的工具.使用线程池有如下好处: 降低资源消耗:通过重复利用已创建的线 ...
- 关于一些基础的Java问题的解答(四)
16. Java面向对象的三个特征与含义 java中的面向对象的三大基本特征分别是:封装.继承.多态: 封装:把过程和数据包围起来,对数据的访问只能通过已定义的界面,主要是方便类的修改 继承:对象的一 ...
- 关于一些基础的Java问题的解答(五)
21. 实现多线程的两种方法:Thread与Runable 在Java中实现多线程编程有以下几个方法: 1.继承Thread类,重写run方法 public class Test { public s ...
- 关于一些基础的Java问题的解答(二)
6. Hashcode的作用 官方对于hashCode的解释如下: Whenever it is invoked on the same object more than once during an ...
- 黑马程序员:Java基础总结----java注解
黑马程序员:Java基础总结 java注解 ASP.Net+Android+IO开发 . .Net培训 .期待与您交流! java注解 lang包中的基本注解 @SuppressWarnings ...
- java面试题—精选30道Java笔试题解答(二)
摘要: java面试题-精选30道Java笔试题解答(二) 19. 下面程序能正常运行吗() public class NULL { public static void haha(){ System ...
- Java基础:Java的四种引用
在Java基础:java虚拟机(JVM)中,我们提到了Java的四种引用.包括:强引用,软引用,弱引用,虚引用.这篇博客将详细的讲解一下这四种引用. 1. 强引用 2. 软引用 3. 弱引用 4. 虚 ...
随机推荐
- EasyUI 数据网格行过滤
前段时间发现一个GridView很有用的功能,可以筛选数据,实现起来很简单 一.添加一个引用,这个可以自己去网上下载 <script type="text/javascript&quo ...
- JS银行取款流程
假设一个简单的ATM机的取款过程是这样的:首先提示用户输入密码(password),最多只能输入三次,超过3次则提示用户"密码错误,请取卡"结束交易.如果用户密码正确,再提示用户 ...
- python/零起点(一、字典)
python/零起点(一.字典) dict( )字典 dict()强型转换成字典类型的数据类型: 字典的键(Key)必须是唯一不可变的 字典是无序,字典可变数据类型,且是可迭代的对象 字典清空操作案例 ...
- mysql中出现Unknown column 'qwe' in 'field list'的错误
下面是我建表的代码 输入数据的代码 可以看到出现了类似Unknown column 'qwe' in 'field list'的错误 当时看了好久改了又改都没有找到错误,直道我在一次打C语言代码的过程 ...
- POJ-2253 Frogger---最短路变形&&最大边的最小值
题目链接: https://vjudge.net/problem/POJ-2253 题目大意: 青蛙A想访问青蛙B,必须跳着石头过去,不幸的是,B所在的石头太远了,需要借助其他的石头,求从A到B的路径 ...
- 02、NetCore2.0优化之Nuget包
02.NetCore2.0优化之Nuget包 在NetCore2.0中的包是如何管理的?如何存储的?微软做了哪些优化工作? -------------------------------------- ...
- 使用supervisor管理进程
Supervisor (http://supervisord.org) 是一个用 Python 写的进程管理工具,可以很方便的用来启动.重启.关闭进程(不仅仅是 Python 进程).除了对单个进程的 ...
- nginx location的命中过程
1 先判断精准命中,立即返回结果并结束解析过程 2 判断普通命中,如果有多个命中,"记录"下"最长"的命中结果(注意:记录但不结束,最长的为准) 3 继续判断正 ...
- 小工具:批量导入导出主机上的docker镜像
工作需要,将主机上的部分镜像导出为tar文件,放便在其他主机上使用 用python实现了一个批量打包脚本: import re import os import subprocess if __nam ...
- 众说纷纭的ul、ol、li
(1)提到ul ol li,大家都知道,就是三个列表标签,ul表示无需列表(unordered list),ol表示有序列表(oredr list), li 表示列表项(list item),之前我也 ...