zoukankan      html  css  js  c++  java
  • JAVA多线程之synchronized和volatile实例讲解

    在多线程中,提到线程安全、线程同步,我们经常会想到两个关键字:volatile和synchronized,那么这两者有什么区别呢?

    1. volatile修饰的变量具有可见性

    volatile是变量修饰符,其修饰的变量具有可见性。

    可见性也就是说一旦某个线程修改了该被volatile修饰的变量,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,可以立即获取修改之后的值。

    Java中为了加快程序的运行效率,对一些变量的操作通常是在该线程的寄存器或是CPU缓存上进行的,之后才会同步到主存中,而加了volatile修饰符的变量则是直接读写主存。

    2. volatile禁止指令重排

    volatile可以禁止进行指令重排。

    指令重排是指处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证各个语句的执行顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。指令重排序不会影响单个线程的执行,但是会影响到线程并发执行的正确性。

    程序执行到volatile修饰变量的读操作或者写操作时,在其前面的操作肯定已经完成,且结果已经对后面的操作可见,在其后面的操作肯定还没有进行。

    3. synchronized

    synchronized可作用于一段代码或方法,既可以保证可见性,又能够保证原子性。

    可见性体现在:通过synchronized或者Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存中。

    原子性表现在:要么不执行,要么执行到底。

    4.总结

    (1)从而我们可以看出volatile虽然具有可见性但是并不能保证原子性。

    (2)性能方面,synchronized关键字是防止多个线程同时执行一段代码,就会影响程序执行效率,而volatile关键字在某些情况下性能要优于synchronized。

    但是要注意volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。

    看下面例子:

    public class VolatileTest {
        public volatile int inc = 0;
        public void increase() {
            inc++;
        }
     
        public static void main(String[] args) {
            final VolatileTest test = new VolatileTest();
            for(int i=0;i<10;i++){
                new Thread(){
                    public void run() {
                        for(int j=0;j<1000;j++)
                            test.increase();
                    };
                }.start();
            }
     
            while(Thread.activeCount()>2)  //保证前面的线程都执行完
                Thread.yield();
            System.out.println(test.inc);
        }
    }

    例子中用new了10个线程,分别去调用1000次increase()方法,每次运行结果都不一致,都是一个小于10000的数字。自增操作不是原子操作,volatile是不能保证原子性的。使用volatile修饰int型变量i,多个线程同时进行i++操作。比如有两个线程A和B对volatile修饰的i进行i++操作,i的初始值是0,A线程执行i++时刚读取了i的值0,就切换到B线程了,B线程(从内存中)读取i的值也为0,然后就切换到A线程继续执行i++操作,完成后i就为1了,接着切换到B线程,因为之前已经读取过了,所以继续执行i++操作,最后的结果i就为1了。同理可以解释为什么每次运行结果都是小于10000的数字。

    但是使用synchronized对部分代码进行如下修改,就能保证同一时刻只有一个线程获取锁然后执行同步代码。运行结果必然是10000。

    public  int inc = 0;  
    public synchronized void increase() {  
            inc++;  
    }
  • 相关阅读:
    Count and Say leetcode
    Find Minimum in Rotated Sorted Array II leetcode
    Find Minimum in Rotated Sorted Array leetcode
    Search in Rotated Sorted Array II leetcode
    search in rotated sorted array leetcode
    Substring with Concatenation of All Words
    Subsets 子集系列问题 leetcode
    Sudoku Solver Backtracking
    Valid Sudoku leetcode
    《如何求解问题》-现代启发式方法
  • 原文地址:https://www.cnblogs.com/rinack/p/8949976.html
Copyright © 2011-2022 走看看