并发 -- volatile

一、概念

Java内存模型:
Java内存模型规定所有的变量都是存在主内存,每个线程都有自己的工作内存。线程对变量的所有操作都必须在工作内存中进行,而不能直接对主内存进行操作,并且每个线程不能访问其他线程的工作内存。

内存模型

线程中的三个概念:
1.原子性
在Java中,对基本数据类型的变量的读取和赋值操作是原子性操作,即这些操作是不可被中断的,要么执行,要么不执行。

x = 10;        //语句1,原子性操作
y = x;         //语句2,非原子性操作,包含两个操作:读取x值、给y赋值
x++;           //语句3,非原子性操作,包含三个操作:读取x值、加1、给x赋值
x = x + 1;     //语句4,非原子性操作,包含三个操作:读取x值、加1、给x赋值

Java内存模型只保证了基本读取和赋值是原子性操作,如果要实现更大范围操作的原子性,可以通过synchronized和Lock来实现。synchronized和Lock能够保证同一时刻只有一个线程获取锁然后执行同步代码,那么自然就不存在原子性问题了,从而保证了原子性。

2.可见性
Java提供了volatile关键字来保证可见性,当一个共享变量被volatile修饰时,它会保证修改的值会立即被更新到主内存,当有其他线程需要读取时,它会去主内存读取新值。而普通的共享变量不能保证可见性,因为普通共享变量被修改之后,什么时候被写入主存是不确定的,当其他线程去读取时,此时内存中可能还是原来的旧值,因此无法保证可见性。

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

3.有序性
在Java内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,但是会影响到多线程并发执行的正确性。可以通过volatile关键字来保证一定的"有序性"。

volatile关键字禁止指令重排序有两层意思:

  • 当程序执行到volatile变量的读操作或者写操作时,在其前面的操作的更改肯定全部已经进行,且结果已经对后面的操作可见,在其后面的操作肯定还没有进行。
  • 在进行指令优化时,不能将在对volatile变量访问的语句放在其后面执行,也不能把volatile变量后面的语句放到其前面执行。

例子:

//x、y为非volatile变量
//flag为volatile变量

x = 2;        //语句1
y = 0;        //语句2
flag = true;  //语句3
x = 4;        //语句4
y = -1;       //语句5

由于flag变量为volatile变量,那么在进行指令重排序的过程的时候,不会将语句3放到语句1、语句2前面,也不会讲语句3放到语句4、语句5后面。但是要注意语句1和语句2的顺序、语句4和语句5的顺序是不作任何保证的。并且volatile关键字能保证执行到语句3时,语句1和语句2必定是执行完毕了的,且语句1和语句2的执行结果对语句3、语句4、语句5是可见的。

另外,也可以通过synchronized和Lock来保证有序性。synchronized和Lock能保证同一时刻只有一个线程获取锁然后执行同步代码,相当于是让线程顺序执行同步代码,自然就保证了有序性。

二、使用

使用volatile关键字增加了成员变量在多个线程之间的可见性,但是volatile关键字最致命的缺点是不支持原子性。

private static volatile boolean flag = false; //成员变量flag在多个线程之间具有可见性

三、volatile与synchronized的比较

  • volatile解决的是变量在多个线程之间的可见性,而synchronized解决的是多个线程之间访问资源的同步性。
  • volatile能保证数据的可见性,但不能保证原子性,而synchronized可以保证原子性,也可以间接保证可见性,因为它将主内存和工作内存中的数据做同步。
  • 多线程访问volatile不会发生阻塞,而synchronized会出现阻塞。
  • volatile是线程同步的轻量级实现,只能修饰于变量,而synchronized可以修饰方法以及代码块。

四、例子

volatile不支持原子性:

static class MyThread extends Thread {
    private static volatile int count = 0; //保证可见性,但是不支持原子性

    private MyThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        Log.d(TAG, "zwm, thread: " + getName() + " run start");
        for(int i=0; i<10000; i++) {
            count++; //非原子性操作,包含三个操作:读取x值、加1、给x赋值
        }
        Log.d(TAG, "zwm, count: " + count);
        Log.d(TAG, "zwm, thread: " + getName() + " run end");
    }
}

//测试代码
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        Log.d(TAG, "zwm, count: " + MyThread.count);
    }
}, 5000);

