多线程读写共享变量时,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可以在任意对象上加 ...
随机推荐
- ANDROID_MARS学习笔记_S01原始版_018_SERVICE之Parcel
一.代码 1.xml(1)activity_main.xml <RelativeLayout xmlns:android="http://schemas.android.com/apk ...
- linux内存管理机制
物理内存和虚拟内存 我们知道,直接从物理内存读写数据要比从硬盘读写数据要快的多,因此,我们希望所有数据的读取和写入都在内存完成,而内存是有限的,这样就引出了物理内存与虚拟内存的概念. 物理内存就是系统 ...
- 模拟+二分 poj-1019-Number Sequence
题目链接: http://poj.org/problem?id=1019 题目大意: Sk表示123...k 把S1S2S3...Sk排成一行 比如:112123123412345123456.... ...
- C# asp.net 操作Word的前提配置和简单的方法
操作的前提: 1.要保证机器本身要安装OFFICE. 有时安装了Office,但是不能找到Microsoft Word 11.0(或者更高的版本) Object Library.那可能是因为在安装of ...
- bugfree如何修改Bug7种解决方案的标注方法
Bug有7种解决方案的标注方法 By Design- 就是这么设计的,无效的Bug Duplicate - 这个问题别人已经发现了,重复的Bug External - 是个外部因素(比如浏览器.操作系 ...
- 基于WebForm+EasyUI的业务管理系统形成之旅 -- 数据统计(Ⅳ)
上篇<基于WebForm+EasyUI的业务管理系统形成之旅 -- 首页快捷方式>,主要介绍通过添加首页快捷方式,快速进入各个应用菜单功能. 将常用的菜单功能作为快捷方式,避免由于寻找诸多 ...
- HDOJ 2167 Pebbles (状态压缩dp)
题意:给你一个n*n的矩阵,让你从矩阵中选择一些数是的他们的和最大,规则是:相邻的两个数不能同时取,位置为(i,j)的数与(i+1,j),(i-1,j),(i,j+1),(i,j-1),(i+1,j+ ...
- 用Oracle的TRIM函数去除字符串首尾指定字符
去掉首尾空格 SELECT TRIM(' abc '), ltrim(' abc '), rtrim(' abc ') FROM dual; 去掉首尾的其他字符 SELECT /*TRIM(';a;b ...
- org.apache.hadoop.io.LongWritable cannot be cast to org.apache.hadoop.io.Text
代码缺少这一行:job.setInputFormatClass(KeyValueTextInputFormat.class);
- eclipse简单注释规范
设置注释模板的入口: Window->Preference->Java->Code Style->Code Template Types/*** @ClassName: ${t ...