在《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. Django单元测试(二)------测试工具

    The test client test client是一个python类,来模拟一个简单的“哑”浏览器,允许你来测试你的view函数.你可以使用test client完成下列事情: 1.模拟&quo ...

  2. Shell编程学习---第五篇:Shell的输入和输出

    在shell脚本中,可以用几种不同的方式读入数据:可以使用标准输入—缺省为键盘,或 者指定一个文件作为输入.对于输出也是一样:如果不指定某个文件作为输出,标准输出总 是和终端屏幕相关联.如果所使用命令 ...

  3. android5.0问题

    2042-2247/system_process W/AudioTrack﹕ AUDIO_OUTPUT_FLAG_FAST denied by client libzte-ril.so has tex ...

  4. Android 内核初识(4)属性服务器

    简介 Windows平台上有一个叫注册表的东西.注册表可以存储一些类似key/value的键值对.一般而言,系统或某些应用程序会把自己的一些属性存储在注册表中,即使下次系统重启或应用程序重启,它还能够 ...

  5. Mysql常用show命令,show variables like xxx 详解,mysql运行时参数

    MySQL中有很多的基本命令,show命令也是其中之一,在很多使用者中对show命令的使用还容易产生混淆,本文汇集了show命令的众多用法. 详细: http://dev.mysql.com/doc/ ...

  6. easyui datagriad 框架 自适应r

    easyui datagriad 框架 在做列自适应时可以采取以下步骤: 1. fitColumns: true,/*自动扩大或缩小列的尺寸以适应表格的宽度并且防止水平滚动*/ 使之保持与父类宽度相同 ...

  7. java.util.concurrent.Exchanger应用范例与原理浅析--转载

    一.简介   Exchanger是自jdk1.5起开始提供的工具套件,一般用于两个工作线程之间交换数据.在本文中我将采取由浅入深的方式来介绍分析这个工具类.首先我们来看看官方的api文档中的叙述: A ...

  8. 读取编码为utf-16le的文件并打印

    import codecs data = open('test.txt').read()if data[:2] == codecs.BOM_UTF16_LE:    data = data[2:]  ...

  9. Javascript检测浏览器对CSS属性的支持 /* supports */

    //检测浏览器对CSS属性的支持 supports = (function() { var div = document.createElement('div'), vendors = 'Khtml ...

  10. Timer和counter

    什么是Timer,什么是Counter 几乎每个嵌入式板都会有counter和timer,重要性比肩gpio.本质上来看timer和counter几乎是一样的东西,底层都是一个硬件counter,如果 ...