线程之间的可见性

以服务器模式运行下面的Java程序: ( 默认为client模式) (本机使用的是Oracle的Hotspot VM)

java -server StopThread

 

import java.util.concurrent.TimeUnit;

// Broken! - How long would you expect this program to run ?
public class StopThread {

	private static boolean stopRequested;  // value: false
	
	public static void main(String... args) throws InterruptedException {
		
		Thread backgroundThread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				int i = 0;
				while(!stopRequested)
					i++;
			}
		});
		
		backgroundThread.start();
		
		TimeUnit.SECONDS.sleep(1);
		stopRequested = true;
	}
}

 这个例子有两个线程,一个是main线程,另一个是在main线程里启动的backgroundThread线程,这两个线程共享一个类型为boolean的stopRequested。理论上如果main线程将stopRequested变量设置为true时,backgroundThread线程会跳出循环然后结束,接着JVM也终止。但是在我的机子上(Intel i5 M460),它一直处于运行中,并不会在1秒中后停止运行。

原因要归咎于Java语言规范(JLS)中的内存模型(Memory Model)。它规定了一个线程所做的变化何时以及如何变成对其它线程可见(可见性)。因此我们不知道何时后台线程能够“看到”main线程对stopRequested的值所做的改变。一个简洁,性能好的方案是对共享变量添加volatile关键字。volatile保证任何一个线程在读取该域的时候都能“看到”最近刚刚被写入的值。

修改后的代码:

import java.util.concurrent.TimeUnit;

// Broken! - How long would you expect this program to run ?
public class StopThread {

	private static boolean volatile stopRequested;  // value: false
	
	public static void main(String... args) throws InterruptedException {
		
		Thread backgroundThread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				int i = 0;
				while(!stopRequested)
					i++;
			}
		});
		
		backgroundThread.start();
		
		TimeUnit.SECONDS.sleep(1);
		stopRequested = true;
	}
}

 以服务器模式运行后会在1秒左右后自动停止VM。

 

除了使用关键字volatile,还可以使用synchronized。同步机制除了能够实现线程之间的互斥之外,还能实现线程间的通信(即可以实现可见性)。

import java.util.concurrent.TimeUnit;

// Broken! - How long would you expect this program to run ?
public class StopThread {

	private static boolean stopRequested;  // value: false
	
	public static synchronized void requestStop() {
		stopRequested = true;
	}
	public static synchronized boolean stopRequested() {
		return stopRequested;
	}
	public static void main(String... args) throws InterruptedException {
		
		Thread backgroundThread = new Thread(new Runnable() {
			
			@Override
			public void run() {
				int i = 0;
				while(!stopRequested())
					i++;
			}
		});
		
		backgroundThread.start();
		
		TimeUnit.SECONDS.sleep(1);
		requestStop();
	}
}

 此时可以且应该取消volatile关键字,因为synchronized关键字也能实现可见性。

另外需要注意的是除了同步写操作之外,还应该同步读操作。如果读和写操作没有都被同步,同步就不会起作用。



已有 0 人发表留言,猛击->> 这里<<-参与讨论


ITeye推荐
  • —软件人才免语言低担保 赴美带薪读研!—



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