`

java中关键字volatile的作用 编辑

 
阅读更多

用在多线程,同步变量。 线程为了提高效率,将某成员变量(如A)拷贝了一份(如B),线程中对A的访问其实访问的是B。只在某些动作时才进行A和B的同步。因此存在A和B不一致 的情况。volatile就是用来避免这种情况的。volatile告诉jvm, 它所修饰的变量不保留拷贝,直接访问主内存中的(也就是上面说的A)

=========================分割线1=================================

版权声明 :转载时请以超链接形式标明文章原始出处和作者信息及本声明
http://aleung.blogbus.com/logs/32090434.html

在Java内存模型中,有main memory,每个线程也有自己的memory (例如寄存器)。为了性能,一个线程会在自己的memory中保持要访问的变量的副本。这样就会出现同一个变量在某个瞬间,在一个线程的memory中的 值可能与另外一个线程memory中的值,或者main memory中的值不一致的情况。

一个变量声明为volatile,就意味着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。以下例子展现了volatile的作用:

Java代码 收藏代码
  1. publicclassStoppableTaskextendsThread{
  2. privatevolatilebooleanpleaseStop;
  3. publicvoidrun(){
  4. while(!pleaseStop){
  5. //dosomestuff...
  6. }
  7. }
  8. publicvoidtellMeToStop(){
  9. pleaseStop=true;
  10. }
  11. }


假如pleaseStop没有被声明为volatile,线程执行run的时候检查的是自己的副本,就不能及时得知其他线程已经调用tellMeToStop()修改了pleaseStop的值。

Volatile一般情况下不能代替sychronized,因为volatile不能保证操作的原子性,即使只是i++,实际上也是由多个原子 操作组成:read i; inc; write i,假如多个线程同时执行i++,volatile只能保证他们操作的i是同一块内存,但依然可能出现写入脏数据的情况。如果配合Java 5增加的atomic wrapper classes,对它们的increase之类的操作就不需要sychronized。

Reference:
http://www.javamex.com/tutorials/synchronization_volatile.shtml
http://www.javamex.com/tutorials/synchronization_volatile_java_5.shtml
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
=========================分割线2=================================

恐怕比较一下volatile和synchronized的不同是最容易解释清楚的。volatile是变量修饰符,而synchronized则作用于一段代码或方法;看如下三句get代码:

Java代码 收藏代码
  1. inti1;
  2. intgeti1(){returni1;}
  3. volatileinti2;
  4. intgeti2()
  5. {returni2;}
  6. inti3;
  7. synchronizedintgeti3(){returni3;}
  8.   geti1()


得到存储在当前线程中i1的数值。多个线程有多个i1变量拷贝,而且这些i1之间可以互不相同。换句话说,另一个线程可能已经改变了它线程内的 i1值,而这个值可以和当前线程中的i1值不相同。事实上,Java有个思想叫“主”内存区域,这里存放了变量目前的“准确值”。每个线程可以有它自己的 变量拷贝,而这个变量拷贝值可以和“主”内存区域里存放的不同。因此实际上存在一种可能:“主”内存区域里的i1值是1,线程1里的i1值是2,线程2里 的i1值是3——这在线程1和线程2都改变了它们各自的i1值,而且这个改变还没来得及传递给“主”内存区域或其他线程时就会发生。
  而 geti2()得到的是“主”内存区域的i2数值。用volatile修饰后的变量不允许有不同于“主”内存区域的变量拷贝。换句话说,一个变量经 volatile修饰后在所有线程中必须是同步的;任何线程中改变了它的值,所有其他线程立即获取到了相同的值。理所当然的,volatile修饰的变量 存取时比一般变量消耗的资源要多一点,因为线程有它自己的变量拷贝更为高效。
  既然volatile关键字已经实现了线程间数据同步,又要 synchronized干什么呢?呵呵,它们之间有两点不同。首先,synchronized获得并释放监视器——如果两个线程使用了同一个对象锁,监 视器能强制保证代码块同时只被一个线程所执行——这是众所周知的事实。但是,synchronized也同步内存:事实上,synchronized在“ 主”内存区域同步整个线程的内存。因此,执行geti3()方法做了如下几步:
1. 线程请求获得监视this对象的对象锁(假设未被锁,否则线程等待直到锁释放)
2. 线程内存的数据被消除,从“主”内存区域中读入(Java虚拟机能优化此步。。。[后面的不知道怎么表达,汗])
3. 代码块被执行
4. 对于变量的任何改变现在可以安全地写到“主”内存区域中(不过geti3()方法不会改变变量值)
5. 线程释放监视this对象的对象锁
  因此volatile只是在线程内存和“主”内存间同步某个变量的值,而synchronized通过锁定和解锁某个监视器同步所有变量的值。显然synchronized要比volatile消耗更多资源。

=========================分割线3=================================

volatile关键字相信了解Java多线程的读者都很清楚它的作用。volatile关键字用于声明简单类型变量,如int、float、 boolean等数据类型。如果这些简单数据类型声明为volatile,对它们的操作就会变成原子级别的。但这有一定的限制。例如,下面的例子中的n就 不是原子级别的:

Java代码 收藏代码
  1. packagemythread;
  2. publicclassJoinThreadextendsThread
  3. {
  4. publicstaticvolatileintn=0;
  5. publicvoidrun()
  6. {
  7. for(inti=0;i<10;i++)
  8. try
  9. {
  10. n=n+1;
  11. sleep(3);//为了使运行结果更随机,延迟3毫秒
  12. }
  13. catch(Exceptione)
  14. {
  15. }
  16. }
  17. publicstaticvoidmain(String[]args)throwsException
  18. {
  19. Threadthreads[]=newThread[100];
  20. for(inti=0;i<threads.length;i++)
  21. //建立100个线程
  22. threads[i]=newJoinThread();
  23. for(inti=0;i<threads.length;i++)
  24. //运行刚才建立的100个线程
  25. threads[i].start();
  26. for(inti=0;i<threads.length;i++)
  27. //100个线程都执行完后继续
  28. threads[i].join();
  29. System.out.println("n="+JoinThread.n);
  30. }
  31. }


如果对n的操作是原子级别的,最后输出的结果应该为n=1000,而在执行上面积代码时,很多时侯输出的n都小于1000,这说明n=n+1不是原子级别 的操作。原因是声明为volatile的简单变量如果当前值由该变量以前的值相关,那么volatile关键字不起作用,也就是说如下的表达式都不是原子 操作:

n = n + 1 ;
n ++ ;

如果要想使这种情况变成原子操作,需要使用synchronized关键字,如上的代码可以改成如下的形式:

Java代码 收藏代码
  1. packagemythread;
  2. publicclassJoinThreadextendsThread
  3. {
  4. publicstaticintn=0;
  5. publicstaticsynchronizedvoidinc()
  6. {
  7. n++;
  8. }
  9. publicvoidrun()
  10. {
  11. for(inti=0;i<10;i++)
  12. try
  13. {
  14. inc();//n=n+1改成了inc();
  15. sleep(3);//为了使运行结果更随机,延迟3毫秒
  16. }
  17. catch(Exceptione)
  18. {
  19. }
  20. }
  21. publicstaticvoidmain(String[]args)throwsException
  22. {
  23. Threadthreads[]=newThread[100];
  24. for(inti=0;i<threads.length;i++)
  25. //建立100个线程
  26. threads[i]=newJoinThread();
  27. for(inti=0;i<threads.length;i++)
  28. //运行刚才建立的100个线程
  29. threads[i].start();
  30. for(inti=0;i<threads.length;i++)
  31. //100个线程都执行完后继续
  32. threads[i].join();
  33. System.out.println("n="+JoinThread.n);
  34. }
  35. }

上面的代码将n=n+1改成了inc(),其中inc方法使用了synchronized关键字进行方法同步。因此,在使用volatile关键字时要慎 重,并不是只要简单类型变量使用volatile修饰,对这个变量的所有操作都是原来操作,当变量的值由自身的上一个决定时,如n=n+1、n++ 等,volatile关键字将失效,只有当变量的值和自身上一个值无关时对该变量的操作才是原子级别的,如n = m + 1,这个就是原级别的。所以在使用volatile关键时一定要谨慎,如果自己没有把握,可以使用synchronized来代替volatile。

分享到:
评论

相关推荐

    Java解惑

    理解synchronized、volatile关键字的作用,以及线程池的使用,能帮助我们编写出安全且高效的并发代码。另外,Java内存模型JMM(Java Memory Model)也需有所了解,它规定了线程间的可见性和内存一致性。 最后,垃圾...

    关于JAVA实验上机指导

    指导将讲解Thread类和Runnable接口,以及synchronized关键字和volatile变量的用法。 9. **JDBC数据库连接**:Java Database Connectivity (JDBC) 是Java访问数据库的标准API。你可能会学习如何加载驱动、建立连接、...

    java软件开发必备

    Java并发API如ExecutorService、Future和CompletableFuture提供了丰富的工具来管理并发任务,而synchronized、volatile关键字则用于保障线程安全。 总的来说,Java软件开发涵盖了广泛的领域,包括但不限于编程基础...

    java资料面试题

    Java 中的关键字包括但不限于:abstract、assert、boolean、break、byte、case、catch、char、class、const、continue、default、do、double、else、enum、extends、final、finally、float、for、if、goto、...

    java初步学习指导

    - 学习如何创建和管理线程,理解同步和互斥,使用 synchronized 关键字和 volatile 属性。 11. **网络编程**: - 理解套接字(Socket)编程,学会创建客户端和服务端的通信。 12. **Java API**: - 熟悉 Java ...

    JAVA Programming Guide - Quick Reference

    1. **编写程序**:在DOS编辑器或记事本中输入程序,并保存为`.java`文件。文件名需与包含`main`方法的类名一致。 2. **编译程序**:使用`javac`编译器编译程序。 - **语法**:`javac filename.java` - **示例**:`...

    Java开发基础(基础语法+面向对象基础+集合基础+常用API)

    - 不能是Java关键字。 - 区分大小写。 5. **命名约定** - **小驼峰命名法**:适用于变量名,首个单词首字母小写,后续单词首字母大写。例如:`firstName` - **大驼峰命名法**:适用于类名,所有单词首字母均大写...

    spring boot《Java课程》多线程教学演示系统(011733)

    此外,Java还提供了synchronized关键字、volatile变量、wait()、notify()和notifyAll()等工具来处理线程间的同步和通信,以避免竞态条件和死锁。 在Java多线程教学演示系统中,我们将使用这些概念来设计和实现一个...

    Java经典试题小集棉

    4. **Java程序中定义接口所使用的关键字是interface,接口中的属性都是静态变量,接口中的方法都是抽象方法。** - 正确。接口使用`interface`关键字定义,并且接口中的属性默认为`public static final`,方法默认为...

    全国计算机等级考试二级Java语言程序设计考试大纲201

    考生需理解线程的生命周期,学会创建和控制线程,掌握同步机制如synchronized关键字、volatile变量和Lock接口的使用,以避免竞态条件和死锁。此外,了解Java的并发工具类如ExecutorService和CountDownLatch,对于...

    Java JDK 7学习笔记(国内第一本Java 7,前期版本累计销量5万册)

    必须要时从Java SE API的源代码分析,了解各种语法在Java SE API中如何应用。  《Java JDK 7学习笔记》将IDE操作纳为教学内容之一,使读者能与实践结合,提供的视频教学能更清楚地帮助读者掌握操作步骤。 内容简介 ...

    java 并发编程的艺术

    此外,书中还讨论了volatile关键字的实现原理,这是理解内存模型中不可忽视的一部分。 多线程编程是并发编程的基础,第4章介绍了多线程技术的好处,并详细阐述了如何启动和终止线程、线程的各种状态以及线程间通信...

    java刺目开发实践

    3. 多线程:Java提供了Thread类和Runnable接口来实现多线程,以及synchronized、volatile关键字保证线程安全。 4. IO与NIO:Java I/O流处理各种输入输出,NIO(New Input/Output)提供非阻塞I/O操作,提高性能。 5. ...

    414.412.JAVA基础教程_IDEA的使用与多线程-模板的使用和设置(414).rar

    此外,Java还提供了ExecutorService和Future接口来管理和控制线程,以及synchronized关键字、volatile变量、Lock接口等用于线程同步和通信,防止数据竞争问题。 在实际项目中,我们常常会遇到多线程的问题,如线程...

    大公司的Java面试题集

    2. **线程与并发**:`Thread`类、`Runnable`接口、线程同步机制(如`synchronized`关键字、`volatile`变量)、线程池的使用。 3. **设计模式**:例如单例模式、工厂模式、策略模式、装饰者模式等,每种模式解决的...

    java代码规范1

    4. **并发处理**:在多线程环境下,Java提供了丰富的并发工具类和机制,如synchronized关键字、volatile变量、Lock接口、Semaphore信号量等。规范中可能涉及如何正确使用这些工具以保证线程安全和性能。 5. **控制...

Global site tag (gtag.js) - Google Analytics