1.volatile 关键字,使一个变量在多个线程间可见
public class Demo12 {
volatile boolean running = true;
public void test() {
System.out.println("test start.......");
while (running) {
/*try {
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}*/
}
System.out.println("test end........");
}
public static void main(String[] args) {
Demo12 demo12 = new Demo12();
new Thread(demo12::test, "t1").start(); //JDK1.8新特性
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
demo12.running = false;
}
}
说明:
1.volatile 关键字,使一个变量在多个线程间可见。例如:A B线程都用到一个变量,java默认是A线程中保留一份copy,这样如果B线程修改了该变量,则A线程未必知道。使用volatile关键字,会让所有线程都会读到变量的修改值
2.在上面的代码中,running是存在于堆内存的t对象中。当线程t1开始运行的时候,会把running值从内存中读到t1线程的工作区,在运行过程中直接使用这个copy,并不会每次都去读取堆内存,这样,当主线程修改running的值之后,t1线程感知不到,所以不会停止运行。使用volatile,当running值做修改了,将会强制所有线程都去堆内存中读取running的值。
3.线程之间的共享变量存储在主内存(main memory)中,每个线程都有一个私有的本地内存(local memory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。
可以阅读这篇文章进行更深入的理解
[http://www.cnblogs.com/nexiyi/p/java_memory_model_and_thread.html]
2.volatile不具备原子性,也就是说volatile不能替代synchronized
public class Demo13 {
volatile int count = 0;
public void test(){
for (int i = 0; i < 10000; i++) {
count ++;
}
}
public static void main(String[] args) {
Demo13 demo13 = new Demo13();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(demo13::test, "thread-" + i));
}
threads.forEach((o)->o.start()); //JDK1.8新特性
threads.forEach((o)->{ //JDK1.8新特性
try {
o.join(); //等线程执行完毕之后才执行主线程main
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println(demo13.count);
}
}
说明:
1.上面程序预期输出10万,实际输出不到10万,这说明volatile并不能保证多个线程共同修改running变量时所带来的不一致问题。
2.volatile关键字保证了线程之间的内存可见性,也就是说从主内存中读取的数据都是最新的,但是当某个线程往主内存中写数据时,不能保证主内存中的数据是最新的,这就会产生不一致的问题。
3.synchronize可以保证可见性和原子性,volatile只能保证可见性
可阅读下面文章进行深入理解
你真的了解volatile关键字吗?
3.解决同样的问题的更高效的方法,使用AtomicXXX类
public class Demo15 {
//int count = 0;
AtomicInteger count = new AtomicInteger(0);
public /*synchronized*/ void test() {
for (int i = 0; i < 10000; i++) {
count.incrementAndGet(); //count++
}
}
public static void main(String[] args) {
Demo15 demo15 = new Demo15();
List<Thread> threads = new ArrayList<Thread>();
for (int i = 0; i < 10; i++) {
threads.add(new Thread(demo15::test, "thread-" + i));
}
threads.forEach((o) -> o.start()); //JDK1.8新特性
threads.forEach((o) -> { //JDK1.8新特性
try {
o.join(); //等线程执行完毕之后才执行主线程main
} catch (Exception e) {
e.printStackTrace();
}
});
System.out.println(demo15.count);
}
}
说明:
解决同样的问题的更高效的方法,使用AtomicXXX类
AtomicXXX类本身方法都是原子性的,但不能保证多个方法连续调用是原子性