volatile概念
volatile关键字主要作用就是在多个线程之间可见,当一个变量被定义为volatile之后,就可以保证此变量对所有线程的可见性,即当一个线程修改了此变量的值的时候,变量新的值对于其他线程来说是可以立即得知的。可以理解成:对volatile变量所有的写操作都能立刻被其他线程得知。但是这并不代表基于volatile变量的运算在并发下是安全的,因为volatile只能保证内存可见性,却没有保证对变量操作的原子性
实例
public class RunThread extends Thread{
private boolean isRunning = true;
private void setRunning(boolean isRunning){
this.isRunning = isRunning;
}
public void run(){
System.out.println("进入run方法..");
while(isRunning == true){
//..
}
System.out.println("线程停止");
}
public static void main(String[] args) throws InterruptedException {
RunThread rt = new RunThread();
rt.start();
Thread.sleep(1000);
rt.setRunning(false);
System.out.println("isRunning的值已经被设置了false");
Thread.sleep(1000);
System.out.println(rt.isRunning);
}
}
结果: 进入run方法..
isRunning的值已经被设置了false
false
结果分析:isRunning 被设置成false,但是代码一直在执行中,并没有打印出“线程停止”,按理说isRunning 被设置成false,跳出while循环,但是没有,为什么?其实这是jdk的原因
在java中执行线程的时候,jdk都会单独的分配一块空间,但是在jdk 1.5以后,对于每一个线程做了一个优化,对每一个线程加了一个独立的内存空间,这个空间装什么内容呢?去装主内存中的一些引用(就是当前线程锁使用到的一些引用变量),将主内存中的这些变量(它的副本)拷贝到自己的这块区间中去,然后线程在运行的过程中就是去这块区间取他的副本,这样jdk是为了提高线程执行的效率。
线程在执行过程中,他会找自己独立的那块空间,跟他交互,而调用的isRunning 被设置成false,只是将主内存中值改变掉,但是线程锁依赖的那块独立内存空间中的变量值并没有改变,所以依然是true,如果想要改变,变量声明成可见性,用volatile修饰,当isRunning 值改变的时候,会强制线程执行引擎去主内存中读取。
变量用volatile 修饰 执行
private volatile boolean isRunning = true;
结果:
进入run方法..
isRunning的值已经被设置了false
true
线程停止
volatile具备可见性,但是不具备原子性
/**
* volatile关键字不具备synchronized关键字的原子性(同步)
*
*/
public class VolatileNoAtomic extends Thread{
private static volatile int count;
//private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
count++ ;
//count.incrementAndGet();
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
结果:
1361
2361
1361
3361
4361
5361
6361
7361
8361
9361
分析: 10个线程,每个线程是1000,最终是10000,但是结果没有10000,因为volatile不具备原子性, 如何保证原子性,推荐使用原子类Atomic进行操作
public class VolatileNoAtomic extends Thread{
//private static volatile int count;
private static AtomicInteger count = new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
//count++ ;
count.incrementAndGet();
}
System.out.println(count);
}
public void run(){
addCount();
}
public static void main(String[] args) {
VolatileNoAtomic[] arr = new VolatileNoAtomic[100];
for (int i = 0; i < 10; i++) {
arr[i] = new VolatileNoAtomic();
}
for (int i = 0; i < 10; i++) {
arr[i].start();
}
}
}
4000
4000
5000
4000
4000
9000
8000
7000
6189
10000
最终结果是10000