关于synchronized 影响可见性的问题
问题来自于学习thinking in java的时候的一个示例,先上代码吧
public class StopThread {
private static boolean stop = false;
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable(){
public void run() {
int i = 0;
while (!stop) {
i++;
}
}
});
t.start();
TimeUnit.SECONDS.sleep(1);
stop = true;
}
}
毫无疑问,这段代码会永远的执行下去,因为后台线程感觉不到主线程已经改变了stop,
然后我习惯性的在while循环中打印了下i(syso)
然后运行,发现程序在运行了一秒左右就停止了!!
我一脸懵逼,然后看了下syso的代码,发现有一段同步块
public void println(int x) {
synchronized (this) {
print(x);
newLine();
}
}
然后我也在代码里尝试着加了一个空的同步块,发现也会停止
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable(){
@Override
public void run() {
int i = 0;
Object lock = new Object();
long start = System.nanoTime();
while (!stop) {
i++;
synchronized (lock) {
// nothing
}
}
long end = System.nanoTime();
Duration d = Duration.ofNanos(end - start);
System.out.println(d.toMillis() + "ms");
}
});
t.start();
TimeUnit.SECONDS.sleep(1);
stop = true;
}
运行结果是 1000ms
那么问题来了,synchronized 到底干了什么。。
按理说,synchronized 只会保证该同步块中的变量的可见性,发生变化后立即同步到主存,但是,stop变量并不在同步块中
实际上,JVM对于现代的机器做了最大程度的优化,也就是说,最大程度的保障了线程和主存之间的及时的同步,也就是相当于虚拟机尽可能的帮我们加了个volatile,但是,当CPU被一直占用的时候,同步就会出现不及时,也就出现了后台线程一直不结束的情况。
举个例子,在while循环中sleep一下,程序会很快的结束,sleep方法中并没有同步代码块
public void run() {
int i = 0;
long start = System.nanoTime();
while (!stop) {
i++;
try {
TimeUnit.MILLISECONDS.sleep(10);
} catch (InterruptedException e) {
}
}
long end = System.nanoTime();
Duration d = Duration.ofNanos(end - start);
System.out.println(d.toMillis() + "ms");
}
再举个栗子,在while循环中做一些耗时但不耗CPU的操作,也会结束的很快,因为这个时候CPU空闲了,JVM就有机会尽快的将主存和栈变量同步
public static void main(String[] args) throws InterruptedException {
Thread t = new Thread(new Runnable(){
@Override
public void run() {
int i = 0;
long start = System.nanoTime();
while (!stop) {
i++;
Object[] arr = new Object[1000000];
}
long end = System.nanoTime();
Duration d = Duration.ofNanos(end - start);
System.out.println(d.toMillis() + "ms");
}
});
t.start();
TimeUnit.SECONDS.sleep(1);
stop = true;
}
关于synchronized 影响可见性的问题的更多相关文章
- synchronized的可见性理解
之前的时候看<并发编程的艺术>,书中提到dcl写法的单例模式是有问题的,有可能会导致调用者得到一个创建了一半的对象,从而导致报错.修复办法是将单例对象的引用添加volatile进行修饰,禁 ...
- java synchronized实现可见性对比volatile
问题: 大家可以先看看这个问题,看看这个是否有问题呢? 那里有问题呢? public class ThreadSafeCache { int result; public int getResult( ...
- Volatile和Synchronized对可见性和原子性的支持
在学习并发编程的时候,遇见了volatile和synchronized关键字问题,volatile是可以保证可见性,但无法保证原子性,synchronized关键字由于其是加锁机制,肯定是可以保证原子 ...
- synchronized内存可见性理解
一.背景 最近在看<Java并发编程实战>这本书,看到共享变量的可见性,其中说到"加锁的含义不仅仅局限于互斥行为,还包括内存可见性". 我对于内存可见性第一反应是vol ...
- synchronized实现可见性
JMM关于synchronized的两条规定: 1)线程解锁前,必须把共享变量的最新值刷新到主内存中 2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值 ( ...
- java线程-synchronized实现可见性代码
以下是一个普通线程代码: package com.Sychronized; public class SychronizedDemo { //共享变量 private boolean ready=fa ...
- volatile、Synchronized实现变量可见性的原理,volatile使用注意事项
变量不可见的两个原因 Java每个线程工作都有一个工作空间,需要的变量都是从主存中加载进来的.Java内存模型如下(JMM): 线程访问一个共享的变量时,都需要先从主存中加载一个副本到自己的工作内存中 ...
- 1 Java线程的内存可见性
Java内存的可见性 可见性: 一个线程对共享变量的修改,能够及时被其它线程看到 共享变量: 如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JM ...
- 细说Java多线程之内存可见性
编程这些实践的知识技能,每一次学习使用可能都会有新的认识 一.细说Java多线程之内存可见性(数据挣用) 1.共享变量在线程间的可见性 共享变量:如果一个 ...
随机推荐
- 互联网实习笔记之shell笔记
linux下面一切都是可以配置的 #vim可以有 .vimrc文件 #------.vimrc开始---- set vb t_vb= set number syntax on set hlsearch ...
- 关于mongoldb 启动时显示 add already in use
1 .不要在国内网上查找 浪费时间 2. stack over flow 是个不错的选择 进入正题. 终端输入: ps wuax | grep mongo 会看到: 随后:kill 447
- poj2763
//Accepted 11676 KB 2344 ms /* source:poj2763 time :2015.5.29 by :songt */ /*题解: 树链剖分 基于边权,路径查询 wind ...
- Hibernate 学习笔记一
Hibernate 学习笔记一 今天学习了hibernate的一点入门知识,主要是配置domain对象和表的关系映射,hibernate的一些常用的配置,以及对应的一个向数据库插入数据的小例子.期间碰 ...
- nodejs最新教程
脚本模式 以下是我们的第一个Node.js程序: 实例 console.log("Hello World"); 保存该文件,文件名为 helloworld.js, 并通过 node ...
- android SDK 更新问题解决
Android在win7更新SDK时出现问题: Download interrupted: hostname in certificate didn't match: <dl-ssl.goog ...
- .net中使用ODP.net访问Oracle数据库(无客户端部署方法)
ODP.net是Oracle提供的数据库访问类库,其功能和效率上都有所保证,它还有一个非常方便特性:在客户端上,可以不用安装Oracle客户端,直接拷贝即可使用. 以下内容转载自:http://b ...
- 万圣节的糖果(Halloween Sweets)
今天遇到codewars的一道题,这是链接,讲的是关于万圣节的一个题目,简单点说,就是9个包裹,一个天平,两次称的机会,怎么找出9个包裹中唯一一个较重的包裹. 像我这种年轻时候喜欢研究难题获得存在感的 ...
- 对一个目录的文件从cp936转换成utf-8
打开一个文件,确认能够无乱码打开 [xw@localhost work]$ vi NPOSP/src/sjl05.cpp 但是,这里打开的方式是以cp936的编码方式打开的. 编码的选择,记录在~/. ...
- js中的call和apply方法的区别
一.call和apply的说明 1.call,apply都属于Function.prototype的一个方法,它是JavaScript引擎内在实现的,因为属于Function.prototype,所以 ...