1 乐观锁
读不锁,可能读到旧数据。写锁。
cas 就是乐观锁,cas 是比拟并替换,即写的时候先存下以后值,而后计算,而后看看当初的值是不是方才存起来的值(看有没有人批改过),如果还是方才存起来的值,就写入。
但这里存在 aba 的问题,即“尽管还是方才存起来的值,但这段时间里,有人改过两次又改回原值”,这时就会有谬误。
依据版本号判断能够解决这个问题。
2 乐观锁
只能有一个线程进行读写,其余线程都要等着这个线程开释锁。synchronized、vector、hashtable
3 自旋锁
没有拿到锁的线程原地期待,不切换线程。
节俭了切换线程的开销。
但占用处理器的执行工夫,如果屡次都没等到,就应该挂起这个线程。
自适应自旋
CAS 就是用自旋锁。
4 可重入锁
再次获取锁:辨认获取锁的线程是否为以后占据锁的线程,如果是,则再次胜利获取。获取锁后,进行计数自增。
开释锁:开释锁时,进行计数自减。通过组合自定义同步器来实现锁的获取与开释。
可重入锁的作用:防止死锁。
面试题 1: 可重入锁如果加了两把,然而只开释了一把会呈现什么问题?
答:程序卡死,线程不能进去,也就是说咱们申请了几把锁,就须要开释几把锁。
面试题 2: 如果只加了一把锁,开释两次会呈现什么问题?
答:会报错,java.lang.IllegalMonitorStateException。
synchronized、Reentrantlock、Lock
5 读写锁
通过 ReentrantReadWriteLock
类来实现。
在读的中央应用读锁,在写的中央应用写锁,多个读锁不互斥,读锁与写锁互斥。
ReentrantReadWriteLock,CopyOnWriteArrayList、CopyOnWriteArraySet
6 偏心锁
多个线程进入一个队列,依照申请锁的程序来获取锁。
Reentrantlock(true)
7 非偏心锁
非偏心锁
是一种思维: 线程尝试获取锁,如果获取不到,则再采纳偏心锁的形式。多个线程获取锁的程序,不是依照先到先得的程序,有可能后申请锁的线程比先申请的线程优先获取锁。
长处: 非偏心锁的性能高于偏心锁。
毛病: 有可能造成线程饥饿(某个线程很长一段时间获取不到锁)
Java 中的非偏心锁:synchronized 是非偏心锁,ReentrantLock 通过构造函数指定该锁是偏心的还是非偏心的,默认是非偏心的。
synchronized、reentrantlock(false)
8 共享锁
能够有多个线程获取读锁,以共享的形式持有锁。
ReentrantReadWriteLock 中读锁
9 独占锁
只能有一个线程获取锁
synchronized、vector、hashtable、ReentrantReadWriteLock 中写锁
10 重量级锁
synchronized
是通过对象外部的一个叫做监视器锁(monitor
)来实现的,监视器锁自身依赖底层的操作系统的 Mutex Lock
来实现。操作系统实现线程的切换须要从用户态切换到外围态,老本十分高。这种依赖于操作系统 Mutex Lock
来实现的锁称为重量级锁。为了优化 synchonized
,引入了 轻量级锁
, 偏差锁
。
Java 中的重量级锁: synchronized
synchronized
11 轻量级锁锁优化技术
轻量级锁
是 JDK6 时退出的一种锁优化机制:
使用 cas 判断是否有人和本人竞争锁,如果没竞争就取得锁,有竞争就升级成重量级锁。
在无竞争时能够缩小重量级锁的开销。
如果有竞争还额定减少了 cas 的开销。在有竞争的状况下,轻量级锁比传统的重量级锁更慢。
12 偏差锁锁优化技术
一个线程拿到锁当前,只有没有其余线程去拿这个锁,这个线程就不须要再做加锁动作。
适宜不同线程拜访不同资源不竞争时。
13 分段锁
最好的例子来阐明分段锁是 ConcurrentHashMap。
ConcurrentHashMap 原理:它外部细分了若干个小的 HashMap,称之为段(Segment)。默认状况下一个 ConcurrentHashMap 被进一步细分为 16 个段,既就是锁的并发度。如果须要在 ConcurrentHashMap 增加一项 key-value,并不是将整个 HashMap 加锁,而是首先依据 hashcode 失去该 key-value 应该寄存在哪个段中,而后对该段加锁,并实现 put 操作。在多线程环境中,如果多个线程同时进行 put 操作,只有被退出的 key-value 不寄存在同一个段中,则线程间能够做到真正的并行。
线程平安:ConcurrentHashMap 是一个 Segment 数组,Segment 通过继承 ReentrantLock 来进行加锁,所以每次须要加锁的操作锁住的是一个 segment,这样只有保障每个 Segment 是线程平安的,也就实现了全局的线程平安
concurrentHashMap
14 互斥锁 synchronized
15 同步锁 synchronized
同步锁与互斥锁同义,
16 死锁互相申请对方的资源
Java 中的死锁不能自行突破,所以线程死锁后,线程不能进行响应。
17 锁粗化锁优化技术
重复对一个对象加锁解锁,例如在循环中加锁。能够把锁粗化,整个循环用一个锁。
18 锁打消锁优化技术
当 Java 虚拟机运行时发现有些共享数据不会被线程竞争时就能够进行锁打消。
那如何判断共享数据不会被线程竞争?
利用 逃逸剖析技术
:剖析对象的作用域,如果对象在 A 办法中定义后,被作为参数传递到 B 办法中,则称为办法逃逸;如果被其余线程拜访,则称为线程逃逸。
在堆上的某个数据不会逃逸进来被其余线程拜访到,就能够把它当作栈上数据看待,认为它是线程公有的,同步加锁就不须要了。
19 synchronized
synchronized
是 Java 中的关键字:用来润饰办法、对象实例。属于独占锁、乐观锁、可重入锁、非偏心锁。
- 1. 作用于实例办法时,锁住的是对象的实例(this);
- 2. 当作用于静态方法时,锁住的是 Class 类,相当于类的一个全局锁,会锁所有调用该办法的线程;
- 3.synchronized 作用于一个非 NULL 的对象实例时,锁住的是所有以该对象为锁的代码块。它有多个队列,当多个线程一起拜访某个对象监视器的时候,对象监视器会将这些线程存储在不同的容器中。
每个对象都有个 monitor 对象,加锁就是在竞争 monitor 对象,代码块加锁是在代码块前后别离加上 monitorenter 和 monitorexit 指令来实现的,办法加锁是通过一个标记位来判断的。
20 Lock
Lock
: 是 Java 中的接口,可重入锁、乐观锁、独占锁、互斥锁、同步锁。
- 1.Lock 须要手动获取锁和开释锁。就好比自动挡和手动挡的区别
- 2.Lock 是一个接口,而 synchronized 是 Java 中的关键字,synchronized 是内置的语言实现。
- 3.synchronized 在产生异样时,会主动开释线程占有的锁,因而不会导致死锁景象产生;而 Lock 在产生异样时,如果没有被动通过 unLock()去开释锁,则很可能造成死锁景象,因而应用 Lock 时须要在 finally 块中开释锁。
- 4.Lock 能够让期待锁的线程响应中断,而 synchronized 却不行,应用 synchronized 时,期待的线程会始终期待上来,不可能响应中断。
- 5. 通过 Lock 能够晓得有没有胜利获取锁,而 synchronized 却无奈办到。
- 6.Lock 能够通过实现读写锁进步多个线程进行读操作的效率。
synchronized 的劣势:
- 足够清晰简略,只须要根底的同步性能时,用 synchronized。
- Lock 应该确保在 finally 块中开释锁。如果应用 synchronized,JVM 确保即便出现异常,锁也能被主动开释。
- 应用 Lock 时,Java 虚拟机很难得悉哪些锁对象是由特定线程锁持有的。
21 ReentrantLock
ReentrantLock
是 Java 中的类: 继承了 Lock 类,可重入锁、乐观锁、独占锁、互斥锁、同步锁。
划重点
相同点:
- 1. 次要解决共享变量如何平安拜访的问题
- 2. 都是可重入锁,也叫做递归锁,同一线程能够屡次取得同一个锁,
- 3. 保障了线程平安的两大个性:可见性、原子性。
不同点:
- 1.ReentrantLock 就像手动汽车,须要显示的调用 lock 和 unlock 办法,synchronized 隐式取得开释锁。
- 2.ReentrantLock 可响应中断,synchronized 是不能够响应中断的,ReentrantLock 为解决锁的不可用性提供了更高的灵活性
- 3.ReentrantLock 是 API 级别的,synchronized 是 JVM 级别的
- 4.ReentrantLock 能够实现偏心锁、非偏心锁,默认非偏心锁,synchronized 是非偏心锁,且不可更改。
- 5.ReentrantLock 通过 Condition 能够绑定多个条件