给我一首歌的时间,带你了解线程安全和死锁(1)

举报
bug郭 发表于 2022/09/30 22:54:58 2022/09/30
【摘要】 大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流作者简介:CSDN java领域新星创作者blog.csdn.net/bug…掘金LV3用户 juejin.cn/user/bug…阿里云社区专家博主,星级博主,developer.a...

大家好,我是bug郭,一名双非科班的在校大学生。对C/JAVA、数据结构、Linux及MySql、算法等领域感兴趣,喜欢将所学知识写成博客记录下来。 希望该文章对你有所帮助!如果有错误请大佬们指正!共同学习交流

作者简介:

什么是线程安全

线程安全是多线程编程时的计算机程序代码中的一个概念。在拥有共享数据的多条线程并行执行的程序中,线程安全的代码会通过同步机制保证各个线程都可以正常且正确的执行,不会出现数据污染等意外情况。(百度百科)

我们上节内容已经了解了java中的多线程状态!
而就是因为线程是并发执行的,而每个线程在不同的时刻会有不同都状态!
而线程是抢占式执行,随机的,没有所谓的顺序可言!
而随机就是不受我们程序员控制!进而会导致很多问题,出现一下bug!
而线程安全问题,就是由于线程的抢占式执行和其他线程特性而导致的bug,这就是线程安全!
注意区别,并不是我们大家所熟知的网络安全,因为黑客而导致的安全问题!!!

线程不安全实例

当我们对一个数自加时,如果自加要得到的数较大,那么我们可能会想到使用多线程,毕竟现学现用嘛!

让我们看看效果:

public class Thread_15 {
    private static int a = 0;//同时对a自加
    private static final int count = 10_0000;//分别自加10万
    public static void increase(){
        for (int i = 0; i < count; i++) {
            a++;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1 =new Thread(){
            @Override
            public void run() {
                increase(); //线程t1对a进行自加10万次
            }
        };
        Thread t2 =new Thread(){
            @Override
            public void run() {
                increase(); //线程t2对a进行自加10万次
            }
        };
        t1.start();
        t2.start();

        t1.join();//等待t1线程结束
        t2.join();//等待t2线程结束
        System.out.println("多线程执行后后a的结果:"+a);
    }
}

在这里插入图片描述
我们可以看到和我们预期的结果并不匹配!
我们预期是两个线程分别自加10万,结果应该是20万!

为啥会出现这样的情况呢?

我们来分析一下a++cpu中到底干了啥!

我们站在cpu的角度:其实a++cpu中需要3条指令才能完成该操作!
分别为:

  1. 在内存中拿到 a值,加载到cpu的寄存器中(load)
  2. a值自增操作(add)
  3. 把寄存器中的值放回到内存(save)

上述3部操作才完成了a++!

而我们知道我们的线程t1t2是并发执行的!

而由于线程的抢占性和随机性!我们无法确定上述3条指令,t1t2线程的执行时刻!
那么便会出现很多先后排列的情况!

我们把各种情况用时间轴的方式一一列举出!

  1. 串行执行,在t1线程对a完成一次自加后,t2在执行!显然这里并不会发生线程安全问题!

在这里插入图片描述
2. 并行执行,两个线程都同时在内存中取出a值进行自加!而此时就会出现线程安全问题,两次自加,结果就a就自加了一次!!!
在这里插入图片描述
上述的3种指令顺序都会导致,线程不安全!!!

分析上述案例,我们知道,如果我们多个线程对一个资源进行操作!由于有多条指令,可能会导致线程不安全问题!

我们知道我们两个线程对a自加,结果肯定在10万和20万之间!

两个极端:
10万 说明两个线程彻底在cpu核并行执行了!
20万 说明两个线程在cpu多核并发执行!

显然上述两种结果出现的情况微乎其微!

如何解决上面出现的线程不安全问题呢?

加锁: synchronized

java

就好比我们去上洗手间!
如果我们进去后,将门反锁!那么其他线程就拿不到该资源!
除非你这个线程结束,其他线程才能拿到该资源进行执行线程!
所以这里加锁,可以避免线程安全问题!

我们java提供了一个加锁的关键字synchronized!

我们演示一下加锁后的效果!
在这里插入图片描述

public class Thread_16 {
    private static int a = 0;//同时对a自加
    private static final int count = 10_0000;//分别自加10万
    public synchronized static void increase(){//对该方法加锁!!!
        for (int i = 0; i < count; i++) {
            a++;
        }
    }
    public static void main(String[] args) throws InterruptedException {
        Thread t1 =new Thread(){
            @Override
            public void run() {
                increase(); //线程t1对a进行自加10万次
            }
        };
        Thread t2 =new Thread(){
            @Override
            public void run() {
                increase(); //线程t2对a进行自加10万次
            }
        };
        t1.start();
        t2.start();

        t1.join();//等待t1线程结束
        t2.join();//等待t2线程结束
        System.out.println("加锁后a的结果:"+a);
    }
}

在这里插入图片描述

我们又想到了另一个问题,如果这样对线程加锁,那么多线程还有意义嘛!这不是和单线程一样了嘛!!!

显然我们在对一个数进行自加采用多线程,并没有达到多线程的效果!!
但是,如果我们避免多个线程同时对一个资源的更改,那么多线程就不会出现线程不安全问题,也发挥了多线程的优势!

【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。