[JAVAee]线程安全

这篇具有很好参考价值的文章主要介绍了[JAVAee]线程安全。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

线程安全的理解

线程不安全的原因

①非原子性

②可见性

③代码重排序

体会何为不安全的线程 

保证线程安全


一个代码在多线程的环境下就很容易出现错误.

线程安全的理解

  线程安全是什么呢?通俗的来讲,线程安全就是在多线程的环境下,代码的结果是符合我们预期的,就可以称这个线程是安全的.

线程不安全的原因

①非原子性

关于原子性:

  原子是最小的粒子,不可被分割.所以,原子性就是代表一系列不能被分割的操作.

  可以这样说,在一节开往学校的火车的10号车厢上.你因为喝了太多水,突然想上厕所.于是便走到10号车险的公共厕所上,开展了"观察厕所是否有人","打开厕所门","进入厕所","关闭厕所门","上锁","上厕所","解锁","打开厕所门","走出厕所","关闭厕所门"着这一系列的操作.

 我们应该很容易理解这一系列操作有着原子性,既是是连续且不可被分割的吧.总不能在你进入厕所关上门后允许有人打开你的厕所门.

 而上锁这一操作就很好的保持了这一系列的原子性,因为当你在厕所执行"任务"的时候,有人想要打开你的厕所门也进行不了这一操作.因为此时门被上锁了,是不可能被打开的.除非你在里面进行了解锁这一操作.

在java当中,原子性是一条语句吗?并不是的:

int n = 0;
n++;

像是上面的代码的"n++"语句,是不是就很容易被理解具有原子性.

java是一门高级编程语言,在其之下的称之为汇编语言.在java中我们只是看到简单的n++一条语句,而在汇编语言中他的代码大致逻辑是:

  • 从内存把数据读取到cpu中
  • 进行数据的更新
  • 将新数据写回cpu中

因为进程里的线程的调度是具有随机性的,在你执行把数据更新完但还没有写回这一步的时候,调度到了另一个线程,而且新被调度到的线程也要使用这个变量.这时候就会发生不对等的情况.

②可见性

  在我们线程的基础知识中提及到,同一个进程中的多个线程之间是具有关联性的,线程间也共享着同一个空间.在这一基础上:可见性指, 一个线程对共享变量值的修改,能够及时地被其他线程看到.

JMM内存模型:

 JMM模型是一个抽象的逻辑型,规定了一个进程中的所有变量都要存储在主内存中,进程中的线程又是共享同一个空间,所以说同一个线程中的所有线程都可以访问到主内存.同时,进程中的线程又有一个单独的工作内存.

  • 当线程要读取一个主内存中的共享变量时,先要把主内存中的共享变量拷贝到工作内存中,再从工作内存中读取此数据
  • 当线程要更新一个主内存中的共享变量时,先要把主内存中的共享变量拷贝到工作内存中并进行更新拷贝过来的副本,再去更改主内存的共享变量 

[JAVAee]线程安全,java,jvm,开发语言

 因为这个可见性的缘故,线程1要修改共享变量的时候,线程2的同一个变量的数据还没有得到更新.就会导致结果的偏差.

③代码重排序

JVM、CPU指令集会对代码进行优化

编译器对于指令重排序的前提是 "保持逻辑不发生变化". 这一点在单线程环境下比较容易判断, 但
是在多线程环境下就没那么容易了, 多线程的代码执行复杂程度更高, 编译器很难在编译阶段对代
码的执行效果进行预测, 因此激进的重排序很容易导致优化后的逻辑和之前不等价

体会何为不安全的线程 

public class Test {
    public static int n = 0;
    public static void count(){
        n++;
    }

    public static void main(String[] args) throws InterruptedException {
        //线程0
        Thread thread0 = new Thread(() ->{
           for(int i = 0; i < 10000; i++){
               count();//每次调用,n+1.预取:线程0能使n加上10000次
           }
        });
        //线程1
        Thread thread1 = new Thread(() ->{
            for(int i = 0; i < 10000; i++){
                count();//每次调用,n+1.预取:线程1能使n加上10000次
            }
        });
        thread0.start();//启动
        thread1.start();
        //等到线程0,线程1执行完成才去打印
        thread0.join();
        thread1.join();
        //我们的预期是,线程0加上10000,线程1加上10000.n为20000
        System.out.println(n);
    }
}

[JAVAee]线程安全,java,jvm,开发语言

出现的结果不符合我们期望,就是因为count方法中没有保持原子性,导致两个线程间同一数据读出和写出的步骤重合而最终的数据错误.

[JAVAee]线程安全,java,jvm,开发语言

通俗的来说,在多线程环境下可能出现线程不安全的原因有:

  • 线程是抢占式执行的,随机调度导致的
  • 多个线程修改同一个变量,会出现可见性的问题
  • 线程中的修改操作不是原子性的 

