Java线程有两个特性,可见性和有序性。多个线程之间是不能直接传递数据交互的,它们之间的交互只能通过共享变量来实现。
可见性:当一个对象在多个内存中都存在副本时,如果一个内存修改了共享变量,其它线程也应该能够看到被修改后的值。拿上篇博文中的例子来说明,在多个线程之间共享了PrintString类的一个对象,这个对象是被创建在主内存(堆内存)中,每个线程都有自己的工作内存(线程栈),工作内存存储了主内存PrintString对象的一个副本,当线程操作PrintString对象时,首先从主内存复制PrintString对象到工作内存中,然后执行代码printer.print(),改变了builder值,最后用工作内存PrintString刷新主内存PrintString。因此上篇博文的例子没有保证可见性。
有序性:多个线程执行时,CPU对线程的调度是随机的,我们不知道当前程序被执行到哪步就切换到了下一个线程,一个最经典的例子就是银行汇款问题,一个银行账户存 款100,这时一个人从该账户取10元,同时另一个人向该账户汇10元,那么余额应该还是100。那么此时可能发生这种情况,A线程负责取款,B线程负责 汇款,A从主内存读到100,B从主内存读到100,A执行减10操作,并将数据刷新到主内存,这时主内存数据100-10=90,而B内存执行加10操 作,并将数据刷新到主内存,最后主内存数据100+10=110,显然这是一个严重的问题,我们要保证A线程和B线程有序执行,先取款后汇款或者先汇款后取款。
为保证可见性和有序性,需要引入同步机制。JAVA关键字volatile和锁机制保证了线程间的同步。
volatile:一个变量可以被volatile修饰,在这种情况下内存模型(主内存和线程工作内存)确保所有线程可以看到一致的变量值。volatile变量具有可见性特性,但是不具备原子特性。这就是说线程能够自动发现 volatile 变量的最新值。Volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。因此,单独使用 volatile 还不足以实现计数器、互斥锁或任何具有与多个变量相关的不变式(Invariants)的类(例如 “start <=end”)。只能在有限的一些情形下使用 volatile 变量替代锁。要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:
- 对变量的写操作不依赖于当前值。
- 该变量没有包含在具有其他变量的不变式中。
第一个条件的限制使 volatile 变量不能用作线程安全计数器。虽然增量操作(x++
)看上去类似一个单独操作,实际上它是一个由读取-修改-写入操作序列组成的组合操作,必须以原子方式执行,而 volatile 不能提供必须的原子特性。实现正确的操作需要使 x
的值在操作期间保持不变,而 volatile 变量无法实现这点。大多数编程情形都会与这两个条件的其中之一冲突,使得 volatile 变量不能像 synchronized
那样普遍适用于实现线程安全。
因此,虽然volatile变量可以提供优于锁的性能优势,但是不能保证线程安全,所以最好不要用volatile保证性能安全,除非您确实是一个编程高手。
锁的讲解见下篇。
相关推荐
不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的。...JVM定义了自己的内存模型,屏蔽了底层平台内存管理细节,对于java开发人员,要清楚在jvm内存模型的基础上,如果解决多线程的可见性和有序性。
不同的平台,内存模型是不一样的,但是jvm的内存模型规范是统一的。...JVM定义了自己的内存模型,屏蔽了底层平台内存管理细节,对于java开发人员,要清楚在jvm模型的基础上,如果解决多线程的可见性和有序性。
主要介绍了了解Java多线程的可见性与有序性,在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。,需要的朋友可以参考下
本资源涵盖了Java并发编程的理论基础和实践,主要包括可见性、原子性和有序性的详细介绍,以及多线程的使用原因、好处和坏处等方面的内容。 Java并发编程是一种高效的编程技术,通过多线程实现将计算过程中不必要的...
如果应场景需要个更范围的原性保证,Java内存模型还提供了lock和unlock操作来满这种需求,尽管虚拟机未把lock与unlock操作直接开放给户使,但是却
那么这篇文章就先解决其中的可见性和有序性问题,引出了今天的主角:Java内存模型(面试并发的时候会经常考核到) 什么是Java内存模型? 现在知道了CPU缓存导致可见性、编译优化导致了有序性问题,那么最简单的方式...
java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...
解决并发问题,我们一般需要从原子性、可见性和有序性三方面入手,借助Java关键字及各种同 步工具类来实现。 原子性、可见性、有序性三特性: 1. 原子性:原子性就是说一个操作不能被打断,要么执行完要么不执行。 2...
本资源为您提供了关于 Java ...volatile 关键字: 解释 volatile 关键字的作用,探讨如何使用 volatile 实现线程间的可见性和有序性。 通过这份资源,将为您提供实用的指导,帮助您构建更稳定、可靠的多线程应用程序。
14、深入理解井发可见性、有序性、原子性与JMM内存模型 (1).pdf 15、CPU缓存架构详解&高性能内存队列Disruptor 实战 (1).pdf 16、常用并发设计模式精讲 (1).pdf designpattern.zip disruptor.zip forkjoin.zip jmm(1...
java面试题_java-interview-questions-master.zip2、在 Java 程序中怎么保证多线程的运行安全? 出现线程安全问题的原因一般都是三个原因: 1、 线程切换带来的原子性问题 解决办法:使用多线程之间同步...
什么是线程安全问题? 当 多个线程 同时共享同一个 全局变量 ...o可见性:多个线程访问同一变量,一个线程修改了变量的值,其他线程可以立即看到修改的值 o有序性:程序按照代码的顺序先后执行(与指令重排有关)
多线程安全问题和锁 文章目录多线程安全问题和锁线程在jvm中的特点锁的出现synchronized 关键字-监视器锁monitor lock死锁的产生和避免 ...可见性是指线程对共享数据的访问是否对其他的线程可见. A
同步控制是并发程序必不可少的重要手段,本文我们将通过重入锁、读写锁、信号量、倒计数器和循环...保证了共享内存的原子性、可见性、有序性 JMM原理图 线程在访问主内存中的变量时并不是直接对主存中的变量进行读写,
java多线程的通讯共享数据就是共享内存,共享内存主要有可见性和有序原子性。java 内存原型JMM解决可见有序,线程锁synchronized所解决它的原子性。 JMM内存原型:java可以在不同操作系统不同硬件存储逻辑下运行的...
o可见性 :一个线程对共享变量的修改,另一个线程能立刻看到。 缓存 可导致可见性问题。 o原子性 :一个或多个CPU执行操作不被中断。 线程切换 可导致原子性问题。 o有序性 :编译器优化可能导致指令顺序发生...
7.三大性质总结:原子性、可见性以及有序性 8.初识Lock与AbstractQueuedSynchronizer(AQS) 9.深入理解AbstractQueuedSynchronizer(AQS) 10.彻底理解ReentrantLock 11.深入理解读写锁ReentrantReadWriteLock 12.详解...
并发情况主要会引出三个基本概念,分别是原子性、可见性、有序性三个基本概念 Java中线程的状态分为6种: 1. 初始(NEW):新创建了一个线程对象,但还没有调用start()方法。 2. 运行(RUNNABLE):Java线程中将就绪...
volatile是JVM提供的一种最轻量级的同步机制,因为Java内存模型为volatile定义特殊的访问规则,使其可以实现Java内存模型中的两大特性:可见性和有序性。这篇文章主要介绍了Java多线程之volatile关键字及内存屏障,...
基本概念 Java内存模型中的可见性,原子性和有序性 可见性: 可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是...