多线程中的可见性问题

1.缓存导致的可见性问题

可见性问题是指一个线程修改了某一个共享变量的值时,其他线程是否能够立即知道这个修改。

  • 对于串行程序来说,可见性问题是不存在的,因为你在任何一个操作步骤中修改了某个变量,在后续的步骤中读取这个变量的值时,读取的一定是修改后的新值。
  • 在并行程序中,如果一个线程修改了某一个全局变量,那么其他线程未必可以马上知道这个改动。多核时代,每颗 CPU 都有自己的缓存,这时 CPU 缓存与内存的数据一致性就没那么容易解决了,一个CPU缓存中的变量对另外一个CPU是不可见的
    多线程中的可见性问题_第1张图片
    如上图中,两个线程在执行时,CPU1和CPU2分别将内存中的变量,缓存到CPU的cache或者寄存器中,如果CPU1的线程修改了变量V,那么CPU2的线程可能无法意识到这个改动,依然会读取CPU2中cache或者寄存器中的值,这就产生了可见性问题。

2.解决办法

1.使用volatile关键字
volatile 是禁用CPU缓存的意思,变量volatile int x = 0,它表达的是:告诉编译器,对这个变量的读写,不能使用 CPU 缓存,必须从内存中读取或者写入。

2.使用synchronized加锁
JMM(java内存模型)关于synchronized的两条规定:

  • 线程解锁前(退出synchronized代码块之前),必须把共享变量的最新值刷新到主内存中,也就是说线程退出synchronized代码块值后,主内存中保存的共享变量的值已经是最新的了
  • 线程加锁时(进入synchronized代码块之后),将清空工作内存中共享变量的值,从而使用共享变量时需要从主内存中重新读取最新的值(注意:加锁与解锁需要是同一把锁)
  • 两者结合:线程解锁前对共享变量的修改在下次加锁时对其他线程可见

3.synchronizedvolatile的比较

1.volatile不需要加锁,比synchronized更轻量级,不会阻塞线程
2.从内存可见性角度讲,volatile读操作=进入synchronized代码块(加锁),volatile写操作=退出synchronized代码块(解锁)
3.synchronized既能保证可见性,又能保证原子性,而volatile只能保证可见性,不能保证原子性

你可能感兴趣的:(多线程,java,并发编程,缓存)