voliatle关键字
1.volatile关键字使用:
多线程中存在私有堆栈中的值和公共堆栈中的值不同步的问题。什么意思呢?可能线程在一个地方修改了内存中变量的值,而其它地方线程却从私有堆栈中去读取不一致的变量值。关键字volatile 的主要作用是使在多个线程上可见。也就是,强制从公共堆栈中取得变量的值,而不是从线程私有数据栈中取得变量的值。
- 线程的私有堆栈:
- 读取公共内存:
强制从私有堆栈中取值的方法为JVM被设置为-server(不设置-server也是从私有堆栈中获取值)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 | package chapter2; public class VolatileTest { static class RunThread extends Thread{ private boolean isRunning = true ; public boolean isRunning() { return isRunning; } public void setRunning( boolean isRunning) { this .isRunning = isRunning; } @Override public void run() { super .run(); System.out.println( "进入run 了" ); while (isRunning) { } System.out.println( "线程被停止了!" ); } } public static void main(String[] args) { try { RunThread runThread = new RunThread(); runThread.start(); Thread.sleep( 1000 ); runThread.setRunning( false ); System.out.println( "已经赋值为false" ); } catch (Exception e) { e.printStackTrace(); } } } |
运行结果:
进入run 了
已经赋值为false
永远不会打印线程被停止了!
volatile private boolean isRunning = true;//强制从公共堆栈中取得变量的值
运行结果:
进入run 了
已经赋值为false
线程被停止了!
非原子性验证:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | package chapter2; public class VolatileThread extends Thread{ volatile public static int count; private static void addCount() { for ( int i= 0 ;i< 100 ;i++) { count++; } System.out.println( "Thread:" +currentThread().getName()+ "--count:" +count); } @Override public void run() { super .run(); addCount(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | package chapter2; public class VolatileTest { public static void main(String[] args) { VolatileThread[] volatileThreads = new VolatileThread[ 100 ]; for ( int i = 0 ; i < 100 ; i++) { volatileThreads[i] = new VolatileThread(); } for ( int i= 0 ;i< 100 ;i++) { volatileThreads[i].start(); } } } |
运行结果:
Thread:Thread-84--count:8683
Thread:Thread-88--count:8863
Thread:Thread-89--count:8863
Thread:Thread-87--count:8963
Thread:Thread-90--count:9063
Thread:Thread-92--count:9163
Thread:Thread-97--count:9663
Thread:Thread-96--count:9563
Thread:Thread-98--count:9763
Thread:Thread-95--count:9863
Thread:Thread-93--count:9463
Thread:Thread-94--count:9363
Thread:Thread-91--count:9263
Thread:Thread-99--count:9963
非原子性的原因:
- read 和 load 阶段:从主存复制变量到当前线程工作内存。
- use 和 assign 阶段:执行代码,改变共享变量值。
- store 和 write 阶段:用工作内存数据刷新主存对应变量的值。
在多线程环境中,use和assign 是多次出现的,但这一操作并不是原子性,也就是说在read和load之后,如果主内存count变量发生修改之后,线程工作内存中的值由于已经加载,不会产生对应的变化,也就是私有内存和公有内存的变量不同步,所以计算出来的结果和预期不一样,也就出现了非线程安全的问题。
2.使用原子类进行i++操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | package chapter2; import java.util.concurrent.atomic.AtomicInteger; public class VolatileThread extends Thread{ private AtomicInteger count = new AtomicInteger( 0 ); private void addCount() { for ( int i= 0 ;i< 10000 ;i++) { System.out.println(count.incrementAndGet()); } } @Override public void run() { super .run(); addCount(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | package chapter2; public class VolatileTest { public static void main(String[] args) { try { VolatileThread volatileThread = new VolatileThread(); Thread t1 = new Thread(volatileThread); t1.start(); Thread t2 = new Thread(volatileThread); t2.start(); Thread t3 = new Thread(volatileThread); t3.start(); Thread t4 = new Thread(volatileThread); t4.start(); Thread t5 = new Thread(volatileThread); t5.start(); } catch (Exception e) { e.printStackTrace(); } } } |
运行结果:
49990
49991
49992
49993
49994
49995
49996
49997
49998
49999
50000
成功累加到50000!
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步