volatile 关键字与内存可见性

我们使用多线程的目的是:尽可能的利用CPU资源,提升效率。

 当然,如果线程使用不当,会比不使用多线程效率更低,线程创建本身,就需要开销。

 

Jdk1.5 之后,java.util.concurrent 包,为我们提供了很强大的多线程支持。 

 

首先我们说说 Volatile 关键字,这是为了解决: 内存可见性 问题。

 

可见性问题,看代码、

public class Volatile {
	
	public static void main(String[] args) {
		ThreadDemo td = new ThreadDemo();
		new Thread(td).start();
		
		while(true){
			
			if(td.isFlag()){
				System.out.println("flag is true now");
				break;
			}	
		}
		
	}

}

class ThreadDemo implements Runnable {

	private  boolean flag = false;

	@Override
	public void run() {
		
		try {
			Thread.sleep(200);
		} catch (InterruptedException e) {
		}

		flag = true;
		
		System.out.println("flag=" + isFlag());

	}

	public boolean isFlag() {
		return flag;
	}

	public void setFlag(boolean flag) {
		this.flag = flag;
	}

}

 这段代码的结果是:

flag=true 

当线程td改变了flag的值为true,但是,main线程却迟迟不打印 ,因为main线程读取到的flag一直是false。两个线程对共享数据flag的操作,彼此不可见,这就是内存可见性问题。

 
每个线程启动的时候,JVM会为每一个线程开启独立的缓存空间,用于提高效率。

 

所以,在main线程的缓存空间中,flag始终是false。

解决方法:

  1. Synchronize (同步锁)
  2.  volatile 关键字

Synchronize 的作用,是去主存中读数据。

 

	while(true){
			//使用synchronize也能搞定。
			synchronized (td) {

				if(td.isFlag()){
					System.out.println("------------------");
					break;
				}	
				
			}
				
		}

 

  输出:

flag is true now
flag=true

  但是,同步锁synchronize,效率非常低

 

用Valotile 也可以解决。

 

private volatile boolean flag = false;

 

  

 输出相同:

flag is true now
flag=true

 

 

实际上,Valotile用的是计算机底层的内存栅栏,实时的把数据刷新到主存上。我们可以理解为:只要是volatile 关键字修饰的变量,那么线程就直接在主存中操作数据。

相较于 synchronized 是一种较为轻量级的同步策略

但是,Volatile也有其缺陷: Volatile 不具备互斥性,因此Volatile 也不能保证对象的原子性。

 

posted @ 2017-10-30 17:51  myJavaCareerLife  阅读(217)  评论(1)    收藏  举报