美文网首页
1.2.1 线程安全之可见性问题

1.2.1 线程安全之可见性问题

作者: 叶凯飞 | 来源:发表于2020-02-13 17:00 被阅读0次

多线程中的问题

  1. 所见非所得
  2. 无法内眼去检查程序的准确性
  3. 不同的运行平台有不同的表现
  4. 错误很难重现

工作内存缓存

截屏2020-02-13下午4.57.58.png

内存模型的含义

内存模型描述程序的可能行为。

Java编程语言内存模型通过检查执行跟踪中的每个读操作,并根据某些规则检查该读操作观察到的写操作是否有效来工作。

只要程序的所有执行产生的结果都可以由内存模型预测。具体的实现者任意实现,包括操作的重新排序和删除不必要的同步。

内存模型决定了在程序的每个点上可以读取什么值。

Shared Variables共享变量描述

可以在线程之间共享的内存称为共享内存或堆内存

所有实例字段、静态字段和数组元素都存储在堆内存中。

如果至少有一个访问是写的,那么对同一个变量的两次访问(读或写)是冲突的。

线程操作的定义

操作定义:
  • write 要写的变量以及要写的值
  • read 要读的变量以及可见的写入值(由此,我们可以确定可见的值)。
  • lock 要锁定的管程(监视器monitor)
  • unlock 要解锁的管程
  • 外部操作(socket等等..)
  • 启动和终止

程序顺序: 如果一个程序没有数据竞争,那么程序的所有执行看起来都是顺序一致的。

本规范只涉及线程间的操作;

对于同步的规则定义

  • 对于监视器m的解锁与所有后续操作对于m的加锁同步
  • 对volatile变量v的写入,与所有其他线程后续对v的读同步
  • 启动线程的操作与线程中的第一个操作同步
  • 对于每个属性写入默认值(0,false,null)与每个线程对其进行的操作同步
  • 线程T1的最后操作与线程T2发现线程T1已经结束同步(isAlive,join可以判断线程是否终结)
  • 如果线程T1中断了线程T2,那么线程T1的中断操作与其他所有线程发现T2被中断了同步,通过抛出interruptedException异常,或者调用Thread.interrupt 或 Thread.isInterrupted。

Happens - before先行发生原则

Happens - before关系主要用于强调两个有冲突的动作之间的顺序,以及定义数据争用的发生时机。

具体的虚拟机实现,有必要确保以下原则的成立

  • 某个线程中的每个动作都 happens - before 该线程中该动作后面的动作。
  • 某个管程上的unlock动作 happens - before 同一个管程上后续的lock动作
  • 对某个volatile字段的写操作 happens - before 某个后续对该volatile字段的读操作
  • 在某个线程对象上调用start()方法 happens - before 该启动了的线程中的任意动作
  • 某个线程中的所有动作 happens - before 任意其他线程成功从该线程对象上的join()中返回
  • 如果某个动作a happens - before 动作b,且b happens - before 动作c,则有a happens - before c

当程序包含两个没有被 happens - before 关系排序的冲突访问时,就称存在数据争用

遵守了这个原则,也就意味着有些代码不能进行重排序,有些数据不能缓存!!

volatile关键字

可见性问题:让一个线程对共享变量的修改,能够及时的被其他线程看到。

根据JMM中规定的 happen - before 和 同步原则:
对某个volatile字段的写操作 happens - before 每个后续对该volatile字段的读操作。
对volatile变量v的写入,与所有其他线程后续对v的读同步。

要满足这些条件,所以volatile关键字就有这些功能:

  1. 禁止缓存;

    volatile变量的访问控制符会加个ACC_VOLATILE。

  2. 对volatile变量相关的指令不做重排序。

final在JMM中的处理

final在该对象的构造函数中设置对象的字段,当线程看到该对象时,将始终看到该对象的final字段的正确构造版本。伪代码示例:f = new finalDemo(); 读取到的f.x一定为最新,x为final字段。

如果在构造函数中设置字段后读取,则会看到该final字段分配的值,否则它将看到默认值;

伪代码示例: public finalDemo(){x = 1; y = x;}; y会等于1;

读取该共享对象的final成员变量之前,先要读取共享变量。

伪代码示例:r = new ReferenceObj(); k = r.f;这两个操作不能重排序

通常static final是不可以修改的字段,然而System.in ,System.out和System.err是static final 字段,遗留原因,必须允许通过set方法改变,我们将这些字段称为写保护,以区别于普通final字段;

Word Tearing字节处理

![截屏2020-02-13下午4.57.20.png](https://img.haomeiwen.com/i19744837/d5ab3217c5c4fb94.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)

相关文章

  • 1.2.1 线程安全之可见性问题

    多线程中的问题 所见非所得 无法内眼去检查程序的准确性 不同的运行平台有不同的表现 错误很难重现 工作内存缓存 内...

  • 线程安全之可见性问题

    Java内存模型 VS JVM运行时数据区 首先Java内存模型(JMM)和JVM运行时数据区并不是一个东西,许多...

  • 并发编程-安全性、活跃性以及性能问题

    一、安全性问题 并发 Bug 的三个主要源头:原子性问题、可见性问题和有序性问题。理论上线程安全的程序,就要避免出...

  • 线程安全可见性问题

    如上,按照这段代码的目的来说,会在两秒后打印出 i 的值并结束。但运行后并不会结束,如下图 这就是一个可见性问题 ...

  • Java线程安全-可见性问题

    Java内存模型(JMM) 与 JVM运行时数据区 Java内存模型是《Java语言规范》中,描述对java语...

  • 多线程之线程安全性

    多线程环境下使用非线程安全类会导致线程安全问题。线程安全问题表现为原子性,有序性,可见性 在讲述线程安全三大特性之...

  • 02.线程安全性问题

    [TOC] 安全性问题概述 什么是安全性问题 多线程情况下的安全问题,是指数据的一致性问题,在多线程环境下,多个线...

  • 互斥锁,解决原子性问题

    并发编程有3个源头性问题:缓存导致的可见性问题,编译优化导致的有序性问题,以及线程切换导致的原子性问题。解决可见性...

  • Java并发编程学习笔记(一)

    本文来源:王宝令——《Java并发编程实战》 导致线程安全问题的原因 源头之一:缓存导致的可见性问题 一个线程对共...

  • 第2章 并发编程的其他基础知识

    目录 并行与并发区别 Java中的线程安全问题 Java中共享变量的内存可见性问题 synchronized关键字...

网友评论

      本文标题:1.2.1 线程安全之可见性问题

      本文链接:https://www.haomeiwen.com/subject/chdlfhtx.html