面试🤺🤺🤺<持续更新
>
- 🗡️JAVA
- ⚔️ Java基础篇
- ⚔️ I/O、泛型、反射、异常篇
- ⚔️ Java容器篇
- ⚔️ JUC
- ⚔️ JVM
- ⚔️ 新特性
- ⚔️ 补充点
⚔️ JUC补充篇
前言
通过本文,你将了解如下内容:
- 🕐 单例模式的双重校验锁
- 🕑 消费者-生产者的虚假唤醒
- 🕒 面向对象基础
- 🕓 Java语言常用类拥有那些方法
穿插在文章内的补充知识点(希望也能看看,说不定有不一样的收获❤️❤️❤️)`
提示:以下是本篇文章正文内容
一、🕐 单例模式的双重校验锁
/**
* @desc Singleton 懒汉式
* @author lanan
* @date 2022-08-04 17:25:42
**/
class Singleton {
private static Singleton singleton;
private Singleton() {}
public static Singleton getSingleton() {
if (singleton != null) {
singleton = new Singleton();
}
return singleton;
}
}
以上写法在多线程情况下可能产生的问题:
- 没有做同步处理,每次都会new一个singleton对象去替换掉原singleton对象
- singleton = new Singleton();操作可能会发生指令重排
– 1、为singleton分配内存
– 2、链接的准备阶段赋默认值
– 3、将new Singleton()堆中地址赋给singleton
正常执行是1=>2=>3,如果发生指令重排,可能导致执行顺序变成1=>3=>2解决办法,使用synchronized保证同步,再使用双重校验锁优化锁的粒度,再使用volatile禁止指令重排
/**
* @desc Singleton 双重校验锁 + volatile
* @author lanan
* @date 2022-08-04 17:35:48
**/
class Singleton {
private volatile static Singleton singleton;
private Singleton() {}
public static Singleton getSingleton() {
if (singleton == null) {
synchronized (Singleton.class) {
if (singleton == null) {
singleton = new Singleton();
}
}
}
return singleton;
}
}
二、🕐 消费者-生产者的虚假唤醒
- 实现先慢慢的看一下代码,代码的目的就是输出1和0,但结果却超出了这个范围,原因是在if判断里面进入了wait状态,进入wait状态之前进行了条件判断,当被唤醒后,不会再去校验本次是否符合if判断条件,而是直接执行下面操作(注意:线程从wait状态唤醒,会继续执行之前操作):
- eg:A为-1操作线程、B为-1操作线程、C为+1操作线程、D为+1操作线程
– A、B当number为0,进入等待,否则-1并执行notifyAll
– C、D当number不为0,进入等待,否则+1并执行notifyAll
– 多线程情况下,假设一种场景,执行C,number=1;再执行C、D,两种都进入阻塞;此时执行A,number=0,C、D被唤醒;依次执行C、D,执行C,number=1,执行D,从wait处继续执行,number=2,出现不应该出现的数字了
– 原因:D唤醒后,number=1,应该再次进入wait,但if不具备再次校验
– 解决:将if改为while,唤醒后再次判断是否符合条件
/**
* 线程之间的通信问题:生产者和消费者问题! 等待唤醒,通知唤醒
* 线程交替执行 A B 操作同一个变量 number = 0
* A number + 1
* B number - 1
* @author LanAn
* @date 2022/8/4-17:03
*/
public class MainTest {
public static void main(String[] args) {
Test t = new Test();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
t.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"A").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
t.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"B").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
t.increment();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"C").start();
new Thread(()->{
for (int i = 0; i < 10; i++) {
try {
t.decrement();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
},"D").start();
}
}
// 判断等待,业务,通知
class Test{ // 数字 资源类
private int number = 0;
//+1
public synchronized void increment() throws InterruptedException {
if (number!=0){ //0
// while (number!=0){ // 0
// 等待
this.wait();
}
number++;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我+1完毕了
this.notifyAll();
}
//-1
public synchronized void decrement() throws InterruptedException {
if (number==0){ // 1
// while (number==0){ // 1
// 等待
this.wait();
}
number--;
System.out.println(Thread.currentThread().getName()+"=>"+number);
// 通知其他线程,我-1完毕了
this.notifyAll();
}
}
总结
资料来源:
- guide哥.【JavaGuide】. 面试指南. JAVA,.