多线程进阶-线程安全问题 Synchronized详细讲解

2024-07-06

多线程进阶-线程安全问题

1.多线程带来的的风险-线程安全 (重点)

1.1观察线程不安全

1.2产生线程安全问题原因:

1.3解决上述问题的办法:加锁

2.关键字-监视器锁monitor lock

2.1 synchronized 的特性

(1) 互斥

(2) 刷新内存

(3) 可重入

2.2 synchronized 使用示例

(1) 直接修饰普通方法: 锁的 SynchronizedDemo 对象

(2) 修饰静态方法: 锁的 SynchronizedDemo 类的对象

(3) 修饰代码块: 明确指定锁哪个对象.

锁类对象

3.死锁

3.1常见的死锁

3.2死锁的成因

3.3解决死锁的方法

1.多线程带来的的风险-线程安全 (重点)

1.1观察线程不安全

线程安全是整个多线程中,最复杂的部分,也是最重要的部分。 在单个线程环境下,执行完全正确但是如果同样的代码,让多个线程去同时执行此时就可能会出现bug这就是“线程安全问题”/“线程不安全”

static class Counter {

public int count = 0;

void increase() {

count++;

}

}

public static void main(String[] args) throws InterruptedException {

final Counter counter = new Counter();

Thread t1 = new Thread(() -> {

for (int i = 0; i < 50000; i++) {

counter.increase();

}

});

Thread t2 = new Thread(() -> {

for (int i = 0; i < 50000; i++) {

counter.increase();

}

});

t1.start();

t2.start();

t1.join();

t2.join();

System.out.println(counter.count);

}

运行结果

预期结果两个线程各增5w次,最后count=10w 实际结果和10w差别很大

for (int i = 0; i < 50000; i++) {

counter.increase();

}

这个代码,在单线程中执行100%正确,但是,两个线程,并发的进行上述循环,此时逻辑就可能出现问题。

count++ 这个操作,本质上,是分成三步进行的~~

站在cpu的角度上,count++ 是由 cpu 通过三个指令来实现的~~

(1)load 把数据从内存,读到cpu寄存器中 (2)add 把寄存器中的数据进行+ 1 (3)save 把寄存器中的数据,保存到内存中 如果是多个线程执行上述代码,由于线程之间的调度顺序,是"随机"的,就会导致在有些调度顺序下,上述的逻辑就会出现问题.

此处,这两个线程执行count++中间会产生多少种不同的执行顺序的情况?? 排列组合问题 此处其实是 无数种 !!! 因为也可能存在,t1执行一次count++的时候,t2执行了两次或者t1执行一次count++,t2执行了三次… 结合上述讨论,就意识