锁:非常广义的话题;
synchronized:只是市面上五花八门的锁的其中一种典型的实现,Java内置的推荐使用的锁;
乐观锁:加锁的时候,假设出现锁冲突的概率不大;接下来围绕加锁要做的工作很少;
悲观锁:加锁的时候,假设出现锁冲突的概率很大;接下来围绕加锁要做的工作更多;
synchronized是乐观还是悲观的呢? "自适应"
使用synchronized,初始情况下是乐观的(预估接下来锁冲突不大),
同时会统计接下来锁冲突了多少次,
如果发现,锁冲突的次数达到一定程度了,就会转成悲观的.
C++中的std::mutex锁的特点就是悲观的.
重量级锁:加锁的开销比较大,要做更多的工作.[往往,悲观的时候,会做的重]
轻量级锁:加锁的开销比较小,要做的工作相对少.[往往乐观的时候,会做的轻]
不能100%等价,乐观悲观是站在"预估锁冲突"的角度;重量清凉则是站在加锁时间开销的角度;
挂起等待锁属于是悲观锁/重量级锁的一种典型实现;(CPU消耗少,等待时间变多了)
预测拿到锁的概率不大,让出了CPU资源,CPU就可以用来做别的事了;
自旋锁属于是乐观锁/轻量级锁的一种典型实现;(CPU消耗多,等待时间相对少了)
忙等:等待的过程中不会释放CPU资源,不停的检测锁是否被释放.一旦锁被释放了,就立即有机会能获取到锁了;
若很多线程都在忙等总的CPU消耗就会非常高,故锁冲突概率不高的情况下,才能忙等;
并且若竞争太激烈,导致有些线程,要等待很久才能拿到锁;
---------------------------------------------------------------------------------------------------------------------------------
synchronized"自适应":
轻量级锁就是基于"自旋"的方式实现的.(JVM内部,用户态代码实现的);
重量级锁就是基于"挂起等待"的方式实现的.(调用操作系统 api, 在内核中实现的);
在计算机中:约定"先来后到"为公平.
synchronized属于"非公平锁",本身操作系统内核中对锁的操作就是如此,synchronized在系统内核的基础上,没有做啥额外的工作;
如果需要使用公平锁,就需要做额外的操作(比如引入队列,记录每个线程加锁的顺序)
死锁问题:如果一个线程,针对一把锁加锁两次,就可能出现死锁;
如果把锁设定成"可重入锁"就可以避免该类型的死锁了;
可重入锁:
(1)记录当前是哪个线程持有了这把锁;
(2)在加锁的时候判定,当前申请锁的线程,是否就是线程的持有者;
(3)计数器,记录加锁的次数,从而确定何时真正的释放锁;
synchronized并非是读写锁.
如果多个线程同时读取一个变量,没有线程安全问题.
但是,一个线程读一个线程写/两个线程都写 就会产生问题.
所谓的读写锁,把"加锁操作"分为两种情况: 1.读加锁; 2.写加锁;
读写锁提供了两种加锁的api: 1.加读锁 2.加写锁; 解锁的api是相同的;
---------------------------------------------------------------------------------------------------------------------------------
如果两个线程,都是按照读方式加锁,此时不会产生锁冲突;
如果两个线程,都是加写锁,此时会产生锁冲突;
如果一个线程读锁,一个线程写锁,也会产生锁冲突;
---------------------------------------------------------------------------------------------------------------------------------
读写锁本身也是系统内置的锁:在Java中对应的类:ReentrantReadWriteLock
该类有包含两个内部类:ReentrantReadWriteLock.ReadLock;ReentrantReadWriteLock.WriteLock;
都提供了lock/unlock的方法进行加锁解锁,此处就要将unlock放到finally中确保能够执行到;
(1)乐观悲观,自适应;
(2)重量轻量,自适应;
(3)自旋挂起等待,自适应;
(4)非公平锁;
(5)可重入锁;
(6)不是读写锁;
刚开始使用synchronized加锁,首先锁会处于"偏向锁"的状态.
遇到线程之间的锁竞争,升级到"轻量级锁";
进一步统计锁竞争的频次,达到一定程度之后,升级到"重量级锁";
synchronized加锁的时候,会经历 无锁 => 偏向锁 => 轻量级锁 => 重量级锁 的过程;
---------------------------------------------------------------------------------------------------------------------------------
不是真的加锁(真的加锁开销比较大),偏向锁,只是做个标记;(标记的过程很轻量高效)
偏向锁本质上,是在推迟加锁的时机(懒汉模式思想的体现);
---------------------------------------------------------------------------------------------------------------------------------
上述锁升级的过程,主要是为了让synchronized这个锁很好的适