關於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;
    }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM