回来填坑:下边说的作用只是volatile保证了goon的可见性,即如果有一个线程修改了共享变量,则会立即让其他线程都知道。如下例子,在主线程中修改了线程类的变量goon的值,每个线程都有属于自己的goon,所以那个goon被在主线程中赋值为false,那个线程就会结束。
补充一下:volatile还有一个作用就是保证有序性,在 Java 内存模型中,允许编译器和处理器对指令进行重排序,重排序过程不会影响到单线程程序的执行,却会影响到多线程并发执行的正确性。volatile 关键字通过添加内存屏障的方式来禁止指令重排,即重排序时不能把后面的指令放到内存屏障之前。
每天进步多一点
假装下边是分割线
--------------------------------------------------------------------------------------------------------------
在网上也有很多关于volatile关键字的文章,看了很多,经过亲自测试,在此写下我的见解:
volatile关键字的作用:拒绝优化!
在java中,会将一些经常需要读写的成员拿到寄存器去进行运算,以此来加快运算速度。但是此操作并不是线程安全的,在多线程中,会有不同的线程对该成员进行操作,当该成员被拿到寄存器进行运算优化后,就不能时时刻刻保证与主内存的值同步,因此在多线程中操作时可能会产生错误。当一个成员被volatile关键字修饰时,就会拒绝优化:拒绝将该成员拿到寄存器去进行运算优化。以此来避免多线程不安全的问题。
举个很简单的例子:for(i = 0; i < 10; i++) {}
这个简单的操作,在没有加volatile关键字,i 被拿到寄存器进行运算优化:
假设i为一个局部变量,他的偏移量是-4,则 i 的首地址应该是edp[-4];
对应的汇编语言如下:
mov ecx, edp[-4]
loop:
inc ecx
cmp ecx, 10
jl loop:
大概意思就是从首地址为-4的区域取出值,赋值给ecx,然后给ecx加一,用ecx和10作比较,如果小于10,就继续循环。
如果对变量i增加volitale关键字,则,将使用下面的汇编:
loop:
mov ecx, edp[-4]
inc ecx
mov edp[-4], ecx
cmp ecx, 10
jl loop
这段意思是说:从首地址未-4的区域取出值,赋值给ecx,给ecx加一,然后再将ecx的值赋值给原地址,即edp[-4],然后使用ecx和10进行比较,如果小于10,则继续进入循环。
由此可见两者只有一句话不同,那就是有没有将改变后的值同步更新到源地址。未加volatile关键字的变量在寄存器中运算的时候使用的是ecx,而自己本身的值并没有变。加了volatile关键字的变量会在运算结束后,同步更新原来的值,然后再进行下一步操作。因为寄存器更注重运算速度,所以会省去同步值得步骤。由此可知:加了volatile关键字的变量,其运算速度会大打折扣。
下边举一个我做的小测试,很能说明问题:
编写一个线程类,在构造方法中启动该线程, 使用一个Boolean类型的成员goon来控制线程的结束。此处goon未加volatile关键字:
package com.yc.test;
/*
*@author yc
*@time 2018/11/14
*/
public class TaskRunByThread implements Runnable{
private static int id;
private Boolean goon;
public TaskRunByThread() {
goon = true;
new Thread(this,"[线程" + ++id + "]").start();
}
public void stopThread() {
System.out.println("收到关闭线程的命令");
goon = false;
}
@Override
public void run() {
System.out.println("线程" + Thread.currentThread().getName() + "开始运行");
while (goon) {
}
System.out.println("线程" + Thread.currentThread().getName() + "运行结束");
}
}
编写一个简单的测试类,将上边的线程启动三次,然后每隔随机时间关闭一个线程。
package com.yc.test;
import java.util.Random;
/*
*@author yc
*@time 2018/11/14
*/
public class Test {
public static void main(String[] args) {
Random random = new Random();
TaskRunByThread trbts[] = new TaskRunByThread[3];
//注册并启动线程
for (int i = 0; i < trbts.length; i++) {
trbts[i] = new TaskRunByThread();
}
for (int i = 0; i < trbts.length; i++) {
int num = random.nextInt(5000);
System.out.println("时间间隔:" + num);
try {
Thread.sleep(num);
trbts[i].stopThread();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
运行该测试类,
可以很清楚的看到:三个线程均启动,并且关闭线程的方法已运行,但从状态栏可以很清楚的看到,三个线程一直在跑,并没有结束。
是因为goon成员未被volatile关键字修饰,被拿到寄存器运算优化,但寄存器的值没有和主内存同步,因此在关闭线程的方法对goon赋值的操作并没有同步到主内存,因此线程无法关闭。
接下来让我们加上volatile关键字再来试一下:
可以很明白的看见,三个线程全部运行完毕且关闭。在运行的时候很明显经过间隔时间线程就会关闭。
每天进步多一点