Java 多线程进阶:常见的锁策略/synchronized原理/CAS/JUC(java.util.concurrent)的常见类/线程安全的集合类

一.常见的锁策略

锁:非常广义的话题;

synchronized:只是市面上五花八门的锁的其中一种典型的实现,Java内置的推荐使用的锁;

(1)乐观锁 && 悲观锁

乐观锁:加锁的时候,假设出现锁冲突的概率不大;接下来围绕加锁要做的工作很少;

悲观锁:加锁的时候,假设出现锁冲突的概率很大;接下来围绕加锁要做的工作更多;

synchronized是乐观还是悲观的呢? "自适应"

使用synchronized,初始情况下是乐观的(预估接下来锁冲突不大),

同时会统计接下来锁冲突了多少次,

如果发现,锁冲突的次数达到一定程度了,就会转成悲观的.

C++中的std::mutex锁的特点就是悲观的.

(2)重量级锁 && 轻量级锁

重量级锁:加锁的开销比较大,要做更多的工作.[往往,悲观的时候,会做的重]

轻量级锁:加锁的开销比较小,要做的工作相对少.[往往乐观的时候,会做的轻]

不能100%等价,乐观悲观是站在"预估锁冲突"的角度;重量清凉则是站在加锁时间开销的角度;

(3)挂起等待锁 && 自旋锁

挂起等待锁属于是悲观锁/重量级锁的一种典型实现;(CPU消耗少,等待时间变多了)

预测拿到锁的概率不大,让出了CPU资源,CPU就可以用来做别的事了;

自旋锁属于是乐观锁/轻量级锁的一种典型实现;(CPU消耗多,等待时间相对少了)

忙等:等待的过程中不会释放CPU资源,不停的检测锁是否被释放.一旦锁被释放了,就立即有机会能获取到锁了;

若很多线程都在忙等总的CPU消耗就会非常高,故锁冲突概率不高的情况下,才能忙等;

并且若竞争太激烈,导致有些线程,要等待很久才能拿到锁;

---------------------------------------------------------------------------------------------------------------------------------

synchronized"自适应":

轻量级锁就是基于"自旋"的方式实现的.(JVM内部,用户态代码实现的);

重量级锁就是基于"挂起等待"的方式实现的.(调用操作系统 api, 在内核中实现的);

(4)公平锁 && 非公平锁

在计算机中:约定"先来后到"为公平.

synchronized属于"非公平锁",本身操作系统内核中对锁的操作就是如此,synchronized在系统内核的基础上,没有做啥额外的工作;

如果需要使用公平锁,就需要做额外的操作(比如引入队列,记录每个线程加锁的顺序)

(5)可重入锁 &&  不可重入锁

死锁问题:如果一个线程,针对一把锁加锁两次,就可能出现死锁;

如果把锁设定成"可重入锁"就可以避免该类型的死锁了;

可重入锁:

(1)记录当前是哪个线程持有了这把锁;

(2)在加锁的时候判定,当前申请锁的线程,是否就是线程的持有者;

(3)计数器,记录加锁的次数,从而确定何时真正的释放锁;

(6)读写锁 

synchronized并非是读写锁.

如果多个线程同时读取一个变量,没有线程安全问题.

但是,一个线程读一个线程写/两个线程都写 就会产生问题.

所谓的读写锁,把"加锁操作"分为两种情况: 1.读加锁; 2.写加锁;

读写锁提供了两种加锁的api: 1.加读锁 2.加写锁; 解锁的api是相同的;

---------------------------------------------------------------------------------------------------------------------------------

如果两个线程,都是按照读方式加锁,此时不会产生锁冲突;

如果两个线程,都是加写锁,此时会产生锁冲突;

如果一个线程读锁,一个线程写锁,也会产生锁冲突;

---------------------------------------------------------------------------------------------------------------------------------

读写锁本身也是系统内置的锁:在Java中对应的类:ReentrantReadWriteLock

该类有包含两个内部类:ReentrantReadWriteLock.ReadLock;ReentrantReadWriteLock.WriteLock;

都提供了lock/unlock的方法进行加锁解锁,此处就要将unlock放到finally中确保能够执行到;

二.synchronized的原理

synchronized的特点:

(1)乐观悲观,自适应;

(2)重量轻量,自适应;

(3)自旋挂起等待,自适应;

(4)非公平锁;

(5)可重入锁;

(6)不是读写锁;

synchronized的加锁过程: 锁升级的过程

刚开始使用synchronized加锁,首先锁会处于"偏向锁"的状态.

遇到线程之间的锁竞争,升级到"轻量级锁";

进一步统计锁竞争的频次,达到一定程度之后,升级到"重量级锁";

synchronized加锁的时候,会经历 无锁 => 偏向锁 => 轻量级锁 => 重量级锁 的过程;

---------------------------------------------------------------------------------------------------------------------------------

 偏向锁?

不是真的加锁(真的加锁开销比较大),偏向锁,只是做个标记;(标记的过程很轻量高效)

偏向锁本质上,是在推迟加锁的时机(懒汉模式思想的体现);

---------------------------------------------------------------------------------------------------------------------------------

上述锁升级的过程,主要是为了让synchronized这个锁很好的适

你可能感兴趣的:(java,开发语言)