1、什么情况下导致死锁,遇到死锁怎么解决?
死锁的定义:
所谓死锁是在多个线程因为竞争资源的时候而造成的一种僵局(互相等待),若无外力作用,这些进程都无法向前推进。
死锁产生的必要条件:
-互斥条件: 线程要求对所分配的资源(如打印机)进行排他性控制,即在一段时间内某资源仅仅为一个线程所占有。此时如果有其他的线程请求该资源,则请求线程只能等待。
-不剥夺条件:线程所使用的的资源在未使用完毕之前,不能被其他线程强行夺走,即只能由获得该资源的线程自己来释放(只能是主动释放)
-请求和保持条件:线程已经保持了至少一个资源,但是又提出了新的资源请求,而该资源已经被其他线程占有,此时请求进程被阻塞
-循环等待条件:存在一种线程资源的循环等待链,链中每一个线程已经获得的资源同时被链中下一个线程所请求。即存在一个处于等待状态的线程集合。{Pl, P2, …, pn},其中 Pi 等待的资源被 P(i+1)占有(i=0, 1, …, n-1),Pn 等待的资源被 P0 占有,
避免死锁的技术:
-加锁顺序(线程按照一定的顺序加锁)
-加锁时限(线程尝试获取锁的时候加上一定的时限,超过时限则放弃对于该锁的请求,释放自己占有的锁)
HashMap和HashTable有什么区别?
-hashMap去掉了HashTable的Contains方法,但是加上了containsValue()和containsKey()方法。
-hashTable是线程同步的,HashMap是线程不同步的,效率上比hashTable要高。
-hashMap允许空的键值,而hashTable不允许。
2、HashMap的实现原理:
HashMap概述: HashMap是基于哈希表的Map接口的非同步实现。此实现提供所有可选的映射操作,并且允许使用null值和null键。此类不保证映射的顺序,特别是不保证顺序永久不变。
HashMap的数据结构: 在java的编程语言中,最基本的结构有两种,一个是数组另外一个是模拟指针(引用),所有的数据结构都可以用这两个基本的结构来构造,HashMap也不例外。HashMap实际上就是一个“链表散列”的数据结构,即数组和链表的结合体。
当我们往Hashmap中put元素时,首先根据key的hashcode重新计算hash值,得到hash值得到这个元素在数组中的位置(下标),如果该数组在该位置上已经存放了其他元素,那么在这个位置上的元素将以链表的形式存放,新加入的放在链头,最先加入的放入链尾.如果数组中该位置没有元素,就直接将该元素放到数组的该位置上。
需要注意Jdk 1.8中对HashMap的实现做了优化,当链表中的节点数据超过八个之后,该链表会转为红黑树来提高查询效率,从原来的O(n)到O(logn)
3、Iterator怎么使用?有什么特点
Java中的Iterator功能比较简单,并且只能单向移动:
(1) 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
(2) 使用next()获得序列中的下一个元素。
(3) 使用hasNext()检查序列中是否还有元素。
(4) 使用remove()将迭代器新返回的元素删除。
Iterator是Java迭代器最简单的实现,为List设计的ListIterator具有更多的功能,它可以从两个方向遍历List,也可以从List中插入和删除元素
4、Iterator 和 ListIterator 有什么区别
-Iterator可以用来遍历Set和List集合,但是ListIterator只能用来遍历List。
-Iterator对集合只能是前向遍历,ListIterator既可以前向又可以后向遍历。
-ListIterator实现了Iterator接口,并包含其他的功能,比如:增加元素,替换元素,获取前一个和后一个元素的索引等等。
5、线程有哪些状态
线程通常有五种状态,创建,就绪,运行,阻塞和死亡
- 创建状态。在生成线程对象,并没有调用该对象的start方法,这是线程处于创建状态。
- 就绪状态。当调用了线程对象的start方法之后,该线程就进入了就绪状态,但是此时线程调度程序还没有把该线程设置为当前线程,此时处于就绪状态。在线程运行之后,从等待或者睡眠中回来之后,也会处于就绪状态。
- 运行状态。线程调度程序将处于就绪状态的线程设置为当前线程,此时线程就进入了运行状态,开始运行run函数当中的代码。
- 阻塞状态。线程正在运行的时候,被暂停,通常是为了等待某个时间的发生(比如说某项资源就绪)之后再继续运行。sleep,suspend,wait等方法都可以导致线程阻塞。
- 死亡状态。如果一个线程的run方法执行结束或者调用stop方法后,该线程就会死亡。对于已经死亡的线程,无法再使用start方法令其进入就绪
6、synchronized的原理是什么
Synchronized是由JVM实现的一种实现互斥同步的方式,查看被Synchronized修饰过的程序块编译后的字节码,会发现,被Synchronized修饰过的程序块,在编译前后被编译器生成了monitorenter和monitorexit两个字节码指令。
在虚拟机执行到monitorenter指令时,首先要尝试获取对象的锁:如果这个对象没有锁定,或者当前线程已经拥有了这个对象的锁,把锁的计数器+1; 当执行monitorexit指令时,将锁计数器-1;当计数器为0时,锁就被释放了。如果获取对象失败了,那当前线程就要阻塞等待,直到对象锁被另外一个线程释放为止。
Java中Synchronize通过在对象头设置标志,达到了获取锁和释放锁的目的。
7、为什么说Synchronized是非公平锁?
非公平主要表现在获取锁的行为上,并非是按照申请锁的时间前后给等待线程分配锁的,每当锁被释放后,任何一个线程都有机会竞争到锁,这样的目的是为了提高执行性能,缺点是产生进程饥饿现象。
8、Synchronized和ReentrantLock的异同?
synchronized:是java内置的关键字,它提供了一种独占的加锁方式。synchronized的获取和释放锁由JVM实现,用户不需要显示的释放锁,非常方便。然而synchronized也有一些问题: 当线程尝试获取锁的时候,如果获取不到锁会一直阻塞。 如果获取锁的线程进入休眠或者阻塞,除非当前线程异常,否则其他线程尝试获取锁必须一直等待
ReentrantLock: ReentrantLock是Lock的实现类,是一个互斥的同步锁。ReentrantLock是JDK 1.5之后提供的API层面的互斥锁,需要lock()和unlock()方法配合try/finally语句块来完成,等待可中断避免,出现死锁的情况(如果别的线程正持有锁,会等待参数给定的时间,在等待的过程中,如果获取了锁定,就返回true,如果等待超时,返回false)
公平锁与非公平锁多个线程等待同一个锁时,必须按照申请锁的时间顺序获得锁,Synchronized锁非公平锁,ReentrantLock默认的构造函数是创建的非公平锁,可以通过参数true设为公平锁,但公平锁表现的性能不是很好
从功能角度:ReentrantLock 比 Synchronized的同步操作更精细(因为可以像普通对象一样使用),甚至实现 Synchronized没有的高级功能
- 等待可中断当持有锁的线程长期不释放锁的时候,正在等待的线程可以选择放弃等待,对处理执行时间非常长的同步块很有用。
- 带超时的获取锁尝试在指定的时间范围内获取锁,如果时间到了仍然无法获取则返回。
- 可以判断是否有线程在排队等待获取锁。
- 可以响应中断请求与Synchronized不同,当获取到锁的线程被中断时,能够响应中断,中断异常将会被抛出,同时锁会被释放。
- 可以实现公平锁
从锁释放角度: Synchronized在JVM层面上实现的,不但可以通过一些监控工具监控 Synchronized的锁定,而且在代码执行出现异常时,JVM会自动释放锁定,但是使用Lock则不行,Lock是通过代码实现的,要保证锁定一定会被释放,就必须将 unLock()放到 finally{}中
从性能角度:Synchronized早期实现比较低效,对比 ReentrantLock,大多数场景性能都相差较大。但是在Java6中对其进行了非常多的改进,
- 在竞争不激烈时:Synchronized的性能要优于 ReetrantLock
- 在高竞争情况下:Synchronized的性能会下降几十倍,但是 ReetrantLock的性能能维持常态
26.说一下 HashSet 的实现原理
- HashSet底层由HashMap实现
- HashSet的值存放于HashMap的key上
- HashMap的value统一为PRESENT
176万+

被折叠的 条评论
为什么被折叠?