//输出
2019-02-11 17:11:04.063 zwm, thread: thread1 run start
2019-02-11 17:11:04.070 zwm, thread: thread2 run start
2019-02-11 17:11:04.073 zwm, count: 10397
2019-02-11 17:11:04.073 zwm, thread: thread2 run end
2019-02-11 17:11:04.094 zwm, thread: thread3 run start
2019-02-11 17:11:04.096 zwm, count: 25119
2019-02-11 17:11:04.096 zwm, thread: thread1 run end
2019-02-11 17:11:04.097 zwm, count: 28435
2019-02-11 17:11:04.097 zwm, thread: thread3 run end
2019-02-11 17:11:09.069 zwm, count: 28435 //最终结果不为30000!

使用synchronized保证原子性:

static class MyThread extends Thread {
    private static int count = 0;

    private MyThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        Log.d(TAG, "zwm, thread: " + getName() + " run start");
        for(int i=0; i<10000; i++) {
            synchronized (MyThread.class) {
                count++;
            }
        }
        Log.d(TAG, "zwm, count: " + count);
        Log.d(TAG, "zwm, thread: " + getName() + " run end");
    }
}

//测试代码 
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        Log.d(TAG, "zwm, count: " + MyThread.count);
    }
}, 5000);

//输出 
2019-02-11 17:22:35.842 zwm, thread: thread1 run start
2019-02-11 17:22:35.842 zwm, thread: thread2 run start
2019-02-11 17:22:35.844 zwm, thread: thread3 run start
2019-02-11 17:22:35.858 zwm, count: 26193
2019-02-11 17:22:35.859 zwm, thread: thread1 run end
2019-02-11 17:22:35.860 zwm, count: 29166
2019-02-11 17:22:35.860 zwm, thread: thread2 run end
2019-02-11 17:22:35.860 zwm, count: 30000
2019-02-11 17:22:35.861 zwm, thread: thread3 run end
2019-02-11 17:22:40.851 zwm, count: 30000

使用Lock保证原子性:

static class MyThread extends Thread {
    private static int count = 0;
    private static Lock lock = new ReentrantLock();

    private MyThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        Log.d(TAG, "zwm, thread: " + getName() + " run start");
        for(int i=0; i<10000; i++) {
            lock.lock();
            try {
                count++;
            } finally {
                lock.unlock();
            }
        }
        Log.d(TAG, "zwm, count: " + count);
        Log.d(TAG, "zwm, thread: " + getName() + " run end");
    }
}

//测试代码
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        Log.d(TAG, "zwm, count: " + MyThread.count);
    }
}, 5000);

//输出
2019-02-11 17:27:03.621 zwm, thread: thread3 run start
2019-02-11 17:27:03.623 zwm, thread: thread1 run start
2019-02-11 17:27:03.626 zwm, thread: thread2 run start
2019-02-11 17:27:03.711 zwm, count: 18295
2019-02-11 17:27:03.711 zwm, thread: thread3 run end
2019-02-11 17:27:03.725 zwm, count: 25110
2019-02-11 17:27:03.725 zwm, thread: thread1 run end
2019-02-11 17:27:03.726 zwm, count: 30000
2019-02-11 17:27:03.726 zwm, thread: thread2 run end
2019-02-11 17:27:08.621 zwm, count: 30000

使用AtomicInteger保证原子性:

static class MyThread extends Thread {
    private static AtomicInteger count = new AtomicInteger();

    private MyThread(String name) {
        setName(name);
    }

    @Override
    public void run() {
        Log.d(TAG, "zwm, thread: " + getName() + " run start");
        for(int i=0; i<10000; i++) {
            count.getAndIncrement();
        }
        Log.d(TAG, "zwm, count: " + count);
        Log.d(TAG, "zwm, thread: " + getName() + " run end");
    }
}

//测试代码 
MyThread thread1 = new MyThread("thread1");
thread1.start();
MyThread thread2 = new MyThread("thread2");
thread2.start();
MyThread thread3 = new MyThread("thread3");
thread3.start();

new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        Log.d(TAG, "zwm, count: " + MyThread.count);
    }
}, 5000);

//输出
2019-02-11 17:32:03.508 zwm, thread: thread1 run start
2019-02-11 17:32:03.509 zwm, thread: thread2 run start
2019-02-11 17:32:03.509 zwm, thread: thread3 run start
2019-02-11 17:32:03.516 zwm, count: 27025
2019-02-11 17:32:03.516 zwm, thread: thread2 run end
2019-02-11 17:32:03.516 zwm, count: 27579
2019-02-11 17:32:03.516 zwm, thread: thread3 run end
2019-02-11 17:32:03.517 zwm, count: 30000
2019-02-11 17:32:03.517 zwm, thread: thread1 run end
2019-02-11 17:32:08.515 zwm, count: 30000

你可能感兴趣的:(并发 -- volatile)