【Java多线程】Java内存模型与Volatile

1.Java内存模型

共享内存模型指的就是Java内存模型(简称JMM),JMM决定一个线程对共享变量的写入何时对另一个线程可见。从抽象的角度来看,JMM定义了线程和主内存之间的抽象关系:线程之间的共享变量存储在主内存(mainmemory)中,每个线程都有一个私有的本地内存(localmemory),本地内存中存储了该线程以读/写共享变量的副本。本地内存是JMM的一个抽象概念,并不真实存在。它涵盖了缓存,写缓冲区,寄存器以及其他的硬件和编译器优化。

【Java多线程】Java内存模型与Volatile_第1张图片 【Java多线程】Java内存模型与Volatile_第2张图片

简单来说:程序开始运行,在主内存中有个共享变量,线程A和线程B需要用到共享变量,他们会各自从主内存中复制变量到属于线程自己的本地内存而不是直接操作主内存的共享变量,线程A和线程B对他们的复印出来的变量进行操作,如果没有及时的把新结果返回到主内存中共享,或者没有及时的从主内存中拿到新的共享变量,线程A和线程B在自己的本地内存中操作的复印变量就会发生误差,这也是造成并发线程安全问题的原因所在。

下面看个例子

TestVolatile.java

public class TestVolatile{
	public static void main(String[] args) {
		VolatileThread thread = new VolatileThread();
		Thread t = new Thread(thread);
		t.start();
		try {
			Thread.sleep(300);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// 调用方法关闭线程
		thread.stopThread();
	}
}

class VolatileThread implements Runnable{
	public static boolean flag = true;
	
	// 关闭线程的方法
	public void stopThread() {
		flag = false;
	}
	
	@Override
	public void run() {
		System.out.println("线程开始执行");
		while(flag){
			
		}
		System.out.println("线程结束执行");
	}
}
结果

【Java多线程】Java内存模型与Volatile_第3张图片

可以看到,虽然调用了关闭方法,但是线程依然没有停止,好像无视flag=false继续运行。

在了解了JMM之后,可以知道虽然主内存里的共享全局变量flag改成了false,但是线程的本地内存里的flag依然是true,而循环是从线程的本地内存里取flag,所以不会停止。

2.Volatile

由于java内存模型会有主内存和线程本地内存不一致的问题,所以就有了volatile来修饰变量。
由volatile修饰的全局变量,当值被改变时,会立刻更新到线程本地内存中。
// 用volatile修饰了静态全局变量
public static volatile boolean flag = true;
结果
线程开始执行
线程结束执行
现在已经能正确的执行了。

Q:那么volatile是不是能解决原子性问题呢?
A:volatile是非原子性的,看以下代码。

Test.java
public class Test {
	public static void main(String[] args) {
		TestThread thread = new TestThread();
		Thread t1 = new Thread(thread);
		Thread t2 = new Thread(thread);
		// 启动两个线程操作
		t1.start();
		t2.start();
		
	}
}

class TestThread implements Runnable {
	// 使用volatile修饰静态全局变量
	public static volatile int count = 0;

	@Override
	public void run() {
		while (count < 100) {
			if(count < 100) {
				try {
					Thread.sleep(10);
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
				count++;
				System.out.println(count);
			}
		}
	}
}
结果片段
99
100
101
结果显然是不安全的,要想让线程安全,可以使用synchronized或者lock。

你可能感兴趣的:(Java多线程)