先来看一个问题
解决线程安全问题有两种方式:
方式1: synchronized
方式2:可重入锁

多读多写:读写锁
多读一写:volate

【一】隐式锁 和 显式锁
一、隐式锁
只有synchronized
详见:synchronized 篇

二、显式锁
1、定义:
除了隐式锁synchronized,其他都是显示锁。
如:Lock接口、ReentrantLock(可重入锁)、ReadWriteLock接口(读写锁)等。
2、显式锁使用规范
1)lock.lock()一定要在try之外之前,否则遇到异常会触发finally块中的unlock。
2)加锁的逻辑代码,必须写在 try-cach 块中;
3)lock.unlock()一定要在finally代码块,不然有死锁风险)

【二】可重入锁 和 不可重入锁
一、可重入锁
1、定义:
如果锁具备可重入性,则称作为可重入锁。
如:synchronized 和 ReentrantLock 都是可重入锁。

可重入性在我看来实际上表明了锁的分配机制:
基于线程的分配,而不是基于方法调用的分配。

举个简单的例子:
当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法 method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。

classMyClass {
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
}

上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程 A获取了这个对象的锁,而由于method2也是synchronized方法,假如synchronized不具备可重入性,此时线程A需要重新申请 锁。但是这就会造成一个问题,因为线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。而由于 synchronized 和 ReentrantLock 都具备可重入性,所以不会发生上述现象。

二、不可重入锁
与可重入锁相反
【三】公平锁 和 非公平锁
一、公平锁
1、定义
公平锁,尽量以申请锁的顺序来获取锁。
比如同是有多个线程在等待一个锁,当这个锁被释放时,等待时间最久的线程(最先请求的线程)会获得该所,这种就是公平锁。

2、举例

ReentrantLock 和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。设置方法如下:ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);

二、非公平锁

1、定义:

非公平锁,无法保证锁的获取是按照申请锁的顺序进行的。

2、优点:

非公平锁的优点在于吞吐量比公平锁大。

3、缺点:

有可能会造成优先级反转或者饥饿现象。

这样就可能导致某个或者一些线程永远获取不到锁。

4、举例

1)synchronized 就是非公平锁,它无法保证等待的线程获取锁的顺序。

2)ReentrantLock和ReentrantReadWriteLock,它默认情况下是非公平锁,但是可以设置为公平锁。

设置方法如下:

ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true);

【四】乐观锁 和 悲观锁

一、 乐观锁
Java中CAS操作,就是一种乐观锁。
读数据,不上锁,乐观认为别的线程不会修改。
写数据,判断下其他线程有没有更新数据,先读取数据,比较数据,写入数据,失败重复操作。

二、悲观锁
Java中Synchronized,就是一种悲观锁。
是一种悲观思想,总认为别人会来修改数据,所以每次读写数据,都会上锁。

比较:
悲观锁:线程挂起,牺牲性能
乐观锁:线程不挂起,适用于竞争不激烈
【独享锁 和 共享锁】

【读写锁】
ReadWriteLock接口(读写锁)

读写锁特点:

a)多个读者可以同时进行读
b)写者必须互斥(只允许一个写者写,也不能读者写者同时进行)
c)写者优先于读者(一旦有写者,则后续读者必须等待,唤醒时优先考虑写者)

【可中断锁】

可中断锁:顾名思义,就是可以相应中断的锁。

Lock是可中断锁。

synchronized不是可中断锁。

如果某一线程A正在执行锁中的代码,另一线程B正在等待获取该锁,可能由于等待时间过长,线程B不想等待了,想先处理其他事情,我们可以让它中断自己或者在别的线程中中断它,这种就是可中断锁。

【分段锁

【自旋锁】
1、原理:
根据上次线程持有资源的时长,决定多线程下次竞争锁的状态,既是否从内核态,全部转换到用户态进行阻塞挂起。 如果不需要,这些线程进就用自旋状态,等有线程释放了锁立即可获取锁,从而避免用户线程和内核态切换的消耗。

2、优缺点
优点:

  1. 减少线程用户态和内核态的切换消耗
  2. 锁资源处理时间非常短代码块,性能极大提升。

缺点:

  1. 不适合锁资源处理时间长的场景
  2. 自旋锁状态既CPU空转消耗

3、自旋锁开启
JDK1.5 通过参数 -XX:+UseSpinning 开启
JDK1.6 自适应自旋锁 , 根据上次占用时间决定。

4、自旋锁变种

  1. TicketLock 公平
  2. CLHLock 基于链表,可扩展,公平,高性能
  3. MCSLock 基于链表,后续节点

【偏向锁、轻量级锁、自旋锁、重量级锁】(synchrnized锁升级)

《JAVA锁有哪些种类,以及区别(转)》

【锁粒度:粗粒度锁住和 细粒度锁】
一、粗粒度锁

按单个用户
按某类人(普通用户竞争1把锁、vip用户竞争10把锁)

二、细粒度锁
按地区