`
geeksun
  • 浏览: 965111 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

volatile的线程安全性

 
阅读更多

       volatile 关键字可以及时把当前线程对变量的修改写入主内存,每个线程读取变量值的时候都必须从主内存读取,这样做保证了多线程环境的可见性,但其安全性(原子性)在多线程环境并不能获得保证。

       下面的code是为了获取加到1000次后的数字:

public class Counter {
	public static volatile int count = 0;

	public static void inc() {
		// 这里延迟1毫秒,使得结果明显
		try {
			Thread.sleep(1);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		count++;
	}

	public static void main(String[] args) {
		// 同时启动1000个线程,去进行i++计算,看看实际结果
		for (int i = 0; i < 1000; i++) {
			new Thread(new Runnable() {
				public void run() {
					inc();
				}
			}).start();
		}

		// 这里每次运行的值都有可能不同, 但都小于1000
		System.out.println("运行结果: count=" + Counter.count);
	}
}

运行几次可以发现, count值并不为1000.

 

下面来分析一下原因:

在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,

线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存

变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,

在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图描述这些交互



  

read and load 从主存复制变量到当前工作内存
use and assign  执行代码,改变共享变量值 
store and write 用工作内存数据刷新主存相关内容

release memory barrier 释放内存屏障, 释放内存屏障后,数据才写入内存,这是一个锁操作。

 

其中use and assign 可以多次出现

但是这一些操作并不是原子性,也就是 在read load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,所以计算出来的结果会和预期不一样

对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的

例如假如线程1,线程2 在进行read,load 操作中,发现主内存中count的值都是5,那么都会加载这个最新的值

在线程1堆count进行修改之后,会write到主内存中,主内存中的count变量就会变为6

线程2由于已经进行read,load操作,在进行运算之后,也会更新主内存count的变量值为6

导致两个线程及时用volatile关键字修改之后,还是会存在并发的情况。

 

synchronized和volatile比较

  1. 关键字volatile是线程同步的轻量级实现,所以volatile性能肯定比synchronized要好,并且volatile只能修饰变量,而synchronized可以修饰方法,以及代码块。
  2. 多线程访问volatile不会发生堵塞,而synchronized可能会出现堵塞。
  3. volatile保证数据的可见性,但不能保证原子性;而synchronized可以保证原子性,也可以间接保证可见性,因为它会将私有内存和公共内存中的数据做了同步。
  4. 关键字volatile解决的是变量在多个线程之间的可见性;而synchronized关键字解决的是多个线程之间访问资源的同步性。

synchronized关键字是防止多个线程同时执行一段代码,那么就会很影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized,但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。通常来说,使用volatile必须具备以下2个条件:

  1)对变量的写操作不依赖于当前值。

  2)该变量没有包含在具有其他变量的不变式中。

 

 

总结

volatile的适用场景为:

适合用于单线程写,多线程读数据的场合。

类似单线程写,多线程读的场景, 也可以考虑使用 ReentrantReadWriteLock 。

 

  • 大小: 46.6 KB
分享到:
评论

相关推荐

    java多线程安全性基础介绍.pptx

    java多线程安全性基础介绍 线程安全 正确性 什么是线程安全性 原子性 竞态条件 i++ 读i ++ 值写回i 可见性 JMM 由于cpu和内存加载速度的差距,在两者之间增加了多级缓存导致,内存并不能直接对cpu可见。 ...

    Java线程:volatile关键字

    volatile 变量可用于提供线程安全,但是只能应用于非常有限的一组用例:多个变量之间或者某个变量的当前值与修改后值之间没有约束。 正确使用 volatile 变量的条件是:对变量的写操作不依赖于当前值,以及该变量...

    Java多线程编程的线程安全性.docx

    Java多线程编程的线程安全性是开发过程中必须关注的重要概念。线程安全指的是一个类在多线程环境中能够正确地处理并发访问,不会因为线程间的交互导致数据的不一致或异常行为。线程安全性的核心问题主要包括原子性、...

    局部变量线程安全测试

    这些测试可能包括并发读写、竞争条件、死锁等问题的测试,通过运行这些测试并分析结果,我们可以理解在不同情况下局部变量是否能够保持其线程安全性。 总之,局部变量线程安全测试是一项重要的软件质量保证措施,...

    从volatile说到i++的线程安全问题.docx

    本文主要讨论了volatile关键字在多线程环境下的应用,特别是解决了多线程间共享变量的可见性问题,以及i++操作的线程安全问题。 一、volatile关键字的作用 volatile关键字保证了两点: 1. 可见性:在多线程环境下...

    java线程安全性精讲.docx

    Java线程安全性是多线程编程中的核心概念,关乎程序的稳定性和正确性。Java提供了多种机制来确保线程安全,主要包括原子性、可见性和有序性。 **原子性**是线程安全的基础,保证了操作不会被其他线程打断。Java提供...

    java线程安全测试

    在Java中,线程安全问题通常与并发、内存模型和可见性有关。Java内存模型(JMM)定义了如何在多线程环境下共享数据的规则,确保线程之间的正确交互。 线程安全可以分为三种类型: 1. 不安全:当多个线程访问共享...

    线程安全的单例模式

    需要注意的是,这里使用了 `volatile` 关键字来确保可见性和禁止指令重排,进一步加强了线程安全性。 #### 四、深入分析:DCL中的问题 尽管双重检查锁定提供了一种较好的解决方案,但还存在一些问题。例如,在某些...

    servlet与Struts action线程安全问题分析

    线程安全是指在多线程环境中,一个类或者方法能够正确处理多个线程的并发访问,保证数据的一致性和完整性。 Servlet的多线程机制源于其生命周期的管理。当Web容器接收到客户端的首次请求时,会根据web.xml配置文件...

    java线程安全总结.doc

    Java线程安全是多线程编程中的一个关键概念,它涉及到在并发环境下如何正确地管理共享资源,确保程序的正确性和一致性。以下是对Java线程安全的深入总结: ### 一、线程安全的定义 线程安全是指当多个线程访问同一...

    c#线程安全的源代码示例

    8. **Lazy 初始化**:`Lazy&lt;T&gt;`类确保共享资源在首次使用时才初始化,保证多线程环境下的初始化安全性。 9. **Monitor.TryEnter**:尝试进入临界区,如果已被其他线程锁定,则立即返回,避免死锁。 在提供的源代码...

    Java多线程 - (一) 最简单的线程安全问题

    一个方法或变量被称为线程安全,当它在多线程环境下被调用时,仍然能保证其正确性和完整性。线程不安全则可能引发竞态条件(race condition),即多个线程同时访问和修改同一份数据,导致结果不可预测。 在Java中,...

    线程安全、volatile关键字、原子性、并发包、死锁、线程池.md

    线程安全、volatile关键字、原子性、并发包、死锁、线程池学习笔记

    java中volatile不能保证线程安全(实例讲解)

    但是,volatile 关键字不能保证线程安全,因为它不能保证操作的原子性。 在上面的实例中,我们可以看到,当我们使用 volatile 关键字声明了变量 a 时,每个线程都可以看到变量 a 的最新值,但是当我们在多个线程中...

    Java 多线程 订票 示例 线程安全

    在Java中,实现线程安全的方法通常包括同步机制(synchronized关键字)、volatile变量、Lock接口及其实现、原子类(Atomic*)等。 现在,让我们来看一个简单的订票系统的Java多线程示例。假设我们有一个票务系统,...

    Linux C中多线程与volatile变量

    综上所述,`volatile`在Linux C多线程编程中主要用于解决共享变量的可见性问题,但在处理多线程同步时,还需要结合其他的同步机制,如互斥锁(`mutex`)、条件变量等,以确保线程安全。理解`volatile`的关键在于它是...

    Volatile详解,深入学习Volatile

    - volatile不能解决并发下的线程安全问题,还需要配合锁机制来实现。 总结来说,volatile是编程中一个重要的关键字,它用于解决并发和实时系统中的可见性和同步问题。正确理解和使用volatile可以避免不必要的数据...

    Java中的线程安全与线程同步.doc

    同时,避免使用静态变量存储线程相关的状态,尽量减少共享资源的使用,都有助于提升程序的线程安全性。 总之,Java中的线程安全与线程同步是保障多线程环境下程序正确运行的关键,开发者需要根据具体场景选择合适的...

    Java多线程与线程安全实践-基于Http协议的断点续传

    总之,Java多线程与线程安全的实践是一个复杂而深入的话题,结合HTTP协议的断点续传,我们需要综合运用各种并发控制机制和优化技术,确保程序的稳定性和效率。通过这个毕业设计项目,你可以深入了解并掌握这些关键...

Global site tag (gtag.js) - Google Analytics