Volatile、 JMM
JMM:java内存模型
我们都知道向mysql这种数据库数据是存储在磁盘文件中,即使有时建立索引,可能读取速度还是会慢,因为读取磁盘的文件会消耗大量的IO资源,而硬件的条件就决定了读取IO的速度,于是出现的内存数据库,内存的读取速度远远大于硬盘。
那什么是JMM java内存模型呢?
假如有个student对象,属性值age=25,现在将此对象写在主内存中,此时有三个线程会来修改age这个属性值,那么该如何操作呢?
线程t1,t2,t3同时将age拷贝一份放入到各自线程的工作内存中,假设线程t1将age=25修改为37之后,他需要将修改之后的值刷新至主内存,使得主内存中的值也为37,并且需要让线程t2 t3知道age的值已经修改了,这就是JMM的可见性。主内存中属性被修改,其他线程需要马上获取通知。
Volatile :可见性 、 不保证原子性 、禁止指令重排
Volatile 的可见性
package com.wei; import java.util.concurrent.TimeUnit; /** * 测试 可见性 */ public class Test{ public static void main(String[] args){ Student student = new Student(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t come in"); try { TimeUnit.SECONDS.sleep(3); student.addAgeTo(); System.out.println(Thread.currentThread().getName()+"\t update"+student.age); }catch (Exception e){ e.printStackTrace(); } },"AAA").start(); //创建一个线程AAA,睡眠3秒之后修改student的值为26 //多线程有一个很重要的问题就是每个线程都会有一份自己的变量拷贝,对该变量的修改不会影响到其他线程的变量,比如抢票 //所以就导致线程AAA即使对变量进行修改了,但mian线程的age还是为0,导致会一直循环 //这个是主线程main while (student.age==0){ } System.out.println(Thread.currentThread().getName()+"\t main"+student.age); } } class Student{ int age = 0; public void addAgeTo(){ this.age = 26; } }
打印结果:可以看到会一直卡在main线程中
加Volatile关键字:
package com.wei; import java.util.concurrent.TimeUnit; /** * 测试 可见性 */ public class Test{ public static void main(String[] args){ Student student = new Student(); new Thread(()->{ System.out.println(Thread.currentThread().getName()+"\t come in"); try { TimeUnit.SECONDS.sleep(3); student.addAgeTo(); System.out.println(Thread.currentThread().getName()+"\t update"+student.age); }catch (Exception e){ e.printStackTrace(); } },"AAA").start(); //创建一个线程AAA,睡眠3秒之后修改student的值为26 //多线程有一个很重要的问题就是每个线程都会有一份自己的变量拷贝,对该变量的修改不会影响到其他线程的变量,比如抢票 //所以就导致线程AAA即使对变量进行修改了,但mian线程的age还是为0,导致会一直循环 //这个是主线程main while (student.age==0){ } System.out.println(Thread.currentThread().getName()+"\t main"+student.age); } } class Student{ volatile int age = 0; public void addAgeTo(){ this.age = 26; } }
问题:变量前加volatile关键字就能保证线程安全吗?
并不能,还是可以根据上面那张图:
三个线程每次都对age=0自增加1,那么运行之后的最理想的age=3。
猜想一:多个线程同时修改一个属性,假如线程一进来在自己的线程工作空间age自增加1,age=1,此时正准备写道主内存中去,在这个过程未完成时,切换至线程2,线程2age还是0然后又加1,将值写入到主内存中去,此时切换至线程1,将1修改为1,是不是已经丢失了,最正确的应该为2。
猜想二:线程1已经将age=1更新至主内存中去了,但是此时主内存还没有叫其他线程更新值,然后cpu又切换至其他线程,是不是又造成值丢失。
也就是说虽然volatile能够保证可见性,但在多线程环境下,各个线程获取到的不一定就是最新值,也会造成线程不安全。