300字范文,内容丰富有趣,生活中的好帮手!
300字范文 > Java多线程之线程的可见性(二)

Java多线程之线程的可见性(二)

时间:2023-04-03 13:55:52

相关推荐

Java多线程之线程的可见性(二)

1.Java的内存模型

Java Memory Model (JAVA 内存模型)描述线程之间如何通过内存(memory)来进行交互。 具体说来, JVM中存在一个主存区(Main Memory或Java Heap Memory),对于所有线程进行共享,而每个线程又有自己的工作内存(Working Memory),工作内存中保存的是主存中某些变量的拷贝,线程对所有变量的操作并非发生在主存区,而是发生在工作内存中,而线程之间是不能直接相互访问,变量在程序中的传递,是依赖主存来完成的。

2、内存可见性

从上图可知,如果线程A对共享变量X进行了修改,但是线程A没有及时把更新后的值刷入到主内存中,而此时线程B从主内存读取共享变量X的值,所以X的值是原始值,那么我们就说对于线程B来讲,共享变量X的更改对线程B是不可见的。

例如:

public class NoVisibility{private static boolean asleep = true;private static class readerThread extends Thread{while(!asleep){doSomething();}}public static void main(String []args){new readerThread().start();asleep = false;}}

当主线程把asleep改成true时,这个new readerThread()可能会继续循环下去,因为主线程的asleep的结果储存在他的本地内存中,而没有刷新到主内存中,所以new readerThread()读取主内存,根本没有察觉到asleep的变化。

3.如何实现可见?

实现可见有四类关键字,volatile,java.util.concurrent中实现的原子操作类,synchronized,还有final

volatile

volatile赋予了变量可见——禁止编译器对成员变量进行优化,它修饰的成员变量在每次被线程访问时,都强迫从内存中重读该成员变量的值;而且,当成员变量发生变化时,强迫线程将变化值回写到共享内存,这样在任何时刻两个不同线程总是看到某一成员变量的同一个值,这就是保证了可见性。简单来说就是读必须从主内存读,写必须写到主内存中。

由于使用volatile屏蔽掉了VM中必要的代码优化,所以在效率上比较低,因此一定在必要时才使用此关键字。 就跟C中的一样 禁止编译器进行优化。

使用建议:

1.在上面的asleep状态变量的例子中,非常适合用volatile关键字,它用来检测某个状态变量以判断是否进行下一步操作。它常常用做中断或状态的标志

2.当要访问的变量已在synchronized代码块中,或者为final时,不必使用。原因很简单,因为synchronized和final都能让变量可见

synchronized

synchronized的可见性是由“对一个变量执行unlock操作之前,必须先把此变量同步回主内存中(执行store和write操作)”这条规则获得的。

Java.util.concurrent中实现的原子操作类

Java.util.concurrent中实现的原子操作类包括:

AtomicBoolean、AtomicInteger、AtomicLong、AtomicReference。

其底层就是volatile和CAS 共同作用的结果:

首先使用了volatile 保证了内存可见性。

然后使用了CAS(compare-and-swap)算法 保证了原子性。

其中CAS算法的原理就是里面包含三个值:内存值A 预估值V 更新值 B 当且仅当 V == A 时,V = B; 否则,不会执行任何操作。

final

被final修饰的字段是构造器一旦初始化完成,并且构造器没有把“this”引用传递出去,那么在其它线程中就能看见final字段的值。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。