java线程volatile_Java:多线程中的volatile

本文通过示例介绍了Java中volatile关键字的作用,解释了为何使用volatile来解决线程间通信问题,同时指出volatile不具备原子性,因此在并发场景下可能会导致非线程安全问题。文中给出了两种解决非原子性问题的方法:使用synchronized关键字或原子类AtomicInteger。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、为什么使用volatile

首先,通过一段简单的代码来理解为什么要使用volatile:

1 public class RunThread extendsThread{2 private boolean isRunning = true;3 public booleanisRunning(){4 returnisRunning;5 }6

7 public void setRunning(booleanisRunning){8 this.isRunning =isRunning;9 }10

11 @Override12 public voidrun() {13 System.out.println("进入run...");14 while(isRunning==true){}15 System.out.println("线程停止了");16 }17 }18

19

20

21 importjava.util.concurrent.TimeUnit;22

23 public classRun {24 public static voidmain(String[] args){25 try{26 RunThread thread = newRunThread();27 thread.start();28 TimeUnit.MILLISECONDS.sleep(1000);29 thread.setRunning(false);30 System.out.println("赋值为false");31 } catch(InterruptedException e) {32 e.printStackTrace();33 }34 }35 }

执行结果:

我们创建了一个RunThread对象,并启动了该线程。当 isRunning == True时,将一直进行while循环。可以看到,我们启动了线程之后就调用setRunning方法将isRunning设为false。

按道理来说,当isRunning的状态为false时,就会退出while循环,输出“赋值为false”。然后run方法执行结束,线程执行完毕,整个程序运行结束。

但通过执行结果发现,线程进入了while死循环,线程从未终止。

583ea29713995aa986889751fab7888c.png

这是由于在启动RunThread.java线程时,变量 private boolean isRunning = True 存在于公共堆栈和线程的私有堆栈中。JVM以server模式运行时,为了提高运行效率,线程一直从私有堆栈中读取isRunning的值为True。而thread.setRunning(false)执行后,是把公共堆栈中的isRunning改为false。所以即便将isRunning改为false,程序依然进入了while死循环。

解决办法:

使用volatile关键字,把 private boolean isRunning = True 改为 volatile private boolean isRunning = True ,再次运行:

70a225f09e4eac9c4e7641492958de20.png

volatile的作用是:强制线程从公共堆栈中取变量的值,而非从线程的私有堆栈中取值

ac3720bc4b0a28388889a6d801914a00.png

0a7404d9749ad6ad15a5242819268b24.png

二、 volatile的非原子性

1. 虽然volatile关键字增加了实例变量在多个线程之间的可见性,但它不具备同步性,也就是原子性。

1 public class MyThread extendsThread{2 volatile public static intcount;3 private static voidaddCount(){4 for(int i =0;i<100;i++){5 count++;6 }7 System.out.println("count= "+count);8 }9

10 @Override11 public voidrun() {12 addCount();13 }14 }15

16 public classRun {17 public static voidmain(String[] args) {18 MyThread[] mythreadArray = new MyThread[100];19 for (int i = 0; i < 100; i++) {20 mythreadArray[i] = newMyThread();21 }22 for (int i = 0; i < 100; i++) {23 mythreadArray[i].start();24 }25 }26 }

执行结果:

8ba429255183d7f97eeebb57f5cca131.png

volatile保证了线程每次从公共内存中读取变量,保证了同步数据的可见性。但由上述代码可以看出,使用关键字volatile时出现了非线程安全问题。i++ 操作并非一个原子操作,可以分解为:

(1)从内存读取 i 的值;

(2)计算 i 的值;

(3)将 i 的值写入内存

当一个线程正在执行步骤(2)时,另一个线程也可能也在修改 i 值,就会出现脏数据。

2. 解决办法

(1)方法一:使用synchronized关键字

当在 private static void addCount() 前面加上关键字synchronized后 synchronized private static void addCount() ,再次执行:

a1f55d4113348d1ae8aa7188e3dc7123.png

当时用了synchronized关键字时,就不必再使用volatile关键字了。

(2)方法二:使用原子类进行i++操作

1 public class MyThread extendsThread{2 private static AtomicInteger count = new AtomicInteger(0);3 synchronized private static voidaddCount(){4 for(int i =0;i<100;i++){5 count.incrementAndGet();6 }7 System.out.println("count= "+count);8 }9

10 @Override11 public voidrun() {12 addCount();13 }14 }

注意:原子类并不完全安全,只有原子类的increamentAndGet( )方法是原子的,不保证方法与方法之间的调用也是原子的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值