【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);
    }
}

 

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