多线程读写共享变量时,synchronized与volatile的作用
在《effective java》中看的的知识点,在工作中确实遇到了~
keywordsynchronized能够保证在同一时刻,仅仅有一个线程能够运行某一个方法,或者某一个代码块。
同步并非单单指线程之间的相互排斥。
假设没有同步,一个线程的变化就不能被其它线程看到。
同步不仅能够阻止一个线程看到对象处于不一致的状态之中, 它还能够保证进入同步方法或者同步代码块的每一个线程,都看到由同一个锁保护的之前的全部改动效果。
思考以下这个程序的执行过程是什么样的。
import java.util.concurrent.TimeUnit;
public class StopThread {
private static boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread =new Thread(new Runnable(){
public void run() {
int i=0;
while(!stopRequested){
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested=true;
}
}
你可能以为的这个程序大约执行一秒左右。之后主线程将stopRequested设置为true。从而导致后台线程终止。
可是结果不是这种。
问题在于,因为没有同步。就不能保证后台线程何时“看到”主线程对stopRequested的值所做的改变。Java语言规范保证读或写一个变量是原子的(atomic)long和double除外。可是它并不保证一个线程写入的值对于还有一个线程将是可见的。
以下看下解决方式
import java.util.concurrent.TimeUnit;
public class StopThread {
private static boolean stopRequested;
private static synchronized void requestStop(){
stopRequested=true;
}
private static synchronized boolean stopRequested(){
return stopRequested;
}
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread =new Thread(new Runnable(){
public void run() {
int i=0;
while(!stopRequested()){
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
//stopRequested=true;
requestStop();
}
}
写入方法(requestStop())和读取(stopRequest())方法作 都被同步了 。仅仅同步写方法是不够的。实际上,假设读和写操作没有都被同步。同步就不会起作用。
StopThread中方法的同步是为了它的 通信效果 ,而不是为了相互排斥訪问。一种更加简洁,性能也可能更好的方法是将stopRequested声明为 volatile 。尽管volatile修饰符不运行相互排斥訪问,但 它能够保证不论什么一个线程在读取该field的时候都将看到近期刚刚被写入的值:
import java.util.concurrent.TimeUnit;
public class StopThread {
private static volatile boolean stopRequested;
public static void main(String[] args) throws InterruptedException {
Thread backgroundThread =new Thread(new Runnable(){
public void run() {
int i=0;
while(!stopRequested){
i++;
}
}
});
backgroundThread.start();
TimeUnit.SECONDS.sleep(1);
stopRequested=true;
}
}
锁提供了两种主要特性:相互排斥(mutual exclusion) 和可见性(visibility)。
相互排斥即一次仅仅同意一个线程持有某个特定的锁,因此可使用该特性实现对共享数据的协调訪问协议。这样,一次就仅仅有一个线程可以使用该共享数据。可见性要更加复杂一些,它必须确保释放锁之前对共享数据做出的更改对于随后获得该锁的还有一个线程是可见的 —— 假设没有同步机制提供的这样的可见性保证,线程看到的共享变量可能是改动前的值或不一致的值,这将引发很多严重问题。
Volatile 变量具有 synchronized 的可见性特性。这就是说线程可以自己主动发现 volatile 变量的最新值。
但要始终牢记使用 volatile 的限制
—— 仅仅有在状态真正独立于程序内其它内容时才干使用 volatile —— 这条规则可以避免将这些模式扩展到不安全的用例。
假设严格遵循
volatile 的使用条件 —— 即变量真正独立于其它变量和自己曾经的值 —— 在某些情况下能够使用 volatile 取代 synchronized 来简化代码
在多线程场景中。假设须要使用标记的时候。volatile往往能够大显身手~
多线程读写共享变量时,synchronized与volatile的作用的更多相关文章
- java多线程之内存可见性-synchronized、volatile
1.JMM:Java Memory Model(Java内存模型) 关于synchronized的两条规定: 1.线程解锁前,必须把共享变量的最新值刷新到主内存中 2.线程加锁时,将清空工作内存中共享 ...
- Java 多线程(二)synchronized和volatile
脏读: 脏读指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据.总的来说取到的数据是其实是被更改过的,但还没有保存到数 ...
- 多线程:Monitor、synchronized、volatile
Moniter的实现原理 再有人问你synchronized是什么,就把这篇文章发给他 深入理解Java中的volatile关键字 既生synchronized,何生volatile
- 【java多线程系列】java中的volatile的内存语义
在java的多线程编程中,synchronized和volatile都扮演着重要的 角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性,可见性指的是当一 ...
- java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现
注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...
- synchronized 与 volatile 区别 还有 volatile 的含义
熟悉并发的同学一定知道在java中处理并发主要有两种方式: 1,synchronized关键字,这个大家应当都各种面试和笔试中经常遇到. 2,volatile修饰符的使用,相信这个修饰符大家平时在项目 ...
- Java多线程之内存可见性和原子性:Synchronized和Volatile的比较
Java多线程之内存可见性和原子性:Synchronized和Volatile的比较 [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article ...
- java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析
java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...
- Java多线程同步方法Synchronized和volatile
11 同步方法 synchronized – 同时解决了有序性.可见性问题 volatile – 结果可见性问题 12 同步- synchronized synchronized可以在任意对象上加 ...
随机推荐
- Java多态的体现之接口
/** * * @author Administrator * 功能:接口体现多态 */ package com.test4; public class Test { public static vo ...
- 146. LRU Cache
题目: Design and implement a data structure for Least Recently Used (LRU) cache. It should support the ...
- 【Linux安全】文件或目录权限设置
实例演示: 要求:新建文件夹,名称为secure,要求改文件夹只能被创建者deadmin以及与deadmin同组用户进行读.写.执行的权限, 其他用户均只有读的权限. 查看 一下secure原本的权限 ...
- Server.MapPath 的使用方法
Server.MapPath 的使用方法 用法: 1.Server.MapPath ("/") 应用程序根目录所在的位置 如 C:\Inetpub\wwwroot\ 2.Serve ...
- 【原创翻译】Reducing Branch Delay to Zero in Pipelined Processors
在流水线处理器中减少分支延迟到零 Antonio M. Gonzalez and Jose M. Llaberia 摘要 一种减少流水处理器中分支延迟到零的机制将在本文被描述以及评估.这种机制基于多重 ...
- 查找Form的文件名
我们经常会要在ORACLE EBS中寻找我们正在浏览的form页面的执行文件,我们都会直接在Help中的菜单里点击"About Oracle Application",然后查看当前 ...
- bzoj1176 2683
我的第一道cdq分治题清明做了一下cdq分治的几道题,感觉这个东西实在是太厉害了离线大法好!关于几个经典的非数据结构做法具体可以看xhr神犇2013年的论文应用cdq分治的前提条件是不强制在线,修改操 ...
- BZOJ_1615_[Usaco2008_Mar]_The Loathesome_Hay Baler_麻烦的干草打包机_(模拟+宽搜/深搜)
描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1615 一个主动轮带着一些轮子转,轮子带着轮子转,轮子带着轮子转...一个非主动轮只会被一个轮子 ...
- ora-01589
今天遇到了ORA-01589这个错误,下面把解决方案记录如下: SQL*Plus: Release 9.2.0.1.0 - Production on 星期四 4月 17 15:01:26 2008C ...
- Extjs4-treepanel-解析json/fields中是否加入leaf的区别
json解析list时,我原先的写法是: Map<String, Object> map = new HashMap<String, Object>(); map.put(&q ...