浅谈volatile,直接看demo,引出来。
package com.second.app.thread.singleton;
/**
* @Author soul yzg
* @Date 2021/2/7 8:22
* 努力学习 天天进步
*/
public class RunThread extends Thread {
private boolean isRunning = true;
public boolean isRunning() {
return isRunning;
}
public void setRunning(boolean running) {
isRunning = running;
}
@Override
public void run() {
System.out.println("进入run了");
while (isRunning == true) {
}
System.out.println("线程要被停止了");
}
}
package com.second.app.thread.singleton;
/**
* @Author soul yzg
* @Date 2021/2/7 8:25
* 努力学习 天天进步
*/
public class Run {
public static void main(String[] args) {
try {
RunThread runThread = new RunThread();
runThread.start();
Thread.sleep(1000);
runThread.setRunning(false);
System.out.println("已经被赋值false了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
- 输出结果:虽然主线程已将isRunning 改成false,但是程序并未停止,一直重复循环,这是什么原因呢?
分析:
如果我们给变量加上volatile关键字修饰,
程序停止了:
- 首先我们了解下线程的原子性,可见性,有序性。
- 对于多个线程之间共享的变量,可以采用volatile关键字修饰,保持可见性。但是volatile不能保证线程的原子性。见下面的代码:
package com.second.app.thread.singleton;
/**
* @Author soul yzg
* @Date 2021/2/7 10:32
* 努力学习 天天进步
*/
public class MyThread extends Thread {
volatile private static int count;
@Override
public void run() {
addCount();
}
private void addCount() {
for (int i = 0; i < 10; i++) {
count++;
}
System.out.println(Thread.currentThread().getName()+"count=" + count);
}
}
package com.second.app.thread.singleton;
/**
* @Author soul yzg
* @Date 2021/2/7 8:25
* 努力学习 天天进步
*/
public class Run {
public static void main(String[] args) {
//自定义线程数组,模拟10个线程
//假设volatile符合原子性,那么10个线程最终计算的count的值应该等于100;
MyThread[] threadArray = new MyThread[10];
for (int i = 0; i < 10; i++) {
threadArray[i] = new MyThread();
}
for (int i = 0; i < 10; i++) {
threadArray[i].start();
}
}
}
实则不然:
- 我们会发现:最终的结果并不等于100;那么如何在保持原子性呢?
答:只需要引入synchronized关键字,保持同步,因为count++并不是原子性操作,所以需要保证同一 个时刻只有一个线程进行++操作。如下:
下面摘抄来自:高红岩的多线程编程核心艺术。
谈到volatile关键字,不得不谈指令重排,因为volatile是禁止指令重排的。
- 说到这里,我对指令重排还是有点迷糊的,说到底平时开发中一般对计算机底层接触的比较少。利用这个春节放假间隙,我慢慢完善。因为真的很少去总结。
- 指令重排:我的理解为计算机在执行程序代码的一种方式。cpu处理器和编译器为了提高性能,默认会对代码进行一定的重排。
- 单线程情况下,确保程序的执行结果和和代码顺序执行结果保持一致。
- 但是在多线程的情况下,由于指令重排的存在,所以不能保证两个线程使用的变量保持一致。