jmu-java-07多线程-同步访问、_面试再被问到 Java 多线程安全问题,就这么回答他...

谈到 Java 的多线程编程,一定绕不开线程的安全性,线程安全又包括原子性,可见性和有序性等特性。今天,我们就来看看他们之间的关联和实现原理。

线程与竞态

开发的应用程序会在一个进程中运行,换句话说进程就是程序的运行实例。运行一个 Java 程序的实质就是运行了一个 Java 虚拟机进程。

如果说一个进程可以包括多个线程,并且这些线程会共享进程中的资源。任何一段代码会运行在一个线程中,也运行在多个线程中。线程所要完成的计算被称为任务。

为了提高程序的效率,我们会生成多个任务一起工作,这种工作模式有可能是并行的,A 任务在执行的时候,B 任务也在执行。

如果多个任务(线程)在执行过程中,操作相同的资源(变量),这个资源(变量)被称为共享资源(变量)。

当多个线程同时对共享资源进行操作时,例如:写资源,就会出现竞态,它会导致被操作的资源在不同时间看到的结果不同。

来看看多个线程访问相同的资源/变量的例子如下:

ba2acfc07e536b27a2334a43907bdd13.png

当线程 A 和 B 同时执行 Counter 对象中的 add() 方法时,在无法知道两个线程如何切换的情况下,JVM 会按照下面的顺序来执行代码:

  • 从内存获取 this.count 的值放到寄存器。
  • 将寄存器中的值增加 value。
  • 将寄存器中的值写回内存。

上面操作在线程 A 和 B 交错执行时,会出现以下情况:

d333ea44dd06fa57e1a71ccb5030690a.png

两个线程分别加 2 和 3 到 count 变量上,我们希望的结果是,两个线程执行后 count 的值等于 5。

但是,两个线程交叉执行,即使两个线程从内存中读出的初始值都是 0,之后各自加了 2 和 3,并分别写回内存。

然而,最终的值并不是期望的 5,而是最后写回内存的那个线程(A 线程)的值(3)。

最后写回内存的是线程 A 所以结果是 3,但也有可能是线程 B 最后写回内存,所以结果是不可知的。

因此,如果没有采用同步机制,线程间的交叉写资源/变量,结果是不可控的。

我们把这种一个计算结果的正确性与时间有关的现象称作竞态(Race Condition)。

线程安全

前面我们谈到,当多线程同时写一个资源/变量的时候会出现竞态的情况。这种情况的发生会造成,最终结果的不确定性。

如果把这个被写的资源看成 Java 中的一个类的话,这个类不是线程安全的。

即便这个类在单线程环境下运作正常,但在多线程环境下就无法正常运行。例如:ArrayList,HashMap,SimpledateFormat。

那么,为了做到线程安全,需要从以下三个方面考虑,分别是:

  • 原子性
  • 可见性
  • 有序性

原子性

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值