概述
java工作内存和主内存模型
在多线程中,多个线程访问主存中的临界资源(共享变量)时,需要首先从主存中拷贝一份共享变量的值到自己的工作内存中,然后在线程中每次访问该变量时都是访问的线程工作内存(高速缓存)中的共享的变量副本,而不是每次都去主存读取共享变量的值(因为CPU的读写速率和主存读写速率相差很大,如果CPU每次都访问主存的话那么效率会非常低)。 java线程变量加载的大致流程是,将主内存的变量加载到工作内存进行处理,处理完毕后写会主内存。
工作内存和主内存数据交换时机
先看下如下代码,主线程运行时开启另一个线程,设置flag为true;但是发现主线程一直都未结束。 这个是大家在学习线程间可见性时,经常会遇到的例子,通常的解决办法是给flag,加上volatile关键字,保证变量的可见性; 对于线程安全问题,很多时候都不是必须的,但有一个奇怪的现象是,下面的程序每次运行都是必现,这又是什么原因导致的呢?
public class MemorySync {
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo, "t1").start();
while (true) {
if (threadDemo.getFlag()) {
System.out.println("end task");
break;
}
}
}
}
class ThreadDemo implements Runnable {
private boolean flag = false;
@SneakyThrows
@Override
public void run() {
TimeUnit.SECONDS.sleep(1);
flag = true;
System.out.println("set flag:" + flag);
}
public boolean getFlag() {
return flag;
}
}
不可见原因分析
- 为什么主线程没有对ThreadDemo线程的变量变更可见呢?
答:从内存模型可知,主线程一直用工作内存的变量,没有重新加载主内存被ThreadDemo线程改变的变量。 - 为什么主线程一直没有重新加载主内存的变量呢?
答:只有工作内存失效的时候,工作内存才会重新加载主内存的变量。
工作内存什么时候同步主内存的变量
- 线程中释放锁,线程自己同步主存变量。
当前线程释放锁后,当前线程会去加载主存变量。 - 线程上下文切换。
- CPU有空闲时间时(比如线程休眠、IO操作等)
上面代码工作内存重新加载主内存方案
- 加volatile关键字
class ThreadDemo implements Runnable {
private volatile boolean flag = false;
@SneakyThrows
@Override
public void run() {
TimeUnit.SECONDS.sleep(1);
flag = true;
System.out.println("set flag:" + flag);
}
public boolean getFlag() {
return flag;
}
}
- main线程加休眠时间
public class MemorySync {
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo, "t1").start();
while (true) {
if (threadDemo.getFlag()) {
System.out.println("end task");
break;
}
TimeUnit.SECONDS.sleep(1);
}
}
}
- IO操作等,比如File file = new File(“F://temp.txt”)
public class MemorySync {
public static void main(String[] args) throws InterruptedException {
ThreadDemo threadDemo = new ThreadDemo();
new Thread(threadDemo, "t1").start();
while (true) {
if (threadDemo.getFlag()) {
System.out.println("end task");
break;
}
File file = new File("F://text.txt");
}
}
}
- 线程释放锁
public class MemorySyncVolatile {
public static void main(String[] args) throws InterruptedException {
ThreadDemo1 threadDemo = new ThreadDemo1();
new Thread(threadDemo, "t1").start();
while (true) {
synchronized (threadDemo) {
if (threadDemo.getFlag()) {
System.out.println("end task");
break;
}
}
}
TimeUnit.SECONDS.sleep(5);
}
}
public class MemorySyncVolatile {
public static void main(String[] args) throws InterruptedException {
ThreadDemo1 threadDemo = new ThreadDemo1();
new Thread(threadDemo, "t1").start();
Lock lock = new ReentrantLock();
while (true) {
lock.lock();
if (threadDemo.getFlag()) {
System.out.println("end task");
break;
}
lock.unlock();
}
TimeUnit.SECONDS.sleep(5);
}
}
参考
线程的工作内存与主内存同步时机 浅析JMM线程工作内存什么时候读取主存变量
|