赞
踩
1.对volatile的理解
1.volatile是Java虚拟机提供的轻量级的同步机制(同步:synchronized)乞丐版的synchronized。
有三大特性:保证可见性
不保证原子性
禁止指令重排
多个线程,访问java主内存中的同一对象,获取对象之后,各自都拷贝到自己的线程内存中,当有一个线程中的对象改变时,需要写回给主内存中,主内存就会通知其他线程此对象改变了,需要重新拷贝的过程就叫可见性
未加volatile关键字 代码演示 package com.hzy; import java.util.concurrent.TimeUnit; class MyData{ int number = 0; public void addT060(){ this.number = 60; } } /** * 验证线程的可见性 * 1.假如int number = 0 ;number变量之前根本没有添加volatile关键字,没有可见性 */ public class VolatileDemo { public static void main(String[] args) { //资源类 MyData myData = new MyData(); //第一个线程 new Thread(() -> { System.out.println(Thread.currentThread().getName()+"\t come in "); //暂停一会线程 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //修改内存中数据为60 myData.addT060(); System.out.println(Thread.currentThread().getName()+"\t update number value : "+ myData.number); },"AAA").start(); //第二个线程 while (myData.number == 0){ //如果number == 0 mian线程就会在这里一直循环,知道number的值不在等于0 } //如果number中的数据不再等于0,就会执行到下面代码 System.out.println(Thread.currentThread().getName()+"\t mission is over"); } }
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7pvxfvLu-1614656364824)(C:\Users\HS\AppData\Roaming\Typora\typora-user-images\image-20210228125951266.png)]
加入volatile关键字 代码演示 package com.hzy; import java.util.concurrent.TimeUnit; class MyData{ volatile int number = 0; public void addT060(){ this.number = 60; } } /** * 验证线程的可见性 * 1.假如int number = 0 ;number变量之前根本没有添加volatile关键字,没有可见性 */ public class VolatileDemo { public static void main(String[] args) { //资源类 MyData myData = new MyData(); //第一个线程 new Thread(() -> { System.out.println(Thread.currentThread().getName()+"\t come in "); //暂停一会线程 try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //修改内存中数据为60 myData.addT060(); System.out.println(Thread.currentThread().getName()+"\t update number value : "+ myData.number); },"AAA").start(); //第二个线程 while (myData.number == 0){ //如果number == 0 mian线程就会在这里一直循环,知道number的值不在等于0 } //如果number中的数据不再等于0,就会执行到下面代码 System.out.println(Thread.currentThread().getName()+"\t mission is over,main get number value: " + myData.number); } }
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BaavQ7w6-1614656364827)(C:\Users\HS\AppData\Roaming\Typora\typora-user-images\image-20210228130305136.png)]
为什么没有保证原子性:
在写回主内存中时,多个线程写修改了线程内部变量值后,写回主内存,发生了重复写值现象,没有区分前后顺序,造成了原子性缺失
代码演示 package com.hzy; import java.util.concurrent.TimeUnit; class MyData1{ volatile int number = 0; public void addPlusPlus(){ number++; } } /** * 验证线程的不保证原子性 * 1.原子性:完整性,不可分割,即某个先后才能正在做某个具体业务时,中间不可加塞或者不可分割,需要整体完整 * 要么同时成功,要么同时失败 */ public class VolatileDemo2 { public static void main(String[] args) { MyData1 myData1 = new MyData1(); for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000; j++) { myData1.addPlusPlus(); } },String.valueOf(i)).start(); } //需要等待上面20个线程都全部计算完成后,再用main线程取得最终的结果值 while(Thread.activeCount() > 2){ //Thread.yield() 方法,使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行。 Thread.yield(); } //mian线程的最终值 System.out.println(Thread.currentThread().getName() + "\t finally number value :" + myData1.number); } }
如果保持了原子性,最终值应该是20000,实际每次得到的值都不相同
如果需要保证原子性,代码如下,在计算方法上加sychronized关键字
package com.hzy; import java.util.concurrent.TimeUnit; class MyData1{ volatile int number = 0; public synchronized void addPlusPlus(){ number++; } } /** * 验证线程的不保证原子性 * 1.原子性:完整性,不可分割,即某个先后才能正在做某个具体业务时,中间不可加塞或者不可分割,需要整体完整 * 要么同时成功,要么同时失败 */ public class VolatileDemo2 { public static void main(String[] args) { MyData1 myData1 = new MyData1(); for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000; j++) { myData1.addPlusPlus(); } },String.valueOf(i)).start(); } //需要等待上面20个线程都全部计算完成后,再用main线程取得最终的结果值 while(Thread.activeCount() > 2){ //Thread.yield() 方法,使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行。 Thread.yield(); } //mian线程的最终值 System.out.println(Thread.currentThread().getName() + "\t finally number value :" + myData1.number); } }
结果:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-toL58O0B-1614656364830)(C:\Users\HS\AppData\Roaming\Typora\typora-user-images\image-20210228132625648.png)]
1,在执行运算的方法中加syhronized关键字
2.使用JUC下的atomic,
代码示例: package com.hzy; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; class MyData1 { //AtomicInteger 默认值为 0 AtomicInteger atomicInteger = new AtomicInteger(); public void addAtomic(){ //源码中:加1 atomicInteger.getAndIncrement(); } } /** * 验证线程的不保证原子性 * 1.原子性:完整性,不可分割,即某个先后才能正在做某个具体业务时,中间不可加塞或者不可分割,需要整体完整 * 要么同时成功,要么同时失败 */ public class VolatileDemo2 { public static void main(String[] args) { MyData1 myData1 = new MyData1(); for (int i = 1; i <= 20; i++) { new Thread(()->{ for (int j = 1; j <= 1000; j++) { myData1.addAtomic(); } },String.valueOf(i)).start(); } //需要等待上面20个线程都全部计算完成后,再用main线程取得最终的结果值 while(Thread.activeCount() > 2){ //Thread.yield() 方法,使当前线程由执行状态,变成为就绪状态,让出cpu时间,在下一个线程执行时候,此线程有可能被执行,也有可能没有被执行。 Thread.yield(); } //mian线程的最终值 System.out.println(Thread.currentThread().getName() + "\t AtomicInteger finally number value :" + myData1.atomicInteger); } }
JMM(java内存模型)一种抽象的规范或规则,并不真正存在
可见性,原子性,有序性
在代码执行时,是编译为字节码指令来提供给进程来执行,在多线程环境下,线程抢到的执行顺序可能会有所不同,会造成数据混乱问题,这个时候数据的安全性机会收到威胁,所以就需要禁止指令重排。保证了多线程环境下不会出现乱序执行的现象
使用:在执行的变量前加volatile
禁止指令重排的好处如下,代码示例:
单线程-单例模式
package com.audition.volatiles; /** * Created with IntelliJ IDEA. * * @Auther: 两杯水 * @Date: 2021/02/28/17:15 * @Description: 单线程-单例模式 */ public class VolatileDemo3 { private static VolatileDemo3 instance= null; private VolatileDemo3(){ System.out.println(Thread.currentThread().getName() +"\t 我是构造方法 "); } public static VolatileDemo3 getInstance(){ if(instance == null){ instance = new VolatileDemo3(); } return instance; } public static void main(String[] args) { //main线程的操作动作 System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance()); System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance()); System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance()); } }
执行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eMHAx9mv-1614656364834)(C:\Users\86178\AppData\Roaming\Typora\typora-user-images\image-20210228172728840.png)]
进行多线程时,就会有问题,代码如下:
package com.audition.volatiles; /** * Created with IntelliJ IDEA. * * @Auther: 两杯水 * @Date: 2021/02/28/17:15 * @Description: 单线程-单例模式 */ public class VolatileDemo3 { private static VolatileDemo3 instance= null; private VolatileDemo3(){ System.out.println(Thread.currentThread().getName() +"\t 我是构造方法 "); } public static VolatileDemo3 getInstance(){ if(instance == null){ instance = new VolatileDemo3(); } return instance; } public static void main(String[] args) { /* //main线程的操作动作 System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance()); System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance()); System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());*/ //多线程 for (int i = 0; i <= 10; i++) { new Thread(()->{ VolatileDemo3.getInstance(); },String.valueOf(i)).start(); } } }
执行结果如下:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MhYAi8QI-1614656364836)(C:\Users\86178\AppData\Roaming\Typora\typora-user-images\image-20210228172815095.png)]
得到结果:多线程下的单例模式,由于多个线程执行的时间不同,导致了构造方法多次被创建实例,也就破坏了原来的单例思想
解决方法
1.在执行方法上加synchronized关键字:
package com.audition.volatiles; /** * Created with IntelliJ IDEA. * * @Auther: 两杯水 * @Date: 2021/02/28/17:15 * @Description: 单线程-单例模式 */ public class VolatileDemo3 { private static VolatileDemo3 instance= null; private VolatileDemo3(){ System.out.println(Thread.currentThread().getName() +"\t 我是构造方法 "); } public static synchronized VolatileDemo3 getInstance(){ if(instance == null){ instance = new VolatileDemo3(); } return instance; } public static void main(String[] args) { /* //main线程的操作动作 System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance()); System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance()); System.out.println(VolatileDemo3.getInstance() == VolatileDemo3.getInstance());*/ //多线程 for (int i = 0; i <= 10; i++) { new Thread(()->{ VolatileDemo3.getInstance(); },String.valueOf(i)).start(); } } }
但是synchronized属于重量级锁,此加锁方式会造成性能降低
2.使用DCL(Double check Lock 双端检索机制)

但是由于指令重排序的原因,在线程很多并发的情况下,此方法也会导致对象重复获取,所以需要对此对象加上volatile

Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。