🌈🌈🌈今天给大家分享的是:什么是线程安全,在程序中多线程并发执行的时候,是否会产生线程不安全问题,以及如何解决线程不安全问题。
清风的CSDN博客
🛩️🛩️🛩️希望我的文章能对你有所帮助,有不足的地方还请各位看官多多指教,大家一起学习交流!🛩️🛩️🛩️
✈️✈️✈️动动你们发财的小手,点点关注点点赞!在此谢过啦!哈哈哈!✈️✈️✈️
目录
一、线程安全的概念
二、观察线程不安全
三、解决线程不安全问题
3.1 线程安全问题的原因
3.2 解决方案
一、线程安全的概念
二、观察线程不安全
首先,我们写一个多线程代码,一个线程负责把一个数加50000次,另一个线程也负责把这个数加50000次。(从0开始加)
class Counter{
private int count = 0;
public void increase(){
count++;
}
public int getCount() {
return count;
}
}
public class Test4 {
public static void main(String[] args) throws InterruptedException {
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.getCount());
}
}
程序运行结果如下:
按照我们的逻辑,希望最后得到的结果是100000,为什么会出现这么大的偏差呢?其实,这样的运行结果是由于线程不安全导致的。
事实上:上面的count++操作,在CPU指令角度看,本质上是三个操作:
- 把内存中的数据加载到CPU的寄存器中。(LOAD)
- 把寄存器中的寄存器进行加一运行算。(ADD)
- 把寄存器中的数据写回到内存中。
如果是单个线程执行,没有问题。但是如果是多个线程并发执行,就可能会出现错误。由于CPU调度线程的顺序是不确定的,因此这两个线程并发执行的过程中,线程1的操作可能会和线程2的操作相互影响,也就是说,这两个线程的命令的排列方式可能有很多种:
还有很多种情况,我就不一一列举了。
如果是按照下面的情况,那么结果是没有问题的:
执行完线程1后,继续执行线程2,结果为2,是正确结果。
那如果是按照下面的顺序,就会出现错误:
正常结果应该是2,但是这里的最终结果是1,发生了错误,虽然是自增两次,但是由于两个线程并发执行,就可能在一定的执行顺序下,导致运算的中间结果被覆盖了。也就是发生了线程不安全。 导致结果出现问题,而且得到的这个错误值,一定小于10w。
那么,如何解决线程不安全问题呢?
三、解决线程不安全问题
3.1 线程安全问题的原因
- 多个线程之间的调度顺序是随机的,操作系统使用抢占式执行的策略来调度线程。【根本原因】
- 多个线程同时修改同一个变量,容易产生线程安全问题。
- 进行的修改,不是原子性的。如果修改操作,能够按照原子的方式来完成,就不会出现线程安全问题。
- 内存可见性引起的线程安全问题。
- 指令重排序,引起的线程安全问题。
3.2 解决方案
我们可以把修改操作改成“原子性”的操作,即可以进行加锁操作。相当于是把一组操作,打包成一个整体的操作。此处这里的原子,是通过锁进行“互斥”,当前线程执行的时候,其他线程无法执行。对于加锁,Java引入了一个synchornized关键字。
通过对increase方法加锁,进入方法,就会加锁;出了方法,就会解锁。 当t1加锁之后,t2也尝试进行加锁,此时t2就会阻塞等待。这样也就保证了原子性。
class Counter{
private int count = 0;
synchronized public void increase(){
count++;
}
public int getCount() {
return count;
}
}
public class Test4 {
public static void main(String[] args) throws InterruptedException {
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.getCount());
}
}
运行结果如下:
🌈🌈🌈好啦,今天的分享就到这里!
🛩️🛩️🛩️希望各位看官读完文章后,能够有所提升。
🎉🎉🎉创作不易,还希望各位大佬支持一下!
✈️✈️✈️点赞,你的认可是我创作的动力!
⭐⭐⭐收藏,你的青睐是我努力的方向!
✏️✏️✏️评论:你的意见是我进步的财富!