请你谈谈对volatile关键字的理解
volatile是Java虚拟机的轻量级的同步机制,具有三个特性:
- 保证可见性
- 不保证原子性
- 禁止指令重排
1.可见性:当一个线程修改了主线程的值,其他的线程可以立即通知其他线程值被修改了
2.原子性:一个线程正在做某个操作时,中途不能被加塞/分割,需要整体执行的完整,要么同时成功,要么同时失败
3.指令重排:处理器在进行重排序之前要考虑最终的执行结果和代码顺序结果的一致性,进行指令重排的操作,必须要
考虑数据的依赖性.由于在多线程的环境中,数据交互执行,由于编译器指令优化重排序的存在,多个线程中使用的数据
的一致性是无法确定的,而volatile通过插入内存屏障,禁止内存屏障前后的指令执行进行重排序优化.
可见性代码:
1 class MyData{ 2 3 int number = 0; //没有加入volatile关键字 4 5 public void change(){ 6 this.number = 60; 7 } 8 } 9 10 public static void seeValueByVolatile(){ 11 MyData myData = new MyData(); 12 new Thread(() -> { 13 System.out.println(Thread.currentThread().getName() + " "+ myData.number); 14 15 try { 16 TimeUnit.SECONDS.sleep(3); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } 20 myData.change(); 21 System.out.println(Thread.currentThread().getName() + " "+ myData.number); 22 },"可见性测试").start();
两个线程的数据并不同步
class MyData{ volatile int number = 0; //添加volatile关键字 public void change(){ this.number = 60; } } public static void seeValueByVolatile(){ MyData myData = new MyData(); new Thread(() -> { System.out.println(Thread.currentThread().getName() + " "+ myData.number); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } myData.change(); System.out.println(Thread.currentThread().getName() + " "+ myData.number); },"可见性测试").start(); while (myData.number == 0){ } System.out.println(Thread.currentThread().getName() + " " + myData.number); }
两个线程的数据同步一致
原子性代码:
开启20个线程执行number++1000次,最终的结果应该是20000
class MyData{ volatile int number = 0; public void change(){ this.number = 60; } //number++ 被拆分为3个指令,执行getfield获取原始的值,执行iadd进行加1操作,执行putfield写把累加后的值写回到主内存 public void addPlus(){ number++; } } public static void atomicByVolatile(){ MyData myData = new MyData(); for (int i = 1; i <= 20; i++) { new Thread(() -> { for (int j = 1; j <= 1000; j++) { myData.addPlus();//出现了写丢失的情况 } },String.valueOf(i)).start(); } while (Thread.activeCount() > 2){ Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + myData.number); System.out.println(Thread.currentThread().getName() + "Atomic Number:" + " " + myData.atomicInteger); }
但由于线程争抢,写丢失的情况存在,导致数值的丢失
解决方法:使用原子引用类
class MyData{ volatile int number = 0; public void change(){ this.number = 60; } //number++ 被拆分为3个指令,执行getfield获取原始的值,执行iadd进行加1操作,执行putfield写把累加后的值写回到主内存 public void addPlus(){ number++; } AtomicInteger atomicInteger = new AtomicInteger(); //不使用sync的方式来实现原子性,使用JUC下的AtomicInteger public void addPlusAtomic(){ atomicInteger.getAndIncrement(); } } public static void atomicByVolatile(){ MyData myData = new MyData(); for (int i = 1; i <= 20; i++) { new Thread(() -> { for (int j = 1; j <= 1000; j++) { //myData.addPlus();//出现了写丢失的情况 myData.addPlusAtomic(); } },String.valueOf(i)).start(); } while (Thread.activeCount() > 2){ Thread.yield(); } System.out.println(Thread.currentThread().getName() + " " + myData.number); System.out.println(Thread.currentThread().getName() + "Atomic Number:" + " " + myData.atomicInteger); }
结果正确
案例完整代码:
1 import java.util.concurrent.TimeUnit; 2 import java.util.concurrent.atomic.AtomicInteger; 3 4 public class VolatileDemo{ 5 public static void main(String[] args) { 6 7 seeValueByVolatile(); 8 atomicByVolatile(); 9 10 } 11 12 public static void atomicByVolatile(){ 13 MyData myData = new MyData(); 14 15 for (int i = 1; i <= 20; i++) { 16 new Thread(() -> { 17 for (int j = 1; j <= 1000; j++) { 18 myData.addPlus();//出现了写丢失的情况 19 myData.addPlusAtomic(); 20 } 21 },String.valueOf(i)).start(); 22 } 23 24 while (Thread.activeCount() > 2){ 25 Thread.yield(); 26 } 27 System.out.println(Thread.currentThread().getName() + " " + myData.number); 28 System.out.println(Thread.currentThread().getName() + "Atomic Number:" + " " + myData.atomicInteger); 29 } 30 31 public static void seeValueByVolatile(){ 32 MyData myData = new MyData(); 33 new Thread(() -> { 34 System.out.println(Thread.currentThread().getName() + " "+ myData.number); 35 36 try { 37 TimeUnit.SECONDS.sleep(3); 38 } catch (InterruptedException e) { 39 e.printStackTrace(); 40 } 41 myData.change(); 42 System.out.println(Thread.currentThread().getName() + " "+ myData.number); 43 },"可见性测试").start(); 44 45 46 47 while (myData.number == 0){ 48 49 } 50 System.out.println(Thread.currentThread().getName() + " " + myData.number); 51 } 52 } 53 54 55 class MyData{ 56 57 volatile int number = 0; 58 59 public void change(){ 60 this.number = 60; 61 } 62 63 //number++ 被拆分为3个指令,执行getfield获取原始的值,执行iadd进行加1操作,执行putfield写把累加后的值写回到主内存 64 public void addPlus(){ 65 number++; 66 } 67 68 AtomicInteger atomicInteger = new AtomicInteger(); 69 //不使用sync的方式来实现原子性,使用JUC下的AtomicInteger 70 public void addPlusAtomic(){ 71 atomicInteger.getAndIncrement(); 72 } 73 }