在《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的作用的更多相关文章

  1. java多线程之内存可见性-synchronized、volatile

    1.JMM:Java Memory Model(Java内存模型) 关于synchronized的两条规定: 1.线程解锁前,必须把共享变量的最新值刷新到主内存中 2.线程加锁时,将清空工作内存中共享 ...

  2. Java 多线程(二)synchronized和volatile

    脏读: 脏读指当一个事务正在访问数据,并且对数据进行了修改,而这种修改还没有提交到数据库中,这时,另外一个事务也访问这个数据,然后使用了这个数据.总的来说取到的数据是其实是被更改过的,但还没有保存到数 ...

  3. 多线程:Monitor、synchronized、volatile

    Moniter的实现原理 再有人问你synchronized是什么,就把这篇文章发给他 深入理解Java中的volatile关键字 既生synchronized,何生volatile

  4. 【java多线程系列】java中的volatile的内存语义

    在java的多线程编程中,synchronized和volatile都扮演着重要的 角色,volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性,可见性指的是当一 ...

  5. java面试记录二:spring加载流程、springmvc请求流程、spring事务失效、synchronized和volatile、JMM和JVM模型、二分查找的实现、垃圾收集器、控制台顺序打印ABC的三种线程实现

    注:部分答案引用网络文章 简答题 1.Spring项目启动后的加载流程 (1)使用spring框架的web项目,在tomcat下,是根据web.xml来启动的.web.xml中负责配置启动spring ...

  6. synchronized 与 volatile 区别 还有 volatile 的含义

    熟悉并发的同学一定知道在java中处理并发主要有两种方式: 1,synchronized关键字,这个大家应当都各种面试和笔试中经常遇到. 2,volatile修饰符的使用,相信这个修饰符大家平时在项目 ...

  7. Java多线程之内存可见性和原子性:Synchronized和Volatile的比较

    Java多线程之内存可见性和原子性:Synchronized和Volatile的比较     [尊重原创,转载请注明出处]http://blog.csdn.net/guyuealian/article ...

  8. java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析

    java多线程系列(五)---synchronized ReentrantLock volatile Atomic 原理分析 前言:如有不正确的地方,还望指正. 目录 认识cpu.核心与线程 java ...

  9. Java多线程同步方法Synchronized和volatile

    11 同步方法  synchronized – 同时解决了有序性.可见性问题  volatile – 结果可见性问题 12 同步- synchronized synchronized可以在任意对象上加 ...

随机推荐

  1. HTML控件ID和NAME属性的区别,以及如何在asp.net页面的.CS文件中获得.ASPX页面中HTML控件的值

    在html中:name指的是用户名称,ID指的是用户注册是系统自动分配给用户的一个序列号. name是用来提交数据的,提供给表单用,可以重复: id则针对文档操作时候用,不能重复.如:document ...

  2. strspn和strcspn妙用

    http://blog.csdn.net/aidenliu/article/details/5460201

  3. exynos4412电路OrCAD问题

    图(1) K4B4G1646B,DDR3 图(2) JIA排针 问题:理论上,引脚命名不能重复,图(1)和图(2)都存在重复的引脚命名,但在进行DRC检查和网表输出时图(1)无任何问题,图(2)却有错 ...

  4. hdu4666Hyperspace

    http://acm.hdu.edu.cn/showproblem.php?pid=4666 先看一个求曼哈顿的帖子http://www.cnblogs.com/lmnx/articles/24797 ...

  5. 安装db2 提示不是有效的win32应用程序?

    问题已经解决了,就是版本的问题.我在官网上下载的最新版本(10.5),网上说是最新的版本不支持xp系统,完了我下了9.7的版本,安装没有一点点问题

  6. BZOJ_1008_[HNOI2008]_越狱_(简单组合数学+快速幂)

    描述 http://www.lydsy.com/JudgeOnline/problem.php?id=1008 监狱有连续编号为1...N的N个房间,每个房间关押一个犯人,有M种宗教,每个犯人可能信仰 ...

  7. voucer

    <style type="text/css"> .fieldset_s{border: 1px #dedede solid;padding: 19px; color: ...

  8. 关于Sublime Text2 GBK编码的问题

    很多文章都说需要"ConvertToUTF8"和"GBK Encoding Support"连个插件. 其实GBK Encoding Support完全不需要, ...

  9. 把USB打印机映射到LPT端口

    把USB打印机映射到LPT端口(pos小票机测试成功)2010-12-23 18:11:00| 分类: 編程 | 标签: |字号大中小 订阅 注释:在DOS命令行下运行以下命令(以下为示例,根据实际情 ...

  10. bootm命令中地址参数,内核加载地址以及内核入口地址

    bootm命令只能用来引导经过mkimage构建了镜像头的内核镜像文件以及根文件镜像,对于没有用mkimage对内核进行处理的话,那直接把内核下载到连接脚本中指定的加载地址0x30008000再运行就 ...