Java并发编程

并发编程的三个问题:原子性问题,可见性问题,有序性问题

原子性

概念简介

  1. 一个操作或者多个操作,要么全部执行,要么就都不执行
  2. 执行的过程不会被任何因素打断
  3. 在并发中要求互斥访问

只有简单的读取、赋值(而且必须是将数字赋值给某个变量,变量之间的相互赋值不是原子操作)才是原子操作。Java内存模型只保证了基本读取和赋值是原子性操作。

Java语言提供的保证:

通过synchronized和Lock来实现。由于synchronized和Lock能够保证任一时刻只有一个线程执行该代码块,那么自然就不存在原子性问题了,从而保证了原子性。

可见性

当多个线程访问同一个变量时,一个线程修改了这个变量的值,其他线程能够立即看得到修改的值。

普通的共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值。

Java语言提供的保证

  1. volatile关键字来保证可见性。当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主存,当有其他线程需要读取时,它会去内存中读取新值。

  2. 通过synchronized和Lock也能够保证可见性,synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,并且在释放锁之前会将对变量的修改刷新到主存当中。因此可以保证可见性。

有序性

程序执行的顺序按照代码的先后顺序执行。

指令重排序(Instruction Reorder)
处理器为了提高程序运行效率,可能会对输入代码进行优化,它不保证程序中各个语句的执行先后顺序同代码中的顺序一致,但是它会保证程序最终执行结果和代码顺序执行的结果是一致的。

处理器在进行重排序时是会考虑指令之间的数据依赖性。重排序会影响多个线程内程序执行的结果。换句话说,重排不会影响单个线程内程序执行的结果。

结论:指令重排的适用范围是线程,它不会影响单个线程的执行,但是会影响到线程并发执行的正确性。

举个参考资料里的例子:
我相信,有经验的程序员,即使不懂的指令重排,本能上也不会用两个线程异步的执行下诉初始化操作。

//线程1:
context = loadContext();   //语句1
inited = true;             //语句2

//线程2:
while(!inited ){
    sleep()
}
doSomethingWithConfig(context);

计划执行:在线程1里执行语句1,初始化加载操作,完成后,标志位inited置为true,线程2,跳出睡眠,执行doSomethingWithConfig方法。

实际执行:由于语句1和语句2没有数据依赖性,因此可能会被重排序。假如发生了重排序,在线程1执行过程中先执行语句2,而此是线程2会以为初始化工作已经完成,那么就会跳出while循环,去执行doSomethingwithconfig(context)方法,而此时context并没有被初始化,就会导致程序出错。

Java语言提供的保证:

通过synchronized和Lock来保证有序性,很显然,synchronized和Lock保证每个时刻是有一个线程执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性

volatile关键字

一个共享变量(类的成员变量、类的静态成员变量)被volatile修饰之后,那么就具备了两层语义:

1.保证了不同线程对这个变量进行操作时的可见性,即一个线程修改了某个变量的值,这新值对其他线程来说是立即可见的。

2.禁止进行指令重排序。

  1. 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见;在其后面的操作肯定还没有进行;

  2. 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

volatile关键字的使用场景

volatile关键字是无法替代synchronized关键字的,因为volatile关键字无法保证操作的原子性。因此,当保证操作是原子性操作,才能保证使用volatile关键字的程序在并发时能够正确执行。

  1. 状态标记量
  2. double check。

单例的最优写法:

class Singleton{

    private volatile static Singleton instance = null;

    private Singleton() {
     
    }

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

参考资料

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容