保证线程安全

   可以给一个代码块使用synchronized关键字进行修饰,达到了上锁的作用.保证其原子性

   我们更新一下,在count方法上使用synchronized修饰.在每一个线程调用的时候,其他线程对于这个count方法都会变为阻塞状态,即不能调用这个方法.

public static synchronized void count(){
        n++;
    }

 最后的结果,我们就可以很好的保证了预期结果的正确,达到了多线程环境下的安全.

[JAVAee]线程安全,java,jvm,开发语言文章来源地址https://www.toymoban.com/news/detail-600900.html

到了这里,关于[JAVAee]线程安全的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • [JAVAee]线程安全

    目录 线程安全的理解 线程不安全的原因 ①非原子性 ②可见性 ③代码重排序 体会何为不安全的线程  保证线程安全 一个代码在多线程的环境下就很容易出现错误.   线程安全是什么呢?通俗的来讲,线程安全就是在多线程的环境下,代码的结果是 符合我们预期 的,就可以称这个

    2024年02月16日
    浏览(32)
  • 【JavaEE】线程安全的集合类

    作者主页:paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将基础知识一网打尽,希望可以帮到读者们哦。 其他专栏:《MySQL》《C语言》

    2024年01月20日
    浏览(39)
  • 【JavaEE】多线程之线程安全(synchronized篇),死锁问题

    线程安全问题 观察线程不安全 线程安全问题的原因  从原子性入手解决线程安全问题 ——synchronized synchronized的使用方法  synchronized的互斥性和可重入性 死锁 死锁的三个典型情况  死锁的四个必要条件  破除死锁 在前面的章节中,我们也了解到多线程为我们的程序带来了

    2024年02月01日
    浏览(55)
  • 【JavaEE初阶】 线程安全的集合类

    原来的集合类, 大部分都不是线程安全的. Vector, Stack, HashTable, 是线程安全的(不建议用), 其他的集合类不是线程安全的. 为什么不建议使用呢? 因为我们在使用的时候,这些类就会自动的加锁,虽然编译器会自动优化为没有锁竞争的线程进行锁消除的优化,但是呢万一编译器没

    2024年02月08日
    浏览(33)
  • 【JavaEE】并发编程(多线程)线程安全问题&内存可见性&指令重排序

    目录 第一个问题:什么是线程安全问题? 第二个问题:为什么会出现线程安全问题?  第三个问题:如何解决多线程安全问题?  第四个问题:产生线程不安全的原因有哪些?  第五个问题:内存可见性问题及解决方案  第六个问题:指令重排序问题? 线程安全就是多线程

    2024年02月01日
    浏览(62)
  • javaEE初阶——多线程(九)——JUC常见的类以及线程安全的集合类

    T04BF 👋专栏: 算法|JAVA|MySQL|C语言 🫵 小比特 大梦想 此篇文章与大家分享多线程专题的最后一篇文章:关于JUC常见的类以及线程安全的集合类 如果有不足的或者错误的请您指出! 3.1Callable接口 Callable和Runnable一样,都是用来描述一个任务的 但是区别在于 ,用Callable描述的任务是有

    2024年04月25日
    浏览(42)
  • 码出高效:Java开发手册笔记(线程安全)

        并发与并行的目标都是尽可能快地执行完所有任务。以医生坐诊为例,某个科室有两个专家同时出诊,这就是两个并行任务,其中一个医生,时而问诊,时而查看化验单,然后继续问诊,突然又中断去处理病人的咨询,这就是并发。在并发环境下,由于程序的封闭性全

    2024年02月08日
    浏览(38)
  • 【JavaEE面试题(九)线程安全问题的原因和解决方案】

    大家观察下是否适用多线程的现象是否一致?同时尝试思考下为什么会有这样的现象发生呢? 原因是 1.load 2. add 3. save 注意:可能会导致 小于5w 想给出一个线程安全的确切定义是复杂的,但我们可以这样认为: 如果多线程环境下代码运行的结果是符合我们预期的,即在单线

    2024年02月16日
    浏览(43)
  • JavaEE初阶:Java线程的状态

    目录 获取当前线程引用 休眠当前线程  线程的状态 1.NEW               2.TERMINATED  3.RUNNABLE 4.WAITING 5.TIMED_WAITING 6.BLOCKED 多线程的意义 单线程  多线程 这个方法返回当前线程的引用。但是我们会对static有疑惑,这其实是一个静态方法,更好的说法是这是一个 类方法, 调用这

    2024年02月11日
    浏览(39)
  • 【JavaEE】Java中的多线程 (Thread类)

    作者主页: paper jie_博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JavaEE》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精力)打造,将基础知识一网打尽,希望可以帮到读者们哦。 其他专栏:《MySQL》《

    2024年02月05日
    浏览(52)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包