Java多线程之内存可见性
课程地址:https://www.imooc.com/learn/352
源码地址(github): https://github.com/Lining128/neicunkejianxing
内存可见性
可见性:一个线程对共享变量值的修改,能够及时地被其他线程看到。
共享变量:如果-一个变量在多个线程的工作内存中都存在副本,那么这个变量就是这几个线程的共享变量
两条规定
线程对共享变量的所有操作都必须在自己的工作内存中进行,不能直接从主内存中读写
不同线程之间无法直接访问其他线程工作内存中的变量,线程间变量值的传递需要通过主内存来完成。
共享变量可见性实现的原理
线程1对共享变量的修改要想被线程2及时看到,必须要经过如下2个步骤:
把工作内存1中更新过的共享变量刷新到主内存中
将主内存中最新的共享变量的值更新到工作内存2中
要实现共享变量的可见性,必须保证两点:
1.线程修改后的共享变量值能够及时从工作内存刷新到主内存中
2.其他线程能够及时把共享变量的最新值从主内存更新到自己的工作内存中
JMM关于synchronized的两条规定:
1.线程解锁前,必须把共享变量的最新值刷新到主内存中
2.线程加锁时,将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)
*线程解锁前对共享变量的修改在下次加锁时对其他线程可见
重排序
重排序
代码书写的顺序与实际执行的顺序不同,指令重排序是编译器或处理器为了提高程序性能而做的优化
1.编译器优化的重排序(编译器优化)
2.指令级并行重排序(处理器优化)
3.内存系统的重排序(处理器优化)
package com.demo.syn;
/**
* @author Lining
*/
public class SyncharonizedDemo {
private boolean ready = false;
private int result = 0;
private int number = 1;
//写操作
public void write() {
ready = true;
number = 2;
}
public void read() {
if (ready) {
result = number * 3;
}
System.out.println("result的值为:" + result);//内部注程类
}
public class ReadWriteThread extends Thread {
private boolean flag;
public ReadWriteThread(boolean flag) {
this.flag = flag;
}
@Override
public void run() {
if (flag) {
write();
}else{
read();
}
}
}
public static void main (String[] args ){
SyncharonizedDemo synDemo = new SyncharonizedDemo();
synDemo.new ReadWriteThread(true).start();
synDemo.new ReadWriteThread(false).start();
}
}
运行结果:
volatie实现可见性
package com.demo.syn;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author Lining
*/
public class VolatileDemo {
private Lock lock = new ReentrantLock();
private volatile int number = 0;
public int getNumber(){
return this.number;
}
public synchronized void intcrease(){
try{
Thread.sleep(100);
}catch (InterruptedException e){
e.printStackTrace();
}
lock.lock();
try{
this.number++;
}finally {
lock.unlock();
}
}
public static void main(String[] args) {
VolatileDemo volatileDemo = new VolatileDemo();
for(int i= 0; i<500; i++){
new Thread(new Runnable() {
@Override
public void run() {
volatileDemo.intcrease();
}
}).start();
}
while (Thread.activeCount()>1){
Thread.yield();
}
System.out.println("number"+volatileDemo.getNumber());
}
}
volatile如何实现内存可见性:
1.通过加入内存屏障和禁止重排序优化来实现的。
2.对volatile变量执行写操作时,会在写操作后加入-条store屏障指令
3.对volatile变量执行读操作时,会在读操作前加入- -条load屏障指令
线程写volatile变量的过程:
1.改变线程工作内存中volatile变量副本的值
2.将改变后的副本的值从工作内存刷新到主内存
volatile适用场合
要在多线程中安全的使用volatile变量,必须同时满足:
对变量的写入操作不依赖其当前值
1.不满足: number++、count = count*5等
2.满足: boolean变量、记录温度变化的变量等2.该变量没有包含在具有其他变量的不变式中不满足:不变式low<up
synchronized和volatile比较
1.volatile不需要加锁,比ksynchronized更轻量级,不会阻塞线程;
2.从内存可见性角度将, volatile读相当于加锁,volatile写相当于解锁。
3.synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,无法保证原子性。