探索flutter框架开发的app在移动应用市场的潜力与挑战
679
2022-11-06
Volatile使用详解
1.概述
volatile是Java提供的轻量级的同步机制,保证了可见性,不保证原子性。了解volatile工作机制,首先要对Java内存模型(JMM)有初步的认识:
每个线程创建时,JVM会为其创建一份私有的工作内存(栈空间),不同线程的工作内存之间不能直接互相访问。JMM规定所有的变量都存在主内存,主内存是共享内存区域,所有线程都可以访问线程对变量进行读写,会从主内存拷贝一份副本到自己的工作内存,操作完毕后刷新到主内存。所以,线程间的通信要通过主内存来实现。
volatile的作用是:线程对副本变量进行修改后,其他线程能够立刻同步刷新最新的数值。这个就是可见性。
2.可见性验证
我们来看一个例子:
package com.bruceliu.demo15;import java.util.concurrent.TimeUnit;/** * @BelongsProject: Thread0509 * @BelongsPackage: com.bruceliu.demo15 * @Author: bruceliu * @ * @CreateTime: 2020-05-13 23:16 * @Description: TODO */public class VolatileDemo { int x = 0; //注意:这里的b没有被volatile修饰 boolean b = false; /** * 写操作 */ private void write() { x = 5; b = true; System.out.println("x=>" + x); System.out.println("b =>" + b); } /** * 读操作 */ private void read() { //如果b=false的话,就会无限循环,直到b=true才会执行结束,会打印出x的值 while (!b) { } System.out.println("x=" + x); } public static void main(String[] args) throws Exception { final VolatileDemo volatileDemo = new VolatileDemo(); //线程1执行写操作 Thread thread1 = new Thread(new Runnable() { public void run() { volatileDemo.write(); } }); //线程2执行读操作 Thread thread2 = new Thread(new Runnable() { public void run() { volatileDemo.read(); } }); //我们让线程2的读操作先执行 thread2.start(); //睡1毫秒,为了保证线程2比线程1先执行 TimeUnit.MILLISECONDS.sleep(1); //再让线程1的写操作执行 thread1.start(); thread1.join(); thread2.join(); //等待线程1和线程2全部结束后,打印执行结束 System.out.println("执行结束"); }}
运行之后会一直出于运行状态,并且没有打印“执行结束”
给b加了volatile关键字修饰后,线程1对b做了修改,然后会立即更新内存中的值,线程2通过嗅探发现自己的副本已经过期了,然后重新从内存中拿到b=true的值,然后跳出while循环,执行结束!
我们知道volatile关键字的作用是保证变量在多线程之间的可见性,它是java.util.concurrent包的核心,没有volatile就没有这么多的并发类给我们使用.
3.原子性验证
看下面一段代码,number变量加了volatile修饰。创建了10个子线程,每个线程循环1000次执行number++。
package com.bruce.demo8;import java.util.concurrent.TimeUnit;/** * @BelongsProject: SingleTon-2020 * @BelongsPackage: com.bruce.demo8 * @CreateTime: 2020-12-10 23:08 * @Description: TODO */public class Demo8 { static class MyTest { public volatile int number = 0; public void incr(){ number++; } } public static void main(String[] args) { MyTest myTest = new MyTest(); for (int i = 1; i <= 10; i++){ new Thread(() -> { for (int j = 1; j <= 1000; j++){ myTest.incr(); } }, "Thread"+String.valueOf(i)).start(); } try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //等线程执行结束了,输出number值 System.out.println("当前number:" + myTest.number); }}
4.原子性问题解决
方法一:使用 synchronized 关键字
//给函数增加synchronized修饰,相当于加锁了 public synchronized void incr(){ number++; }
public class Demo8 { static class MyTest { public volatile AtomicInteger number = new AtomicInteger(); public void incr(){ number.getAndIncrement(); } } public static void main(String[] args) { MyTest myTest = new MyTest(); for (int i = 1; i <= 10; i++){ new Thread(() -> { for (int j = 1; j <= 1000; j++){ myTest.incr(); } }, "Thread"+String.valueOf(i)).start(); } try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } //等线程执行结束了,输出number值 System.out.println("当前number:" + myTest.number); }}
版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~