关于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;
    }
时间: 2024-10-13 03:58:22

关于synchronized 影响可见性的问题的相关文章

synchronized实现可见性

JMM关于synchronized的两条规定: 1)线程解锁前,必须把共享变量的最新值刷新到主内存中 2)线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新获取最新的值 (注意:加锁与解锁需要是同一把锁) 通过以上两点,可以看到synchronized能够实现可见性.同时,由于synchronized具有同步锁,所以它也具有原子性 多线程中程序交错执行时,重排序可能会造成内存可见性问题 接下来我们看一段代码: /** * synchronized能够实现原子性(同步)

1 Java线程的内存可见性

Java内存的可见性 可见性: 一个线程对共享变量的修改,能够及时被其它线程看到 共享变量: 如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量 Java内存模型(JMM): 描述了Java程序中各种线程共享变量的访问规则,以及在JVM中将线程共享变量存储到内存和从内存中读取出线程共享变量这样的底层细节 上面这些规则都是针对线程的共享变量的,JMM的细节会在以后的博客里面写. 本篇只需要知道 1 所有的变量都存储在主内存中 2 每个线程都有自己独立的工作内存,里面

多线程内存可见性

一.细说Java多线程之内存可见性(数据挣用)         1.共享变量在线程间的可见性                共享变量:如果一个变量在多个线程的工作内存中都存在副本,                         那么这个变量就是这几个线程的共享变量                可见性:一个线程对共享变量值的修改,能够及时的被其他线程看到                Java内存模型(JMM,Java Memory Model):                      描

java多线程与内存可见性

一.java多线程 JAVA多线程实现的三种方式: http://blog.csdn.net/aboy123/article/details/38307539 二.内存可见性 1.什么是JAVA 内存模型 共享变量 :如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量. Java Memory Model (JAVA 内存模型)描述线程之间如何通过内存(memory)来进行交互,描述了java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存

Java线程安全:可见性,原子性,有序性

Java线程安全 可见性,原子性,有序性 Java内存模型(JMM) Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则,以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节. 所有的变量都存储在主内存中. 每个线程都有自己独立的工作内存,里面保持该线程使用到的变量副本. 线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中进行读写. 不同线程之间无法直接访问其他线程工作内存的变量,所以线程间变量值的传递需要

多线程读写共享变量时,synchronized与volatile的作用

在<effective java>中看的的知识点,在工作中确实遇到了~ 关键字synchronized可以保证在同一时刻,只有一个线程可以执行某一个方法,或者某一个代码块. 同步并不是单单指线程之间的互斥.如果没有同步,一个线程的变化就不能被其他线程看到.同步不仅可以阻止一个线程看到对象处于不一致的状态之中, 它还可以保证进入同步方法或者同步代码块的每个线程,都看到由同一个锁保护的之前的所有修改效果. 思考下面这个程序的运行过程是什么样的. <span style="font-

【慕课网学习笔记】Java共享变量的可见性和原子性

1. Java内存模型(Java Memory Model, JMM) Java的内存模型如下,所有变量都存储在主内存中,每个线程都有自己的工作内存. 共享变量:如果一个变量在多个线程中都使用到了,那么这个变量就是这几个线程的共享变量. 可见性:一个线程对共享变量的修改,能够及时地到主内存并且让其他的线程看到. 怎么理解上面的可见性的意思呢? 线程对共享变量的修改,只能在自己的工作内存里操作,不能直接对主内存中的共享变量进行修改.而且一个线程不能直接访问另一个线程中的变量的值,只能通过主内存进行

JavaSE学习53:细说多线程之内存可见性

一共享变量在线程间的可见性 (1)有关可见性的一些概念介绍 可见性:一个线程对共享变量值的修改,能够及实地被其他线程看到. 共享变量:如果一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量.所 有的变量都存储在主内存中. 线程的工作内存:每个线程都有自己独立的工作内存,里面保存该线程使用到的变量的副本(主内存中该变量的 一份拷贝). (2)数据争用问题 多个线程对同一资源操作时,通常会产生进程,比如一个线程往消息队列插入数据,而另一个线程从消息队列取 出数据 当消息队

java多线程synchronized volatile解析

先简单说说原子性:具有原子性的操作被称为原子操作.原子操作在操作完毕之前不会线程调度器中断.即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行.在Java中,对除了long和double之外的基本类型的简单操作都具有原子性.简单操作就是赋值或者return.比如”a = 1;“和 “return a;”这样的操作都具有原子性.但是在Java中,类似”a += b”这样的操作不具有原子性,不是同步的就会出现难以预料的结果. 在我们平常的编程过程中,经常会遇到线程安