JVM系列(五)——Java线程

来源:互联网 发布:淘宝手机图片空间签订 编辑:程序博客网 时间:2024/06/05 11:20


如果没有多线程的出现,处理器的处理效率将会是很低的。试想象一个图形化界面,如果只有一个线程,那么每点击一次界面的操作,特别是费时的操作,界面就需要一阵子的停顿,待后台处理完后,才能够继续操作。这该是多差的用户体验啊。

Java提供了较为成熟的内存模型,来支持多线程的运作。为什么需要好的内存模型呢?因为在多线程中,最难以处理的就是各个变量的安全问题。例如,一个变量在某个线程中修改了,而另一个线程又读取了旧数据并对其进行修改,就出现了“脏读”的情况。一个好的内存模型既要考虑到变量、操作的安全性,又要兼顾效率,这就使之成为一个棘手的问题了。

1、  Java的内存模型。

Java的内存模型主要目标是定义程序中各个变量的访问规则。由于一些变量如:局部变量、方法参数,这些是由内存私有的,因此,不在讨论的范畴。主要针对的是能够被多个线程共用的变量,如:实例字段、静态字段、构成数组对象的元素。因此,下文的变量,也是该意。

Java中,将内存分为主内存与工作内存。

主内存中的变量是所有线程共享的,所有变量都是存储在主内存当中的。

工作内存是每个线程所独享的,他保存的是主内存中某些变量的拷贝,线程所有的操作都是在工作内存中,各个线程无法进行相互访问,变量的传递需要通过主内存。


图 线程、工作内存、主内存间的关系

 

2、  Volatile关键字

写过多线程程序的话,应该对这个关键字并不陌生。他是一个轻量级的同步,与synchronized相比,所需的编码和运行时的开销都少。下面看看volatile变量的作用:

1)      作用一:保证此变量对所有线程的可见性。Volatile修饰的变量,在每次被线程访问时,都强迫从共享内存中重读该成员变量的值。而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存。这样在任何时刻,两个不同的线程总是看到某个成员变量的同一个值。

但是,以上的做法,并不能够保证volatile修饰的变量是绝对线程安全的。因为JVM只能保证在修改了volatile变量之后,将其写回共享内存,但并不能够保证从修改-写回内存,这两个操作是原子操作,因此,若另一个线程在变量还没来得及写回的情况下,就对其进行访问,那么读到的数据便会是旧数据了。

对于普通变量,为了获得最佳速度,允许线程保存共享成员变量的私有拷贝,而且只当线程进入或者离开同步代码块时才与共享成员变量的原始值对比。

2)      作用二:禁止指令排序优化。JVM为了能够加快程序的执行速度,会对程序的指令进行排序优化。由于volatile修饰的变量可能会作为其他线程的判断符(如通过在对象初始化完毕之后赋值,用来判定某个对象是否初始化完毕),若该变量提前赋值,则会导致其他线程在对象未初始化完毕的情况下就进行对象使用,而导致出错。

3)      对于volatile修饰的变量,就是提示VM:对于这个成员变量不能保存它的私有拷贝,而应直接与共享成员变量交互。在两个或者更多的线程访问的成员变量上使用volatile。当要访问的变量已在synchronized代码块中,或者为常量时,不必使用。

原创粉丝点击