Java线程与锁-6

1 前言

2 Synchronization

3 Wait Sets and Notification

4 Sleep and Yield

5 Memory Model

5.1 Shared Variables

5.2 Actions

5.3 Programs and Program Order

5.4 Synchronization Order

5.5 Happens-before Order

5.6 Executions

5.7 Well-Formed Executions

5.8Executions and Causality Requirements

5.9 Observable Behavior and Nonterminating Executions

有些程序被执行一段时间之后终止,有些程序会被无时间限制地执行而不会终止,本章节主要描述程序的可观察行为以及无终止的执行。一段程序的可观察行为是由该段程序的外部动作的有限集构成,例如,一段程序简单地循环输出n次的字符串常量,n是有限的正整数,则这些外部动作构成了该段程序的可观察行为。

在Java语言中,executionTermination动作是在当所有线程都终止的时候执行,其表示终止一段程序的执行。hang动作是一个挂起的动作,当一个行为由外部动作以及一个挂起动作构成,其表示当所有的外部动作执行完成,则程序会被无时间限制地或者无终止地执行。因此,当所有的线程被阻塞,则程序被挂起,当程序被无时间限制地而不再执行任何外部动作,则程序被挂起。

6 final Field Semantics

定义成final的作用域的类型值仅被初始化一次,一般情况下初始化成功之后永远不会再被改变。编译器可以将final修饰的类型值存储在寄存器中而在使用的过程中不需要重新被加载。final修饰的作用域一般应用在无锁的、线程安全的不可变对象中,则该对象对于所有的线程在发生数据竞争的时候都是不可变的,该机制能有效保证代码在一些使用场景中的数据安全性,例如String类是使用final修饰的不变类。

在Java语言中,一个对象在完成构造函数执行之后才会被认为是完全初始化完成,而一个线程只能看到引用完全初始化完成的对象,该机制保证线程能看到正确的、由final修饰的作用域的初始化值。

安全地使用final修饰作用域的方法非常简单,在一个对象中定义一个final类型的作用域,或者在该对象的构造函数中设置初始化值,在该对象的构造函数初始化完成之前,即使其他线程引用了该对象,则其他线程也能看到正确的初始化值,也就是,在多线程的并发环境中,其他线程能看到最新版本的、由final修饰的作用域的初始化值。

如上所示,假设,存在两个并发线程,其中一个线程t1调用writer方法,另一个线程t2调用reader方法,由于x是由final修饰的作用域的值,而y是由非final修饰的作用域的值,则,则t2能看到最新版本的、x的值等于3,而t2可能看到y的多个版本的值0或者4,也就是。在本例的代码中,由于在多线程并发环境中未使用同步机制约束非final修饰的作用域的值的初始化过程。

假设存在对象o,c是对象o的构造函数,f是o定义的final修饰的作用域,在c中写入f的值,当在c正常退出或者异常退出的时候,执行一个对f的freeze类型动作。假如,在c中调用另外一个o的构造函数c1,则在c1结束的时候执行对f的freeze类型动作。对应final修饰的作用域,JVM使用特殊的内存处理模型,其中,包括解引用链函数dereferences()以及内存链函数mc()。

如果在相同的线程中,对final修饰的作用域的读写操作,符合hb的顺序关系,也就是,所有的读动作可见到发生在该次读动作之前的写动作。

Java提供反射的机制支持修改final修饰的作用域的值,在反射机制的作用下,Java不保证在多线程并发的环境中,线程读取到最新的值。

如上所示,JVM在编译的过程中记录了i的值等于1,但是,对于g方法使用反射机制修改x的值的过程发生在运行时而具有不确定性,因此,j-i的运行结果可能是1、0或者-1。

7 Word Tearing

JVM的实现规定了每个对象的每个作用域以及数组的每个元素都是不同的,因此,在多线程并发的环境中,不同的线程同时更新同一个数组相邻的两个元素也不会发生相互作用以及相互影响,一些处理器不提供方法更新一个单一的字节,而将全部数组的字节读出,再更新其中一个数组的字节,在写入全部数组的字节,该方式是不合法的,因此,JVM提供其他方法兼容该处理器对应的错误。

如上所示,在多线程的并发环境中,出现覆盖相邻字节的情况,JVM提供方法兼容该错误。

8 Non-Atomic Treatment of double and long

在JVM的内存模型中,对一个非volatile类型的long值或者double值的写入操作是被分割成两次独立的写,分别写入两次的32位的字节,在多线程的并发环境中,可能会一个严重的问题,可见到其中一个线程写入左边的32位字节,以及可见到另外一个线程写入右边的32位字节。但是,使用volatile修饰的long值或者double值是原子操作,保证一次只能一个线程写入值。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

wangys2006

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值