深入理解java虚拟机第十二章

第十一章 Java内存模型与线程

一 Java内存模型

1. 主内存与工作内存

Java内存模型规定了所有的变量都存储在主内存中。每条线程还有自己的工作内存,线程的工作内存保存了被该线程使用到的变量的主内存副本拷贝,线程对变量的所有操作都必须在工作内存中进行,而不能直接读写主内存中的变量。

主内存主要对应于Java堆中的对象实例数据部分,而工作内存则对应于虚拟机栈中的部分区域。

2. volatile变量

volatile变量具有可见性,可见性是指当一条线程修改了这个变量的值,新值对于其它线程来说是可以立即得知的。但是并发下并不保证是安全的。而下面的场景就很适合使用volatile变量来控制并发

        volatile boolean shutdownRequested;
	
	public void shutdown(){
		shutdownRequested = true;
	}
	
	public void doWork(){
		while(!shutdownRequested){
			//do stuff
		}
	}

使用volatile变量的第二个语义是禁止指令重排序优化,例如标准的单例设计模式代码:

class Singleton{
    
	private volatile static Singleton instance;
	
	public static Singleton getInstance(){
		if(instance == null){
			synchronized (Singleton.class) {
				if(instance == null){
					instance = new Singleton();
				}
			}
		}
		return instance;
	}
		
}

上面的代码中volatile变量可以使得在一个CPU的Cache写入内存时,引起别的CPU或者内核无效化其Cache,使修改对其他CPU立即可见。如果上述代码不用volatile变量会发生什么问题?

在执行instance=new Singleton();时,并不是原子语句,实际是包括了三大步骤:1.为对象分配内存;2.初始化实例对象 ;3.把引用instance指向分配的内存空间

这三个步骤并不能保证按序执行,处理器会进行指令重排序优化,存在这样的情况:优化重排后执行顺序为:1,3,2, 这样在线程1执行到3时,instance已经不为null了,线程2此时判断instance!=null,则直接返回instance引用,但现在实例对象还没有初始化完毕,此时线程2使用instance可能会造成程序崩溃。

另外,双重检查的原因:进入方法过后,先检查实例是否存在,如果不存在才进入下面的同步块,这是第一重检查。进入同步块过后,再次检查实例是否存在,如果不存在,就在同步的情况下创建一个实例,这是第二重检查。这样一来,就只需要同步一次了,从而减少了多次在同步情况下进行判断所浪费的时间。 


二 Java与线程

线程的实现方式主要有三种:使用内核线程实现,使用用户线程实现,使用用户线程加轻量级进程混合实现。而对于Java(Sun JDK)来说,Windows版和linux版都是使用一对一的线程模型实现的,一条java线程就映射到一条轻量级进程之中。

线程调度的方式有两种:协同式线程调度和抢占式线程调度。java使用的调度方式就是抢占式调度。

java语言定义了以下这些线程状态:新建;运行;无限期等待;限期等待;阻塞;结束。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值