这里涉及的内容是关于实现锁 (不只局限于Java的锁),我们作以了解即可。
1.锁策略
1.悲观锁/乐观锁
悲观锁:发生锁竞争的可能性很大------>逻辑更复杂,就是悲观锁
乐观锁:发生锁竞争的可能性很小------>逻辑更简单,就是乐观锁
2.重量级锁/轻量级锁
重量级锁:加锁这个操作开销大------》重量级锁
轻量级锁:加锁这个开销小------》轻量级锁
"加锁":指的是线程代码执行到synchronized上锁时,尝试占有这把锁的整套执行流程,是cpu执行抢锁的全过程。
重量级锁:涉及用户态核心态切换。线程抢不到锁时,交给OS(Operate System)把线程阻塞挂起,放到等待队列,等锁释放后OS唤醒线程。而在这过程中,涉及用户态切换核心态、线程挂起、线程唤醒,CPU要保存、恢复现场等,开销很大
轻量级锁:只涉及用户态。线程抢不到锁时,不会阻塞线程,而是不断重试尝试抢占锁。轻量级锁的优点是没有线程阻塞唤醒的大开销,但明显的缺点是如果线程一直抢不到锁,CPU一置空转,浪费资源。
悲观锁可能是重量级锁,乐观锁有可能是轻量级锁
3.挂起等待锁/自旋锁
挂起等待锁:线程抢不到锁时,交给OS把线程阻塞,让出CPU,等锁被释放时唤醒线程。涉及操作系统内部线程调度,开销大
自旋锁:遇到锁冲突,不会阻塞线程,而是不断重试尝试抢占锁。
但是,注意!!!
自旋锁与轻量级锁不一样。二者的核心策略一样,但不同的是:
自旋锁:无限空转,一直占有CPU,空耗CPU资源
轻量级锁:JVM轻量级锁的自旋时有限制的自适应自旋。即JVM会自动调整自旋次数,预判很快就能拿到锁------自旋,预判长时间抢不到锁,不会空耗CPU资源,会直接升级为重量级锁,交给OS阻塞休眠。
4.公平锁/不公平锁
公平锁:谁先发起抢锁请求,先进入等待队列,等锁释放后就优先唤醒最早排队的线程
非公平锁:释放锁的瞬间,任何线程都可直接竞争抢锁
synchronized是不公平锁,因为如果是公平锁,锁已被占用,多个线程发起抢锁请求,进入阻塞队列,而阻塞队列的线程要被唤醒,这其中涉及操作系统内部线程调度,开销很大。而如果是公平锁,可减少大量线程唤醒的开销,整体并发吞吐量更高。
5.可重入锁/不可重入锁
可重入锁(递归锁):一把锁可多次加锁
不可重入锁:一把锁不可多次加锁
synchronized是可重入锁
6.读写锁/普通互斥锁
普通互斥锁:普通锁是排他锁。即只要一个线程拿到锁,不管是读还是写,其他线程全都排队阻塞。如果1000个线程全是读操作,那么只能串行排队,并发极低。
读写锁:一个线程拿到锁,其它线程可同时拿到锁。具体情况如下:
线程1读+线程2读------》不会发生锁竞争。效率提高
线程1读+线程2写------》发生锁竞争。读锁在读,写锁必须阻塞等读锁释放后才能拿到写锁;
写锁在修改,其它读锁阻塞,等写锁释放后再拿到读锁。
线程1写+线程2写:------》发生锁竞争。
synchronized是普通互斥锁。