问题来自于学习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 影响可见性的问题的更多相关文章

  1. synchronized的可见性理解

    之前的时候看<并发编程的艺术>,书中提到dcl写法的单例模式是有问题的,有可能会导致调用者得到一个创建了一半的对象,从而导致报错.修复办法是将单例对象的引用添加volatile进行修饰,禁 ...

  2. java synchronized实现可见性对比volatile

    问题: 大家可以先看看这个问题,看看这个是否有问题呢? 那里有问题呢? public class ThreadSafeCache { int result; public int getResult( ...

  3. Volatile和Synchronized对可见性和原子性的支持

    在学习并发编程的时候,遇见了volatile和synchronized关键字问题,volatile是可以保证可见性,但无法保证原子性,synchronized关键字由于其是加锁机制,肯定是可以保证原子 ...

  4. synchronized内存可见性理解

    一.背景 最近在看<Java并发编程实战>这本书,看到共享变量的可见性,其中说到"加锁的含义不仅仅局限于互斥行为,还包括内存可见性". 我对于内存可见性第一反应是vol ...

  5. synchronized实现可见性

    JMM关于synchronized的两条规定: 1)线程解锁前,必须把共享变量的最新值刷新到主内存中 2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值 ( ...

  6. java线程-synchronized实现可见性代码

    以下是一个普通线程代码: package com.Sychronized; public class SychronizedDemo { //共享变量 private boolean ready=fa ...

  7. volatile、Synchronized实现变量可见性的原理,volatile使用注意事项

    变量不可见的两个原因 Java每个线程工作都有一个工作空间,需要的变量都是从主存中加载进来的.Java内存模型如下(JMM): 线程访问一个共享的变量时,都需要先从主存中加载一个副本到自己的工作内存中 ...

  8. 1 Java线程的内存可见性

    Java内存的可见性 可见性: 一个线程对共享变量的修改,能够及时被其它线程看到 共享变量: 如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JM ...

  9. 细说Java多线程之内存可见性

    编程这些实践的知识技能,每一次学习使用可能都会有新的认识 一.细说Java多线程之内存可见性(数据挣用)         1.共享变量在线程间的可见性                共享变量:如果一个 ...

随机推荐

  1. jsp数据交互(一),九大内置对象

    九大内置对象 九大内置对象 内置对象 类型 说明 作用域 request javax.servlet.ServletRequest 请求对象——在 一次请求/一个Request请求周期 中传递数据,请 ...

  2. 802.11MAC基础

    做无线网络测试已经大半年了,在这过程中发现<802.11权威指南>真是以本好书,在这里分享一下学习到的知识,也帮助我记忆. 1.MAC: mac(媒介访问控制层),它位于物理层之上,控制着 ...

  3. Mac下手动安装Chromedriver.exe

    Mac OS X Yosemite 10.10.4下,ChromeDriver运行异常,需要手动安装chromedriver.exe Step 1: 打开https://sites.google.co ...

  4. 理解python的with语句

    Python’s with statement provides a very convenient way of dealing with the situation where you have ...

  5. css position static | absolute | fixed | relative

    <div id="bigbox1">    <div id="box1" class="box">box1</ ...

  6. 我的第一个HTML的程序

    <html> <head><title>title为显示标签页的内容</title></head> <body> <h1& ...

  7. LayaAir引擎——(九)

    var h = new Array(); var j = new Array(); var xbCursor = 0; function xbinit() { xbinitName(); xbRect ...

  8. win32自绘按钮,使用GDI+(二)

    一.解决上一篇的两个问题. 1.按钮背景透明 方法是,在绘制按钮之前,向按钮的父窗口发生WM_CTLCOLORBTN消息.该消息返回一个画刷句柄,系统使用该画刷句柄画出按钮的背景.所以我们在处理这个消 ...

  9. Word Break II

    Given a string s and a dictionary of words dict, add spaces in s to construct a sentence where each ...

  10. 可能是Mac环境变量恢复的参考

    因为要做物联网实验的缘故,于是在Mac上用Android Studio想导入SensorSimulator的demo项目. 根据SensorSimulator的相关说明,需要先将Sensor Simu ...