在没有说volatile之前先看看未使用volatile导致问题的情况。例如下面的TestVolatile1编译的时候添加了 -server。则程序不会终止.
这是由于编译器对其进行了优化,while(!exit)因为内部没有修改exit变量且没有声明为volatile类型,JVM会把判断提前,类似于优化成如下。
if(exit)
{
while(true)
{
doSomeThing();
}
}
所以main函数虽然对其进行了exit=true的操作,但是由于已经跳过了判断阶段,所以不会起作用了。
注:在没有同步的情况下,编译器、处理器以及运行时等操作的执行顺序进行意想不到的调整。在缺乏足够同步的多线程程序中,要相对内存的执行顺序进行判断,几乎无法得到正确的结论。《java并发编程实践》
在while循环中添加System.out.prinln()时不会有这样的死循环发生,也许对有IO操作的while循环也不会进行优化? --谁知道告诉我下。
package Volatile;
public class TestVolatile1 {
public static boolean exit = false;
public static int count = 0;
public static void main(String[] args)
{
TestVolatile1 tv = new TestVolatile1();
testThread thread = tv.new testThread();
thread.start();
//消耗时间,避免先设置exit,后执行线程。
for(int i =0; i < 100; i++)
{
System.out.println("waste time!");
}
exit = true;
}
private class testThread extends Thread
{
public void run()
{
while(!exit)
{
count ++;
// System.out.println("count" + count); //添加打印会导致exit=true生效?
}
System.out.println("EXIT! count is :" + count);
}
}
}
通过上面的程序可以看出来,volatile的一个作用就是告诉编译器不要给我乱排序,不要优化我,这样保证了对其操作的原子性(赋值,获取等),java不会把获取到一半的时候就进行其他操作,切换线程。但是需要注意的是例如count++与count=count+1,并不是原子的。因为他分为了获取,赋值两个操作。例如count=i+1,就是原子的,因为就赋值一个操作。
另个volatile变量的特点就是不会被缓存在寄存器(线程的内存中)或者其他处理器不可见的地方。保证操作的volatile变量没有复制版本,只有一个内存空间进行存储,因此多线程在访问volatile变量时获取的都是最新的值,但需注意的是如上面所说的他只能保证可见性,不能保证对其操作的原子性,因此在多线程操作的时候可能也会出现数据失效的情况,例如下面程序只有coun1能够正确输出1000;
package Volatile;
public class TestVolatile extends Thread{
/**
* @param args
*/
static volatile int count = 0;
static volatile int count1 = 0;
static int count2 = 0;
public static synchronized void inc()
{
count1++;
}
public static void inc2()
{
count2++;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Thread []tArray = new Thread[100];
for(int i =0; i < 100; i++)
{
tArray[i] = new TestVolatile();
}
for(int i = 0; i < 100; i++)
{
tArray[i].start();
}
for(int i = 0; i < 100; i++)
{
try {
tArray[i].join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println("count is :" + TestVolatile.count);
System.out.println("count1 is :" + TestVolatile.count1);
System.out.println("count2 is :" + TestVolatile.count2);
}
public void run()
{
for(int i = 0; i < 10; i++)
{
count ++;
inc2();
inc();
try {
sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
输出结果:
count is :996
count1 is :1000
count2 is :997