【Java】創建線程池驗證synchronized以及線程池關閉的正確姿勢

 talk is cheap,show me the code

這裏創建了包含10個線程的線程池,每個線程池對變量做1000次自增

如果去掉synchronized,最後的結果就不是10000,因爲自增操作不是原子性的

舉個栗子:

假如某個時刻變量inc的值爲10,

線程1對變量進行自增操作,線程1先讀取了變量inc的原始值,然後線程1被阻塞了;

然後線程2對變量進行自增操作,線程2也去讀取變量inc的原始值,由於線程1只是對變量inc進行讀取操作,而沒有對變量進行修改操作,所以不會導致線程2的工作內存中緩存變量inc的緩存行無效,所以線程2會直接去主存讀取inc的值,發現inc的值時10,然後進行加1操作,並把11寫入工作內存,最後寫入主存。

然後線程1接着進行加1操作,由於已經讀取了inc的值,注意此時在線程1的工作內存中inc的值仍然爲10,所以線程1對inc進行加1操作後inc的值爲11,然後將11寫入工作內存,最後寫入主存。

那麼兩個線程分別進行了一次自增操作後,inc只增加了1。

最後說明一點,操作的原子性是靠鎖保證的,僅僅用volatile關鍵字修飾是沒有用的

因爲volatile僅僅保證了變量進行操作時的可見性,也就是讀取到最新的值,但是無法保證對變量的操作的原子性

那麼volatile關鍵字修飾有啥用呢?

  • 使用volatile關鍵字會強制將修改的值立即寫入主存;
  • 使用volatile關鍵字的話,當線程2進行修改時,會導致線程1的工作內存中緩存變量stop的緩存行無效(反映到硬件層的話,就是CPU的L1或者L2緩存中對應的緩存行無效);
  • 由於線程1的工作內存中緩存變量stop的緩存行無效,所以線程1再次讀取變量stop的值時會去主存讀取。
package learn_synchronized;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class test_volatile {

    // 可見性
    public volatile int inc = 0;

    public synchronized void increase() {
        inc++;
    }

    public static void main(String[] args) {
        final test_volatile test = new test_volatile();
        ExecutorService addTreads = Executors.newFixedThreadPool(10);
        for (int i =1; i <= 10;i++){
            final int index=i ;
            addTreads.execute(new Runnable(){
                @Override
                public void run() {
                    System.out.println("\n第"+index+"個線程"+Thread.currentThread().getName());
                    for(int j = 0;j < 1000;j++) {
                        test.increase();
                    }
                }

            });
        }
        addTreads.shutdown();
        // 若關閉後所有任務都已完成,則返回true。
        // 注意除非首先調用shutdown或shutdownNow,否則isTerminated永不爲true。
        // 返回:若關閉後所有任務都已完成,則返回true
        while (!addTreads.isTerminated()) {
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println("\n結束了!");
        System.out.println("\n"+test.inc);
    }
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章