1、非公平锁和公平锁在reetrantlock 里的实现

如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,FIFO。

对于非公平锁,只要CAS 设置同步状态成功,则表示当前线程获取了锁,而公平锁还需要判断当前节点是否有前驱节点,如果有,则表示有线程比当前线程更早请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。


2、synchronized,可重入怎么实现。

每个锁关联一个线程持有者和一个计数器。当计数器为0 时表示该锁没有被任何线程持有,那么任何线程都都可能获得该锁而调用相应方法。

当一个线程请求成功后,JVM 会记下持有锁的线程,并将计数器计为1。此时其他线程请求该锁,则必须等待。而该持有锁的线程如果再次请求这个锁,就可以再次拿到这个锁,同时计数器会递增。

当线程退出一个synchronized 方法/块时,计数器会递减,如果计数器为0 则释放该锁。


3、锁和同步的区别

1、用法上的不同
synchronized 既可以加在方法上,也可以加载特定代码块上,而lock 需要显示地指定起始位置和终止位置。

synchronized 是托管给JVM 执行的,lock 的锁定是通过代码实现的,它有比synchronized更精确的线程语义。

2、性能上的不同
lock接口的实现类ReentrantLock,不仅具有和synchronized 相同的并发性和内存语义,还多了超时的获取锁、定时锁、等候和中断锁等。

在竞争不是很激烈的情况下,synchronized 的性能优于ReentrantLock,竞争激烈的情况下synchronized 的性能会下降的非常快,而ReentrantLock 则基本不变。

3、锁机制不同
synchronized 获取锁和释放锁的方式都是在块结构中,当获取多个锁时,必须以相反的顺序释放,并且是自动解锁。而Lock 则需要开发人员手动释放,并且必须在finally 中释放,否则会引起死锁。


4、什么是死锁?

两个线程或两个以上线程都在等待对方执行完毕才能继续往下执行的时候就发生了死锁。结果就是这些线程都陷入了无限的等待中。

例如,如果线程1 锁住了A,然后尝试对B 进行加锁,同时线程2 已经锁住了B,接着尝试对A 进行加锁,这时死锁就发生了。线程1 永远得不到B,线程2 也永远得不到A,并且它们永远也不会知道发生了这样的事情。为了得到彼此的对象(A 和B),它们将永远阻塞下去。这种情况就是一个死锁。

产生死锁必须具备以下四个条件

1、互斥条件:该资源任意一个时刻只由一个线程占用

2、请求与保持条件:一个进程因请求资源而阻塞时,对已获得的资源保持不放

3、不剥夺条件:线程已获得的资源在未使用完之前不能被其他线程强行剥夺,只有自己使用完毕后才释放资源

4、循环等待条件:若干进程之间形成一种头尾相接的循环等待资源关系


5、如何确保N 个线程可以访问N 个资源同时又不导致死锁?

使用多线程的时候,一种非常简单的避免死锁的方式就是:指定获取锁的顺序,并强制线程按照指定的顺序获取锁。因此,如果所有的线程都是以同样的顺序加锁和释放锁,就不会出现死锁了。

预防死锁,预先破坏产生死锁的四个条件。互斥不可能破坏,所以有如下三种方法:

1、破坏请求和保持条件:一次性申请所有的资源

2、破坏不剥夺条件:占用部分资源的线程进一步申请其他资源时,如果申请不到,可以主动释放它所占有的资源

3、破坏循环等待条件:靠按序申请资源来预防。按某一顺序申请资源,释放资源则反序释放。