java多线程总结-同步之volatile关键字

1 案例之变量内存可见性

代码解析:新起一个子线程执行m()方法,1秒后主线程将b置为false,子线程是否会中止执行死循环while(b){},打印“end”缓存

package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_09 {
    
    boolean b = true;
    
    void m(){
        System.out.println("start");
        while(b){}
        System.out.println("end");
    }
    
    public static void main(String[] args) {
        final Test_09 t = new Test_09();
        new Thread(new Runnable() {
            @Override
            public void run() {
                t.m();
            }
        }).start();
        
        try {
            TimeUnit.SECONDS.sleep(1);
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        
        t.b = false;
    }
    
}

结果:1秒钟事后并不会中止执行死循环while(b){},打印“end”
这时候,若是将boolean b = true;这段代码前加一个volatile关键字
volatile boolean b = true;,就会达到预想中的效果安全

思考:为何加上这个关键字,其余线程就会读取到已经改变的变量的值了?ide

是由于在CPU计算过程当中,会将计算过程须要的数据加载到CPU计算缓存中,当CPU计算中断时,有可能刷新缓存,从新读取内存中的数据。在线程运行的过程当中,若是某变量被其余线程修改,可能形成数据不一致的状况,从而致使结果错误。
而volatile修饰的变量是线程可见的,当JVM解释volatile修饰的变量时,会通知CPU,在计算过程当中,每次使用变量参与计算时,都会检查内存中的数据是否发生变化,而不是一直使用CPU缓存中的数据,能够保证计算结果的正确。atom

可是这样还有一个问题,volatile只能保证可见性,不能保证原子性线程

2 案例之变量的原子性

下面再看一个示例:
预期结果:起10个线程,每一个线程都对count增长10000,预期结果为count=100000code

package com.bernardlowe.concurrent.t01;

import java.util.ArrayList;
import java.util.List;

public class Test_10 {
    
    volatile int count = 0;
    /*synchronized*/ void m(){
        for(int i = 0; i < 10000; i++){
            count++;
        }
    }
    
    public static void main(String[] args) {
        final Test_10 t = new Test_10();
        List<Thread> threads = new ArrayList<>();
        for(int i = 0; i < 10; i++){
            threads.add(new Thread(new Runnable() {
                @Override
                public void run() {
                    t.m();
                }
            }));
        }
        for(Thread thread : threads){
            thread.start();
        }
        for(Thread thread : threads){
            try {
                thread.join();
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        System.out.println(t.count);
    }
}

但结果并非blog

缘由是volatile只是通知底层计算时,CPU检查内存数据,而不是让一个变量在多个线程中同步。内存

这时候能够给m()方法增长一个synchronized关键字,能够达到预期的效果,即synchronized void m()rem

还有另外一种方法能够保证原子性,在上面代码将count声明为AtomicInteger原子操做,结果仍然是100000

// 其中的每一个方法都是原子操做。能够保证线程安全。
    AtomicInteger count = new AtomicInteger(0);
    void m(){
        for(int i = 0; i < 10000; i++){
            count.incrementAndGet();
        }
    }

这里不单单可声明Integer类型,java.util.concurrent.atomic包里面还有其余类型的

相关文章
相关标签/搜索