文章目录
前言
常见锁策略
1、悲观锁 VS 乐观锁
2、读写锁 vs 普通的互斥锁
3、解锁
ReentrantReadWriteLock.ReadLock 类表示一个读锁. 这个对象提供了 lock / unlock 方法进行加锁解锁.
ReentrantReadWriteLock.WriteLock 类表示一个写锁. 这个对象也提供了 lock / unlock 方法进行加锁解锁.
3、重量级锁 vs 轻量级锁
重量级锁就是做了更多的事情,开销很大。
轻量级锁,做的事情很少,开销也就很小。
4、挂起等待锁 vs 自旋锁
小结
5、公平锁 和 非公平锁
非公平锁:多个线程在等待一把锁的时候,不遵守先来后到。
对于操作系统来说,本身线程之间的调度就是随机的(机会均等的),操作系统提供的 mutex 这个锁,就属于非公平锁。
6、可重入锁 和 不可重入锁
总结
拓展一 :synchronized 与 锁策略的对应关系
2、synchronized 不是读写锁,只是一个普通互斥锁、
3、synchronized 既是一个重量级锁,也是一个轻量级锁。
4、轻量级锁的部分是基于自旋锁来实现的,重量级锁的部分是基于挂起等待锁来实现的。
5、非公平锁
6、可重入锁
相关面试题
1、 你是怎么理解乐观锁和悲观锁的,具体怎么实现呢?
2、 介绍下读写锁?
3、 什么是自旋锁,为什么要使用自旋锁策略呢,缺点是什么?
4、 synchronized 是可重入锁么?
CAS
什么是CAS?
CAS 如何帮我们解决一些线程安全问题?
1、基于CAS 能够实现“原子类”
2、基于 CAS 能够实现“自旋锁”
如何理解 CAS中的 ABA 问题?- 面试主要问的问题
如何处理ABA问题
相关面试题
1、 讲解下你自己理解的 CAS 机制
2、ABA问题怎么解决?
synchronized 中的 锁优化的机制
基本特点 - 前面讲过的
synchronized 几个典型的优化手段
1、锁膨胀/锁升级
2、锁粗化
3、锁消除
Java中的 JUC 包
callable接口
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class Test2 {
public static void main(String[] args) {
// 通过 Callable 来描述一个任务
Callable<Integer> callable = new Callable<Integer>() {
@Override
public Integer call() throws Exception {
int sum = 0;
for(int i = 1;i <= 1_000;i++){
sum+=i;
}
return sum;
}
};
// 为了让线程执行 callable 中的任务
// 光使用构造方法还不够,还需要一个辅助的类: FutureTask
FutureTask task = new FutureTask(callable);
// 创建线程,来完成这里的计算工作
Thread t = new Thread(task);
t.start();
// 凭着task(小票)来获取 call方法的结果(自己的麻辣烫)
// get 操作,
// 如果线程的任务没有执行完,get就会先陷入阻塞
// 一直阻塞到,任务完成了,得出结果为止。
try {
System.out.println(task.get());
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
}
}
ReentrantLock
ReentrantLock 的 基础用法
ReentrantLock 和 synchronized 区别
2、synchronized 不需要手动释放锁,出了代码块,锁就自然释放了。
ReentrantLock 必须要手动释放锁,要谨防忘记释放。
(重点)3、synchronized 如果竞争锁失败,就会阻塞等待。
ReentrantLock 除了会阻塞等待,还有一手:trylock【失败了,就直接返回】
(重点)4、synchronized 是一个非公平锁。
而 ReentrantLock 提供了 非公平锁 和 公平锁 两个版本!!!
在构造方法中,通过参数来指定 当前是公平锁,还是非公平锁。
5、基于 synchronized 衍生出来的等待机制,是 wait 和notify。功能是相对有限的。
基于 ReentrantLock 衍生出来的等待机制,是 Condition 类(又可称为条件变量)。
功能上要更丰富一些。
在日常开发中,绝大部分情况下,synchronized 就够用了
信号量 - Semaphore
回过头来,信号量表示的是可用资源的个数。
那么,它和锁有什么关系?
锁可以视为“二元信号量”:可用资源就一个,计数器的取值,非0即1(二元)。
CountDownLatch
import java.util.concurrent.CountDownLatch;
public class Test5 {
public static void main(String[] args) {
// 构造方法的参数表示有几个选手参赛
CountDownLatch latch = new CountDownLatch(10);
for (int i = 0; i < 10; i++) {
Thread t = new Thread(()->{
try {
Thread.sleep(3000);
System.out.println(Thread.currentThread().getName() + "达到终点");
latch.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t.start();
}
// 裁判 需要等待所有的编程到达
// 当这些线程没有执行完的时候,await 就阻塞等待
// 所有的线程都执行完了,await 才返回
try {
latch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("比赛结束");
}
}
线程安全的集合类
多线程环境使用 ArrayList
多线程环境使用队列