java多线程编程底层原理剖析以及volatile原理

       今天总结一下java多线程机制,以及volatile

       首先,为什么需要多线程?

      主要是因为计算机的运算能力远远大于I/O,通信传输,还有数据库访问等操作。所以缓存出现了,从而提高了访问速度。但是由于会有多个缓存,以及数据读写问题,很有可能会读到脏数据,其实这也就是缓存的一致性。

      另外为了提高效率,处理器会对程序进行乱序执行优化,而对于虚拟机来说,就是指令重排序。意思就是说代码顺序与实际执行顺序无关,实际执行顺序是虚拟机根据前后依赖关系,结合运算器来决定的,但是结果是一样的。

      走入正题,先介绍一下java内存模型,内存模型主要用来屏蔽硬件与内存访问的差异。

     

     对于每一个线程会有工作内存,多个线程共享一个主内存,例如对象实例就在主内存会多个线程共享,而引用这个对象的变量实际在每个线程的工作内存,工作内存拥有主内存实例的副本拷贝,通过它来对实例进行,读取与赋值都在工作内存,并且线程之间无法读取对方的变量,都是通过主内存做一个过渡作用。(这里工作内存与主内存跟堆内存与栈内存不是一个概念,这是为了好理解)

   接下来工作内存与主内存怎么进行交互?虚拟机定义了8种原子操作,包括lock(锁定主内存的变量,使其被某一线程独占),unlock(同理),read(把一个主内存的变量传递到工作内存中,以便load),load(将从主内存传递的值传递到工作内存的变量副本中),store(将工作内存中变量副本传递到主内存中去,以便write),write(将工作内存传递过来的值赋到主内存中变量),use(将工作内存的值传递给执行引擎),assign(将执行引擎的值传递到工作内存),这8中操作可以用来确定你的访问是否安全。

  下面介绍一下volatile,经常被问到的一个关键字,他的作用主要有两个,我们一一说明:1 保证变量在各个线程的可见性,意思就是说这个变量的值一修改,其他线程可以立即得知。而一个普通变量需要先写回主内存,然后其他线程去读取这个值。2:禁止指令重排序优化。然而它并不能保证原子性,以及运算的线程安全,下面代码来解释一下第一个特性。

 public class VolatileTest extends Thread{
   public  static volatile int a=0;
   public void run() {
			a++;
			}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        VolatileTest array[]=new VolatileTest[10000];
             for (int i = 0; i < array.length; i++) {
	             array[i]=new VolatileTest();
	             array[i].start();
                                    }
         System.out.println(VolatileTest.a);
	}

}
我们希望结果会是10000,然而并不是,原因就是a++这一条指令并不是原子操作,volatile的确保证从主内存获得的数据是最正确的,但是当你运算的时候,其他线程很有可能会把一个值穿进去,导致值会变小。

那么什么情况下用volatile呢?一定要明白,它的开销一定会小与同步块。下面就是使用的情况,不符合这两条就要用同步块了。

1:运算结果并不依赖与当前值。

2:变量不需要与其他变量参与不变约束。

同样用代码解释一下。

public class VolatileTest extends Thread{
   public   static volatile int a=0;
   public void run() {
	               a=2;
			}
	public static void main(String[] args) {
		// TODO Auto-generated method stub
        VolatileTest array[]=new VolatileTest[100];
             for (int i = 0; i < array.length; i++) {
	             array[i]=new VolatileTest();
	             array[i].start();
	              System.out.print(array[i].a+" ");
     }
      }}

     个人理解就是a的值不依赖与现在在主内存a的实际值,不管a是几,都变成1,而其他线程也会立即受到通知,因为也没有运算,也会直接变为1.

接下来讲一下指令重排序优化的东东,其实指令重排序对于单线程来说有利无害,反正最后的结果是一样的,而且还提高了效率,但是对于多线程,可能会出现一些问题,而volatile修饰的变量,会在操作的时候,设置一个屏障,后面的操作,肯定不会比这个提前。否则后面的操作先执行,从而提前影响其他的线程。

  好的,下面介绍几个概念1:原子性   就像前面说的那8张操作就是,粒度小到多线程也不可能拆开它,而用synchronized,内部的东西其实就是一个组装的“大原子”,但是记住volatile是不可以的 2 可见性 意思就是线程修改了值之后会立即同步到主内存,并且获取值会从主内存直接获取,而非缓存,volatile和synchronized都可以保证 3有序性 意思是保证线程内部执行顺序,volatile可以保证禁止指令重排序,而synchronized,直接就锁上了,所以它能解决几乎所有同步问题,造成了滥用。

线程是cpu调度的基本单位,粒度比进程小,Thread的类很多方法是native,可能会为了效率,然而同时可能会平台相关,注意线程的优先级不太靠谱,以为可能与平台线程的优先级不一样,造成冲突。再次补充一个线程状态模型(本文章主要介绍java多线程模型,以及volatile,线程基础不再赘述)


阻塞状态与挂起状态的区别在于阻塞在等待一个排它锁,而挂起是等待时间到,或者是唤醒。

更多细节请查看我的线程基础的这篇博客,多谢大家支持

本博客知识来源于深入理解java虚拟机,值得一看,强力 推荐,特别底层!!!

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值