精华内容
下载资源
问答
  • [超级链接:Java并发学习系列-绪论] 在Java并发编程中,如果要保证代码的安全性,则必须保证代码的原子性、可见性和有序性。 在 Java并发12:并发三特性-...原子定义:一个或多个操作,要么全部执行且在执行过...

    [超级链接:Java并发学习系列-绪论]

    在Java并发编程中,如果要保证代码的安全性,则必须保证代码的原子性、可见性和有序性。

    Java并发12:并发三特性-原子性、可见性和有序性概述及问题示例中,对并发中的三个特性(原子性、可见性和有序性)进行了初步学习。

    本章主要就Java中保障原子性的技术进行更加全面的学习。

    1.整体回顾

    • 原子性定义:一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行。
    • Java自带原子性:对基本数据类型的变量读取赋值操作是原子性操作。

    2.原子性问题

    由上面的章节已知,不采取任何的原子性保障措施的自增操作并不是原子性的。
    下面的代码实现了一个自增器(不是原子性的)。

    /**
     * <p>原子性示例:不是原子性</p>
     *
     * @author hanchao 2018/3/10 14:58
     **/
    static class Increment {
        private int count = 1;
    
        public void increment() {
            count++;
        }
    
        public int getCount() {
            return count;
        }
    }

    下面的代码展示了在多线程环境中,调用此自增器进行自增操作。

    int type = 0;//类型
    int num = 50000;//自增次数
    int sleepTime = 5000;//等待计算时间
    int begin;//开始的值
    Increment increment;
    //不进行原子性保护的大范围操作
    increment = new Increment();
    begin = increment.getCount();
    LOGGER.info("Java中普通的自增操作不是原子性操作。");
    LOGGER.info("当前运行类:" +increment.getClass().getSimpleName() +  ",count的初始值是:" + increment.getCount());
    for (int i = 0; i < num; i++) {
        new Thread(() -> {
            increment.increment();
        }).start();
    }
    //等待足够长的时间,以便所有的线程都能够运行完
    Thread.sleep(sleepTime);
    LOGGER.info("进过" + num + "次自增,count应该 = " + (begin + num) + ",实际count = " + increment.getCount());

    某次运行结果:

    2018-03-17 22:52:23 INFO  ConcurrentAtomicityDemo:132 - Java中普通的自增操作不是原子性操作。
    2018-03-17 22:52:23 INFO  ConcurrentAtomicityDemo:133 - 当前运行类:Increment,count的初始值是:1
    2018-03-17 22:52:33 INFO  ConcurrentAtomicityDemo:141 - 进过50000次自增,count应该 = 50001,实际count = 49999

    通过观察结果,发现程序确实存在原子性问题。

    3.原子性技术保障

    在Java中提供了多种原子性保障措施,这里主要涉及三种:

    • 通过synchronized关键字定义同步代码块或者同步方法保障原子性。
    • 通过Lock接口保障原子性。
    • 通过Atomic类型保障原子性。

    3.1.synchronized关键字

    Increment类进行扩展:

    /**
     * <p>原子性示例:通过synchronized保证代码块的原子性</p>
     *
     * @author hanchao 2018/3/10 15:07
     **/
    static class SynchronizedIncrement extends Increment {
        /**
         * <p>添加关键字synchronized,使之成为同步方法</p>
         *
         * @author hanchao 2018/3/10 15:12
         **/
        @Override
        public synchronized void increment() {
            super.count++;
        }
    }

    在多线程环境中进行SynchronizedIncrement 的自增:

    //synchronized关键字能够保证原子性(代码块锁,多线程操作某一对象时,在某个代码块内只能单线程执行)
    increment = new SynchronizedIncrement();
    begin = increment.getCount();
    LOGGER.info("可以通过synchronized关键字保障代码的原子性");
    LOGGER.info("当前运行类:" +increment.getClass().getSimpleName() +  ",count的初始值是:" + increment.getCount());
    for (int i = 0; i < num; i++) {
        new Thread(() -> {
            increment.increment();
        }).start();
    }
    //等待足够长的时间,以便所有的线程都能够运行完
    Thread.sleep(sleepTime);
    LOGGER.info("进过" + num + "次自增,count应该 = " + (begin + num) + ",实际count = " + increment.getCount());

    运行结果(多次):

    2018-03-18 00:41:30 INFO  ConcurrentAtomicityDemo:147 - 可以通过synchronized关键字保障代码的原子性
    2018-03-18 00:41:30 INFO  ConcurrentAtomicityDemo:148 - 当前运行类:SynchronizedIncrement,count的初始值是:1
    2018-03-18 00:41:40 INFO  ConcurrentAtomicityDemo:156 - 进过50000次自增,count应该 = 50001,实际count = 50001

    通过多次运行,发现运行结果一致,所以可以确定synchronized关键字能够保证代码的原子性

    3.2.Lock接口

    Increment类进行扩展:

    /**
    * <p>原子性示例:通过Lock接口保证指定范围代码的原子性</p>
    *
    * @author hanchao 2018/3/10 15:14
    **/
    static class LockIncrement extends Increment {
       //定义个读写锁:锁内运行多线程读,单线程写
       private static final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock(true);
    
       /**
        * <p>运用读写所重写方法</p>
        *
        * @author hanchao 2018/3/10 15:13
        **/
       @Override
       public void increment() {
           //写锁 加锁
           readWriteLock.writeLock().lock();
           try {
               //开始写
               super.count++;
           } finally {
               //将解锁放在finally块中,保证必然执行,防止死锁
               readWriteLock.writeLock().unlock();
           }
       }
    }

    在多线程环境中进行LockIncrement的测试:

    //通过Lock接口保证原子性操作
    increment = new LockIncrement();
    begin = increment.getCount();
    LOGGER.info("可以通过Lock接口保证代码的原子性");
    LOGGER.info("当前运行类:" +increment.getClass().getSimpleName() +  ",count的初始值是:" + increment.getCount());
    for (int i = 0; i < num; i++) {
        new Thread(() -> {
            increment.increment();
        }).start();
    }
    //等待足够长的时间,以便所有的线程都能够运行完
    Thread.sleep(sleepTime);
    LOGGER.info("进过" + num + "次自增,count应该 = " + (begin + num) + ",实际count = " + increment.getCount());

    运行结果(多次):

    2018-03-18 10:12:12 INFO  ConcurrentAtomicityDemo:163 - 可以通过Lock接口保证代码的原子性
    2018-03-18 10:12:12 INFO  ConcurrentAtomicityDemo:164 - 当前运行类:LockIncrement,count的初始值是:1
    2018-03-18 10:12:29 INFO  ConcurrentAtomicityDemo:172 - 进过50000次自增,count应该 = 50001,实际count = 50001

    通过多次运行,发现运行结果一致,所以可以确定Lock接口能够保证代码的原子性

    3.3.Atomic类型

    Increment类进行扩展:

    /**
    * <p>原子性示例:通过Atomic类型保证类型的原子性</p>
    *
    * @author hanchao 2018/3/10 15:19
    **/
    static class AtomicIncrement {
       private AtomicInteger count = new AtomicInteger(1);
    
       /**
        * <p>无需其他处理,直接自增即可</p>
        *
        * @author hanchao 2018/3/10 15:21
        **/
       public void increment() {
           count.getAndIncrement();
       }
    
       public AtomicInteger getCount() {
           return count;
       }
    }

    在多线程环境中进行AtomicIncrement的测试:

    //通过Atomic变量保证变量操作的原子性
    AtomicIncrement increment1 = new AtomicIncrement();
    begin = increment1.getCount().get();
    LOGGER.info("可以通过Atomic类型保证变量的原子性");
    LOGGER.info("当前运行类:" +increment1.getClass().getSimpleName() +  ",count的初始值是:" + increment1.getCount());
    for (int i = 0; i < num; i++) {
        new Thread(() -> {
            increment1.increment();
        }).start();
    }
    //等待足够长的时间,以便所有的线程都能够运行完
    Thread.sleep(sleepTime);
    LOGGER.info("进过" + num + "次自增,count应该 = " + (begin + num) + ",实际count = " + increment1.getCount());

    运行结果(多次):

    2018-03-18 10:14:37 INFO  ConcurrentAtomicityDemo:178 - 可以通过Atomic类型保证变量的原子性
    2018-03-18 10:14:37 INFO  ConcurrentAtomicityDemo:179 - 当前运行类:AtomicIncrement,count的初始值是:1
    2018-03-18 10:14:48 INFO  ConcurrentAtomicityDemo:187 - 进过50000次自增,count应该 = 50001,实际count = 50001

    通过多次运行,发现运行结果一致,所以可以确定Atomic类型能够保证代码的原子性

    4.总结

    经验证,以下三种措施,可以保证Java代码在运行时的原子性:

    • synchronized关键字
    • Lock接口
    • Atomic类型

    并发三特性总结

    特性volatile关键字synchronized关键字Lock接口Atomic变量
    原子性无法保障可以保障可以保障可以保障
    可见性可以保障可以保障可以保障可以保障
    有序性一定程度保障可以保障可以保障无法保障
    展开全文
  • 原子性、有序性和可见性解释

    千次阅读 2019-05-17 18:26:53
    原子性(Atomicity) 由 Java 内存模型来直接保证的原子性变量操作包括 read、load、assign、use、store 和 write。大致可以认为基本数据类型的操作是原子性的。同时 lock 和 unlock 可以保证更大范围操作的原子性。而...

    概念解释:

    原子性(Atomicity)
    由 Java 内存模型来直接保证的原子性变量操作包括 read、load、assign、use、store 和 write。大致可以认为基本数据类型的操作是原子性的。同时 lock 和 unlock 可以保证更大范围操作的原子性。而 synchronize 同步块操作的原子性是用更高层次的字节码指令 monitorenter 和 monitorexit 来隐式操作的。

    可见性(Visibility)
    是指当一个线程修改了共享变量的值,其他线程也能够立即得知这个通知。主要操作细节就是修改值后将值同步至主内存(volatile 值使用前都会从主内存刷新),除了 volatile 还有 synchronize 和 final 可以保证可见性。同步块的可见性是由“对一个变量执行 unlock 操作之前,必须先把此变量同步会主内存中( store、write 操作)”这条规则获得。而 final 可见性是指:被 final 修饰的字段在构造器中一旦完成,并且构造器没有把 “this” 的引用传递出去( this 引用逃逸是一件很危险的事情,其他线程有可能通过这个引用访问到“初始化了一半”的对象),那在其他线程中就能看见 final 字段的值。

    有序性(Ordering)
    如果在被线程内观察,所有操作都是有序的;如果在一个线程中观察另一个线程,所有操作都是无序的。前半句指“线程内表现为串行的语义”,后半句是指“指令重排”现象和“工作内存与主内存同步延迟”现象。Java 语言通过 volatile 和 synchronize 两个关键字来保证线程之间操作的有序性。volatile 自身就禁止指令重排,而 synchronize 则是由“一个变量在同一时刻指允许一条线程对其进行 lock 操作”这条规则获得,这条规则决定了持有同一个锁的两个同步块只能串行的进入。
     

    1、原子性

    原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。我们先来看看哪些是原子操作,哪些不是原子操作,有一个直观的印象:

    int a = 10; //1
    a++; //2
    int b=a; //3
    a = a+1; //4

    上面这四个语句中只有第1个语句是原子操作,将10赋值给线程工作内存的变量a,而语句2(a++),实际上包含了三个操作:1. 读取变量a的值;2:对a进行加一的操作;3.将计算后的值再赋值给变量a,而这三个操作无法构成原子操作。对语句3,4的分析同理可得这两条语句不具备原子性。当然,java内存模型中定义了8中操作都是原子的,不可再分的。

    上面的这些指令操作是相当底层的,可以作为扩展知识面掌握下。那么如何理解这些指令了?比如,把一个变量从主内存中复制到工作内存中就需要执行read,load操作,将工作内存同步到主内存中就需要执行store,write操作。注意的是:java内存模型只是要求上述两个操作是顺序执行的并不是连续执行的。也就是说read和load之间可以插入其他指令,store和writer可以插入其他指令。比如对主内存中的a,b进行访问就可以出现这样的操作顺序:read a,read b, load b,load a

    由原子性变量操作read,load,use,assign,store,write,可以大致认为基本数据类型的访问读写具备原子性(例外就是long和double的非原子性协定)

    synchronized:

    上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和unlock两条原子操作。如果我们需要更大范围的原子性操作就可以使用lock和unlock原子操作。尽管jvm没有把lock和unlock开放给我们使用,但jvm以更高层次的指令monitorenter和monitorexit指令开放给我们使用,反应到java代码中就是---synchronized关键字,也就是说synchronized满足原子性

    上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和unlock两条原子操作。如果我们需要更大范围的原子性操作就可以使用lock和unlock原子操作。尽管jvm没有把lock和unlock开放给我们使用,但jvm以更高层次的指令monitorenter和monitorexit指令开放给我们使用,反应到java代码中就是---synchronized关键字,

     

    也就是说volatile
    我们先来看这样一个例子:

    public class VolatileExample {
        private static volatile int counter = 0;
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 10000; i++)
                            counter++;
                    }
                });
                thread.start();
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(counter);
        }
    }

    开启10个线程,每个线程都自加10000次,如果不出现线程安全的问题最终的结果应该就是:10*10000 = 100000;可是运行多次都是小于100000的结果,问题在于 volatile并不能保证原子性,在前面说过counter++这并不是一个原子操作,包含了三个步骤:1.读取变量counter的值;2.对counter加一;3.将新值赋值给变量counter。如果线程A读取counter到工作内存后,其他线程对这个值已经做了自增操作后,那么线程A的这个值自然而然就是一个过期的值,因此,总结果必然会是小于100000的。

    如果让volatile保证原子性,必须符合以下两条规则:

    1. 运算结果并不依赖于变量的当前值,或者能够确保只有一个线程修改变量的值;
    2. 变量不需要与其他的状态变量共同参与不变约束

    3. 有序性

    synchronized

    synchronized语义表示锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。因此,synchronized语义就要求线程在访问读写共享变量时只能“串行”执行,因此synchronized具有有序性

    volatile

    在java内存模型中说过,为了性能优化,编译器和处理器会进行指令重排序;也就是说java程序天然的有序性可以总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。在单例模式的实现上有一种双重检验锁定的方式(Double-checked Locking)。代码如下:

    4. 可见性

    可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。通过之前对synchronzed内存语义进行了分析,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。从而,synchronized具有可见性。同样的在volatile分析中,会通过在指令中添加lock指令,以实现内存可见性。因此, volatile具有可见性

    5. 总结

    通过这篇文章,主要是比较了synchronized和volatile在三条性质:原子性,可见性,以及有序性的情况,归纳如下:

    synchronized: 具有原子性,有序性和可见性
    volatile:具有有序性和可见性

    展开全文
  • 原子性、可见性、有序性的概念都是基于多线程操作条件下的。 原子性:一个操作具有原子操作,那么我们称它具有原子性。(什么是原子操作?一个操作是不可分割的。) 原子是世界上的最小单位,具有不可分割性...

    原子性、可见性、有序性的概念都是基于多线程操作条件下的。

     

     

    原子性:一个操作具有原子操作,那么我们称它具有原子性。(什么是原子操作?一个操作是不可分割的。)

            原子是世界上的最小单位,具有不可分割性比如 a=0;(a非long和double类型) 这个操作是不可分割的,那么我们说这个操作时原子操作。再比如:a++; 这个操作实际是a = a + 1;是可分割的,所以他不是一个原子操作。非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。一个操作是原子操作,那么我们称它具有原子性。java的concurrent包下提供了一些原子类,我们可以通过阅读API来了解这些原子类的用法。比如:AtomicInteger、AtomicLong、AtomicReference等。

      在 Java 中 synchronized 和在 lock、unlock 中操作保证原子性。

     

    可见性:

      可见性是一种复杂的属性,因为可见性中的错误总是会违背我们的直觉。通常,我们无法确保执行读操作的线程能适时地看到其他线程写入的值,有时甚至是根本不可能的事情。为了确保多个线程之间对内存写入操作的可见性,必须使用同步机制。

      可见性,是指线程之间的可见性,一个线程修改的状态对另一个线程是可见的。也就是一个线程修改的结果。另一个线程马上就能看到。比如:用volatile修饰的变量,就会具有可见性。volatile修饰的变量不允许线程内部缓存和重排序,即直接修改内存。所以对其他线程是可见的。但是这里需要注意一个问题,volatile只能让被他修饰内容具有可见性,但不能保证它具有原子性。比如 volatile int a = 0;之后有一个操作 a++;这个变量a具有可见性,但是a++ 依然是一个非原子操作,也就是这个操作同样存在线程安全问题。

      在 Java 中 volatile、synchronized 和 final 实现可见性。

     

    有序性:

      Java 语言提供了 volatile 和 synchronized 两个关键字来保证线程之间操作的有序性,volatile 是因为其本身包含“禁止指令重排序”的语义,synchronized 是由“一个变量在同一个时刻只允许一条线程对其进行 lock 操作”这条规则获得的,此规则决定了持有同一个对象锁的两个同步块只能串行执行。

     

    展开全文
  • 在并发编程中分析线程安全的问题时往往需要切入点,那就是两大核心:JMM抽象内存模型以及happens-before规则(在这篇文章中已经经过了),三条性质:原子性,有序性和可见性。关于synchronized和volatile已经讨论过...

    原创文章&经验总结&从校招到A厂一路阳光一路沧桑

    详情请戳www.coderccc.com

    # 1. 三大性质简介 #
    在并发编程中分析线程安全的问题时往往需要切入点,那就是两大核心:JMM抽象内存模型以及happens-before规则(在这篇文章中已经经过了),三条性质:原子性,有序性和可见性。关于synchronizedvolatile已经讨论过了,就想着将并发编程中这两大神器在 原子性,有序性和可见性上做一个比较,当然这也是面试中的高频考点,值得注意。

    2. 原子性

    原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着“同生共死”的感觉。及时在多个线程一起执行的时候,一个操作一旦开始,就不会被其他线程所干扰。我们先来看看哪些是原子操作,哪些不是原子操作,有一个直观的印象:

    int a = 10; //1

    a++; //2

    int b=a; //3

    a = a+1; //4

    上面这四个语句中只有第1个语句是原子操作,将10赋值给线程工作内存的变量a,而语句2(a++),实际上包含了三个操作:1. 读取变量a的值;2:对a进行加一的操作;3.将计算后的值再赋值给变量a,而这三个操作无法构成原子操作。对语句3,4的分析同理可得这两条语句不具备原子性。当然,java内存模型中定义了8中操作都是原子的,不可再分的。

    1. lock(锁定):作用于主内存中的变量,它把一个变量标识为一个线程独占的状态;
    2. unlock(解锁):作用于主内存中的变量,它把一个处于锁定状态的变量释放出来,释放后的变量才可以被其他线程锁定
    3. read(读取):作用于主内存的变量,它把一个变量的值从主内存传输到线程的工作内存中,以便后面的load动作使用;
    4. load(载入):作用于工作内存中的变量,它把read操作从主内存中得到的变量值放入工作内存中的变量副本
    5. use(使用):作用于工作内存中的变量,它把工作内存中一个变量的值传递给执行引擎,每当虚拟机遇到一个需要使用到变量的值的字节码指令时将会执行这个操作;
    6. assign(赋值):作用于工作内存中的变量,它把一个从执行引擎接收到的值赋给工作内存的变量,每当虚拟机遇到一个给变量赋值的字节码指令时执行这个操作;
    7. store(存储):作用于工作内存的变量,它把工作内存中一个变量的值传送给主内存中以便随后的write操作使用;
    8. write(操作):作用于主内存的变量,它把store操作从工作内存中得到的变量的值放入主内存的变量中。

    上面的这些指令操作是相当底层的,可以作为扩展知识面掌握下。那么如何理解这些指令了?比如,把一个变量从主内存中复制到工作内存中就需要执行read,load操作,将工作内存同步到主内存中就需要执行store,write操作。注意的是:java内存模型只是要求上述两个操作是顺序执行的并不是连续执行的。也就是说read和load之间可以插入其他指令,store和writer可以插入其他指令。比如对主内存中的a,b进行访问就可以出现这样的操作顺序:read a,read b, load b,load a

    由原子性变量操作read,load,use,assign,store,write,可以大致认为基本数据类型的访问读写具备原子性(例外就是long和double的非原子性协定)

    synchronized

    上面一共有八条原子操作,其中六条可以满足基本数据类型的访问读写具备原子性,还剩下lock和unlock两条原子操作。如果我们需要更大范围的原子性操作就可以使用lock和unlock原子操作。尽管jvm没有把lock和unlock开放给我们使用,但jvm以更高层次的指令monitorenter和monitorexit指令开放给我们使用,反应到java代码中就是—synchronized关键字,也就是说synchronized满足原子性

    volatile
    我们先来看这样一个例子:

    public class VolatileExample {
        private static volatile int counter = 0;
    
        public static void main(String[] args) {
            for (int i = 0; i < 10; i++) {
                Thread thread = new Thread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 10000; i++)
                            counter++;
                    }
                });
                thread.start();
            }
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(counter);
        }
    }
    

    开启10个线程,每个线程都自加10000次,如果不出现线程安全的问题最终的结果应该就是:10*10000 = 100000;可是运行多次都是小于100000的结果,问题在于 volatile并不能保证原子性,在前面说过counter++这并不是一个原子操作,包含了三个步骤:1.读取变量counter的值;2.对counter加一;3.将新值赋值给变量counter。如果线程A读取counter到工作内存后,其他线程对这个值已经做了自增操作后,那么线程A的这个值自然而然就是一个过期的值,因此,总结果必然会是小于100000的。

    如果让volatile保证原子性,必须符合以下两条规则:

    1. 运算结果并不依赖于变量的当前值,或者能够确保只有一个线程修改变量的值;
    2. 变量不需要与其他的状态变量共同参与不变约束

    3. 有序性

    synchronized

    synchronized语义表示锁在同一时刻只能由一个线程进行获取,当锁被占用后,其他线程只能等待。因此,synchronized语义就要求线程在访问读写共享变量时只能“串行”执行,因此synchronized具有有序性

    volatile

    在java内存模型中说过,为了性能优化,编译器和处理器会进行指令重排序;也就是说java程序天然的有序性可以总结为:如果在本线程内观察,所有的操作都是有序的;如果在一个线程观察另一个线程,所有的操作都是无序的。在单例模式的实现上有一种双重检验锁定的方式(Double-checked Locking)。代码如下:

    public class Singleton {
        private Singleton() { }
        private volatile static Singleton instance;
        public Singleton getInstance(){
            if(instance==null){
                synchronized (Singleton.class){
                    if(instance==null){
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    这里为什么要加volatile了?我们先来分析一下不加volatile的情况,有问题的语句是这条:

    instance = new Singleton();

    这条语句实际上包含了三个操作:1.分配对象的内存空间;2.初始化对象;3.设置instance指向刚分配的内存地址。但由于存在重排序的问题,可能有以下的执行顺序:

    不加volatile可能的执行时序

    如果2和3进行了重排序的话,线程B进行判断if(instance==null)时就会为true,而实际上这个instance并没有初始化成功,显而易见对线程B来说之后的操作就会是错得。而用volatile修饰的话就可以禁止2和3操作重排序,从而避免这种情况。volatile包含禁止指令重排序的语义,其具有有序性

    4. 可见性

    可见性是指当一个线程修改了共享变量后,其他线程能够立即得知这个修改。通过之前对synchronzed内存语义进行了分析,当线程获取锁时会从主内存中获取共享变量的最新值,释放锁的时候会将共享变量同步到主内存中。从而,synchronized具有可见性。同样的在volatile分析中,会通过在指令中添加lock指令,以实现内存可见性。因此, volatile具有可见性

    5. 总结

    通过这篇文章,主要是比较了synchronized和volatile在三条性质:原子性,可见性,以及有序性的情况,归纳如下:

    synchronized: 具有原子性,有序性和可见性
    volatile:具有有序性和可见性

    参考文献

    《java并发编程的艺术》
    《深入理解java虚拟机》

    展开全文
  • [超级链接:Java并发学习系列-绪论] 本章主要学习Java并发中的三个特性:原子性、可见性和有序性。 在Java并发编程中,如果要保证代码的安全性,则必须保证...1.1.原子定义 原子性:一个或多个操作,要么全部...
  • 但是volatile只是对关键字进行了修饰,保证了其可见性,而对于线程的有序性和变量的原子性,volatile根本没有什么卵用,也就是什么意思呢? private static volatile int volatileCount = 0; private static void ...
  • Java的原子性&&可见性&&有序

    万次阅读 2017-06-21 10:16:53
    Java的原子性&&可见性&&有序原子定义原子性:是指一个操作或多个操作要么全部执行,且执行的过程不会被任何因素打断,要么就都不执行。原子操作原理(处理器是如何实现原子操作的)处理器实现原子操作有3种...
  • Java面试题大全(2020版)

    万次阅读 多人点赞 2019-11-26 11:59:06
    不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类,如下图所示,编辑器也会提示错误信息: 14. 接口和抽象类有什么区别? 实现:抽象类...
  • 目录Java内存模型原子性可见性有序性Happen Before 规则 Java内存模型 ​ Java虚拟机规范试图定义一种Java内存模型(JMM),来屏蔽掉各种硬件和操作系统的内存访问差异,让Java程序在各种平台上都能达到一致的内存...
  • 其实就是该数据对这多个线程没有可见性,这些线程就不能有序性的去操作共同数据,还不是原子操作,所以导致预期结果不一样。这之间的一些细节的问题是啥呢?下面我们举例说明: 在说例子之前我们来看一下,...
  • 线程安全性详解(原子性、可见性、有序性)

    千次阅读 多人点赞 2019-10-22 10:00:52
    一、定义:什么是线程安全性 当多个线程访问某个类时,不管运行时环境采用 何种调度方式 或者这些进程将如何交替执行,并且在主调代码中不需要任何额外的同步或协同,这个类都能表现出正确的行为,那么就称这个类...
  • Java内存模型是围绕着并发过程中如何处理原子性、可见性、有序性这三个特征来建立的,下面是这三个特性的实现原理: 1.原子性(Atomicity)  由Java内存模型来直接保证的原子性变量操作包括read、load、use、assign、...
  • 在并发编程中,我们通常会遇到以下三个问题:原子性问题,可见性问题,有序性问题。1、原子性即一个操作或者多个操作,要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。原子性就像数据库里面的事务...
  • 原子原子性:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。
  • [超级链接:Java并发学习系列-绪论] 在Java并发编程中,如果要保证代码的安全性,则必须保证代码的原子性、可见性和有序性。 在 Java并发12:并发三特性-...有序定义:即程序执行的顺序按照代码的先后顺序执行...
  • volatile的可见性、原子性、有序性? 1.什么是volatile? volatile是Java虚拟机提供的轻量级的同步机制 2.volatile特性 可见性 不保证原子性 禁止指令重排(有序性) 3.具体介绍 ​ 具体介绍之前,介绍一个概念...
  • 原子性、可见性、有序性解决方案

    千次阅读 2020-07-11 09:38:27
    原子性、可见性、有序性解决方案 (一)原子原子性是指:一个或多个操作,要么全部执行且在执行过程中不被任何因素打断,要么全部不执行。在Java中当我们讨论一个操作具有原子性问题是一般就是指这个操作会被线程...
  • 定义:即一个操作或者多个操作 要么全部执行并且执行的过程不会被任何因素打断,要么就都不执行。 举个最简单的例子,大家想一下假如为一个32位的变量赋值过程不具备原子性的话,会发生什么后果? int i...
  • mysql面试题

    千次阅读 2019-09-23 12:28:36
    事务(transaction)是作为一个单元的一组有序的数据库操作。如果组中的所有操作都成功,则认为事务成功,即使只有一个操作失败,事务也不成功。如果所有操作完成,事务则提交,其修改将作用于所有其他数据库进程。...
  • 一、原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败,有着...那么java内存模型中定义了8种原子方法,这8中方法也不能再拆分 1.lock(锁定)用于锁定主内存中变量,将他变成一个线程独占的状...
  • 原子性、可见性、有序性、 (1)原子原子性是指一个操作是不可中断的,要么全部执行成功要么全部执行失败 ```javascript Int a = 10; //原子性 a++; //1.读取a的值 2.对a+1 3.更新的值赋给a Int b = a; //...
  • 计算机复试面试题总结

    万次阅读 多人点赞 2019-03-07 20:06:56
    原子性,一致性,隔离性,持久性。 4.数据库和文件系统的比较? 数据库结构化,共享性好,独立性。有界面接口。 5.数据模型有哪几种? 关系模型,层次模型,网状模型 6索引建的多的好还是少的好? 恰当把握,多的话...
  • 前言 我在上一篇文章简单的介绍了一些synchronized关键字的知识点和用法(有兴趣的可以点这里,传送门biubiu),而这篇文章主要介绍synchronized底层实现,还有它是如何保证原子性、有序性和可见性的。 在进入正题...
  • Java内存模型(JMM)定义了主存、工作内存的抽象概念,体现在以下几个方面: 原子性:保证指令不会受到线程上下文切换的影响 可见性:保证指令不会受cpu缓存的影响 有序性:保证指令不会受cpu指令并行优化的影响 1、...
  • Java多线程编程基础三(原子性,可见性和有序性)

    万次阅读 多人点赞 2018-04-27 16:18:40
    也就是说,要想并发程序正确地执行,必须要保证原子性、可见性以及有序性。只要有一个没有被保证,就有可能会导致程序运行不正确。 在Java内存模型中,允许编译器和处理器对指令进行重排序,但是重排序过程不会...
  • 并发编程并发程序要正确地执行,必须要保证其具备原子性、可见性以及有序性;只要有一个没有被保证,就有可能会导致程序运行不正确线程不安全在编译、测试甚至上线使用时,并不一定能发现,因为受到当时的CPU调度...
  • MySQL 面试题

    万次阅读 多人点赞 2019-09-02 16:03:33
    MySQL 面试题 MySQL 涉及的内容非常非常非常多,所以面试题也容易写的杂乱。当年,我们记着几个一定要掌握的重心: 重点的题目添加了【重点】前缀。 索引。 ...因为 MySQL 还会有部分内容和运维相关度比较高,所以...
  • 转载自:JAVA并发八(上):三大性质总结:原子性、可见性以及有序性 1. 三大性质简介 在并发编程中分析线程安全的问题时往往需要切入点,那就是**两大核心**:JMM抽象内存模型以及happens-before规则(,三条性质...
  • 原子性,有序性,可见性 synchronized 具有原子性,有序性,可见性; volatile 具有有序性,可见性 原子原子性是指在一个操作过程中,要不就全部执行成功,要不就全部执行失败。即使在多个线程一起执行的时候,...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 40,254
精华内容 16,101
关键字:

原子有序定义