Java并发编程之volatile关键字的理解
Java中每个线程都有自己的工作内存,类比于处理器的缓存,线程的工作内存中保存了被该线程使用到的变量的主内存的拷贝。线程读写变量都是直接在自己的工作内存中进行的,而何时刷新数据(指将修改的结果更新到主存或者把主存的变量读取覆盖掉工作内存中的值)是不确定的。
volatile关键字是修饰字段的关键字,貌似是JDK1.5之后才有的,在多线程编程中,很大的几率会用到这个关键字,volatile修饰变量后该变量有这么一种效果:线程每一次读该变量都是直接从主存(JVM的主存)中读,而不是从线程的工作内存中;每一次写该变量都会同时写到主存中,而不仅仅是线程的工作内存中。因此一开头说的"何时刷新数据是不确定的"只适用于非volatile变量。
JVM对volatile变量有两个保证:
- 可见性。这个上面也大概解释了,就是某个线程改变了值,另一个线程立马就能读到改变后的值,容易理解。
- Happens-Before。有两点要说明:
- 线程在写volatile变量时,若对一个普通变量的写在对该volatile变量的写之前,那么对该普通变量的写也将会被写到主存,而不仅仅是工作内存;线程在读volatile变量时,若对一个普通变量的读在对该volatile变量的读之后,那么对该普通变量的读将会先和主存同步,再读取,而不是直接从工作内存中读。例如
- Thread A:
- object.nonVolatileVar = 1; // stepA1
- object.volatileVar = object.volatileVar + 1; // stepA2
- Thread B:
- int volatileVar = object.volatileVar; // stepB1
- int nonVolatile = object.nonVolatileVar; // stepB2
线程A执行到stepA2,当要把volatileVar的新值写到主存时,nonVolatileVar的新值也会被刷到主存中;线程B执行到stepB1时,当要从主存中读object.volatileVar时,object.nonVolatileVar也会被一起读进工作内存,因此当线程 B执行到StepB2时,是可以拿到nonVolatileVar 的最新值的。这种特性其实蛮有用的:当一个线程有多个volatile变量时,可以根据这个特性减少volatile变量(通过变量的读、写顺序),可以达到和多个volatile变量同样的效果。
- 对volatile变量的读写指令是不会被JVM重排序的。读/写之前或之后的其他指令可以重排序,但对volatile变量的读/写指令和其它指令的相对顺序是不会改变的。例如
- object.nonVolatile1 = 123; //instruction1
- object.nonVolatile2 = 456; //instruction2
- object.nonVolatile3 = 789; // //instruction3
- object.volatile = true; //a volatile variable, //instruction4
- int value1 = sharedObject.nonVolatile4; //instruction5
- int value2 = sharedObject.nonVolatile5; //instruction6
- int value3 = sharedObject.nonVolatile6; //instruction7
由于JVM发现instruction1、instruction2、instruction3没有前后作用关系,因此jvm有可能会重排序这三条指令,instruction456也是如此,但是中间有个volatile变量的读。因此instruction123是不会被重排序到instruction4后面去的,同样instruction456也不会重排序到instruction4前面去的,他们的相对顺序不会变。
一个很常见的用volatile的例子就是单例模式(某种线程安全的写法):
- public class Singleton {
- private volatile static Singleton instance;
- public static Singleton getInstance() {
- if(instance == null) { //step1
- synchronized (Singleton.class) { //step2
- if(instance == null) { //step3
- instance = new Singleton(); // step4
- }
- }
- }
- return instance;
- }
- private Singleton(){}
- }
这里的isntance如果不用volatile修饰,那么这个单例就是非多线程安全的,知道synchronized有可见性保证的人可能会问:为什么这里用了synchronized还需要用volatile修饰?确实,这里两者都保证了可见性,但是这里用volatile不是因为可见性的原因,而是因为指令重排序的原因:首先要知道一点的就是new一个对象时有三步(伪码):
- memory = allocate(); //1:分配对象的内存空间
- ctorInstance(memory); //2:初始化对象
- instance = memory; //3:设置instance指向刚分配的内存地址
而这三条指令肯定都是同一个线程执行,根据intra-thread semantic(intra-thread semantics保证重排序不会改变单线程内的程序执行结果),这三条指令是可以重排序成下面这样的:
- memory = allocate(); //1:分配对象的内存空间
- instance = memory; //3:设置instance指向刚分配的内存地址
- ctorInstance(memory); //2:初始化对象
那么上面的单例就有问题了。假设不幸上述重排序发生了,那么初始化对象的线程正好设置了instance = memory(即instance已经不为null了)但是instance还没被初始化时,另一个线程跑到step1,发现instance不为null,然后直接把instance拿去用了,后面自然就会出现各种问题,因为对象根本还没被初始化。用了volatile修饰后,上面所说的重排序就被禁止了。
java.util.concurrent包下用到volatile的地方数不胜数,比如java.util.concurrent.FutureTask<T>中就有使用到volatile的happens-before原则::
可以看到有两个变量state, callable都需要保证其可见性, 但是这里只用volatile修饰其中一个,而通过写的顺序来保证不被volatile修饰的那个变量的可见性。
书上看到的:“除了volatile外,synchronized和final也能实现可见性。synchronized的可见性:在离开synchronized代码块前,必须先把变量同步到主存中。final的可见性:被final修饰的字段在构造器中一旦完成初始化,并且构造器没有把this引用传递出去,那在其他线程中就能看到这个值”。
以上是个人结合书本的理解,如果不妥,欢迎指正。
相关推荐
### Java并发编程:volatile关键字解析 #### 一、内存模型的相关概念 在深入了解`volatile`关键字之前,我们首先需要理解计算机内存模型的一些基本概念。在现代计算机系统中,CPU为了提高执行效率,会将频繁访问的...
《Java并发编程实战》是Java并发编程领域的一本经典著作,它深入浅出地介绍了如何在Java平台上进行高效的多线程编程。这本书的源码提供了丰富的示例,可以帮助读者更好地理解书中的理论知识并将其应用到实际项目中。...
综合来看,该书不仅适合于对Java并发编程感兴趣的初学者,同样也适合有经验的开发者,作为一本参考资料和实践指南,读者可以通过它来加深对Java并发特性的理解和应用。书中丰富的案例分析和代码示例将有助于读者更...
在Java并发编程中,多线程是核心概念之一。多线程允许程序同时执行多个任务,从而充分利用系统资源,提高程序性能。然而,多线程编程也带来了同步和竞态条件等问题,这需要开发者具备良好的线程管理和同步机制的知识...
以上知识点覆盖了Java并发编程的主要方面,包括线程管理、同步机制、并发工具、设计模式、并发集合以及并发编程的最佳实践等,是理解和掌握Java并发编程的关键。在实际开发中,理解和熟练运用这些知识可以编写出高效...
这份“java并发编程内部分享PPT”显然是一个深入探讨这一主题的资料,旨在帮助开发者理解并掌握Java并发编程的核心概念和技术。 在Java并发编程中,首先我们需要了解的基本概念是线程。线程是操作系统分配CPU时间的...
《JAVA并发编程实践》这本书是Java开发者深入理解并发编程的重要参考资料。它涵盖了Java并发的核心概念、工具和最佳实践,旨在帮助读者在多线程环境下编写高效、安全的代码。 并发编程是现代软件开发中的关键技能,...
通过学习《Java并发编程实践》,开发者将能够更好地理解和利用Java平台的并发特性,编写出更高效、更可靠的多线程应用程序。无论是初级开发者还是经验丰富的专业人员,都能从这本书中收获宝贵的并发编程知识。
《Java并发编程的艺术》这本书是Java开发者深入理解并发编程的重要参考书籍。这本书全面地介绍了Java平台上的并发和多线程编程技术,旨在帮助开发者解决在实际工作中遇到的并发问题,提高程序的性能和可伸缩性。 ...
Java并发编程是软件开发中的一个关键领域,尤其是在大型企业级应用和分布式系统中。通过学习相关的书籍,开发者可以深入理解如何有效地设计和实现高效的多线程应用程序,避免并发问题,如竞态条件、死锁、活锁等。...
《JAVA并发编程艺术》是Java开发者深入理解和掌握并发编程的一本重要著作,它涵盖了Java并发领域的核心概念和技术。这本书详细阐述了如何在多线程环境下有效地编写高效、可靠的代码,对于提升Java程序员的技能水平...
《Java并发编程从入门到精通》是一本专为Java开发者设计的深度学习并发编程的书籍。作者韩剑锋,凭借其12年的IT行业经验,曾担任多家IT公司的研发总监和技术总监,以其丰富的实战经验和深厚的理论知识,为读者提供了...
通过阅读这些书籍,开发者可以深入理解Java并发编程的底层机制,掌握有效的并发编程技巧,提高程序的运行效率,同时避免并发编程中常见的陷阱和问题。无论是初学者还是经验丰富的开发者,都能从中受益匪浅。
### Java 并发核心编程知识点解析 #### 一、Java并发概述 自Java诞生之初,其设计者就赋予了该语言强大的并发处理能力。Java语言内置了对线程和锁的支持,这...理解和掌握这些概念和技术是成功进行并发编程的关键。
"Java并发编程与实践"文档深入剖析了这一主题,旨在帮助开发者理解和掌握如何在Java环境中有效地实现并发。 并发是指在单个执行单元(如CPU)中同时执行两个或更多任务的能力。在Java中,这主要通过线程来实现,...
Java并发编程是Java开发者必须掌握的关键技能之一,它涉及到如何在多线程环境中高效、安全地执行程序。并发编程能够充分利用多核处理器的计算能力,提高应用程序的响应速度和整体性能。《Java编程并发实战》这本书是...
在Java并发编程中,首先需要理解“并发”(Concurrency)和“并行”(Parallelism)的区别。“并发”指的是多个任务在同一时间段内交替执行,而“并行”则指的是多个任务同时执行。在实际应用中,“并发”更侧重于...
《Java并发编程的艺术》是一本全面探讨Java并发编程技术的专业书籍,它深入剖析了Java并发机制的底层实现,如volatile、synchronized等关键技术的工作原理,并详细讲解了线程池、并发容器等高级并发工具的使用。...
通过学习“Java并发编程设计原则和模式”,开发者可以更好地理解和应用这些概念,编写出高效、可靠的并发程序。理解并熟练掌握这些知识,对于提升Java程序员的技能水平和解决实际问题的能力至关重要。