精华帖 (0) :: 良好帖 (0) :: 新手帖 (0) :: 隐藏帖 (0)
|
|
---|---|
作者 | 正文 |
发表时间:2015-11-27
最后修改:2015-11-27
string2020 写道 一直看不懂volatile这个关键字是干啥的,楼主能否深入讲解一下。
我们知道对Java中的64位数据类型(long和double)进行赋值的时候,JVM是不保证原子性的。例如: private long count=0; void someUpdate(){ count=1; } 上述代码中,一个线程调用 someUpdate更新count值的时候,另外一个线程读取到的该变量的值可能是0、也可能是1,甚至可能是其它数值。如果对上述变量采用voalitle进行修饰,那么上述代码对long型变量的赋值就具有了原子性。因此,其它线程读取到的该变量的值只可能是0或者1。
简单来说,就是一个线程对一个共享变量的更新对于另外一个线程而言不一定是可见的。比如, private boolean isDone=false; 如果有个线程将上面的变量修改为true,那么其它线程可能读取到的值一直是false。如果将上述变量采用volatile修饰,那么一个线程将其值修改后,之后有其它线程来读取该变量的值,后面这个线程总是可以读取到跟新后的变量值。
比如下面的代码: private int nonVoaltileVar=1; private boolean isVarSet=false; private void update(){ nonVoaltileVar=2; isVarSet=true; } 上述代码执行时,由于重排序的结果,一个线程执行update方法后(假设此时再也没有其它线程会去更新上述两个变量的值),其它线程读取到isVarSet的值为true的情况下,它所读取到nonVoaltileVar的值可能仍然是1。这是由于update方法中的两个语句的执行顺序可能被对调(重排序)。而如果我们用voalitle去修饰isVarSet,那么voaltile会禁止其修饰的变量的赋值操作前的操作被重排序到其之后。这样,就保证了其它线程读取到isVarSet的值为true的情况下,nonVoaltileVar的值总是为2。 本书第3章的实战案例代码中有使用volatile关键字,可以参考下。如果要进一步或者更加详细的解释,那要不小的篇幅。深入的理解voaltile关键字涉及到CPU访问内存的机制以及JMM。 |
|
返回顶楼 | |
发表时间:2015-11-27
什么是ThreadLocal类,怎么使用它? 一般在什么样的场景下可以使用ThreadLocal类?
多线程情况下,怎么结合当前资源(硬件资源,如cpu,内存,IO等,缓层服务、软件负载等)进行有效控制,一般如何考虑和规划这方面的设计? |
|
返回顶楼 | |
发表时间:2015-11-27
viscent 写道 string2020 写道 一直看不懂volatile这个关键字是干啥的,楼主能否深入讲解一下。
我们知道对Java中的64位数据类型(long和double)进行赋值的时候,JVM是不保证原子性的。例如: private long count=0; void someUpdate(){ count=1; } 上述代码中,一个线程调用 someUpdate更新count值的时候,另外一个线程读取到的该变量的值可能是0、也可能是1,甚至可能是其它数值。如果对上述变量采用voalitle进行修饰,那么上述代码对long型变量的赋值就具有了原子性。因此,其它线程读取到的该变量的值只可能是0或者1。
简单来说,就是一个线程对一个共享变量的更新对于另外一个线程而言不一定是可见的。比如, private boolean isDone=false; 如果有个线程将上面的变量修改为true,那么其它线程可能读取到的值一直是false。如果将上述变量采用volatile修饰,那么一个线程将其值修改后,之后有其它线程来读取该变量的值,后面这个线程总是可以读取到跟新后的变量值。
比如下面的代码: private int nonVoaltileVar=1; private boolean isVarSet=false; private void update(){ nonVoaltileVar=2; isVarSet=true; } 上述代码执行时,由于重排序的结果,一个线程执行update方法后(假设此时再也没有其它线程会去更新上述两个变量的值),其它线程读取到isVarSet的值为true的情况下,它所读取到nonVoaltileVar的值可能仍然是1。这是由于update方法中的两个语句的执行顺序可能被对调(重排序)。而如果我们用voalitle去修饰isVarSet,那么voaltile会禁止其修饰的变量的赋值操作前的操作被重排序到其之后。这样,就保证了其它线程读取到isVarSet的值为true的情况下,nonVoaltileVar的值总是为2。 本书第3章的实战案例代码中有使用volatile关键字,可以参考下。如果要进一步或者更加详细的解释,那要不小的篇幅。深入的理解voaltile关键字涉及到CPU访问内存的机制以及JMM。 viscent老师,您解释的volatile关键字的中的第二点: 接着,如果B线程比C线程先改变volatile变量X, 问题1:那么对于线程C来说,线程C知道volatile变量X的改变吗? 问题2:线程C会更新读取的volatile的X的旧值(线程A改变的)吗? 希望你能解答一下,感激呀! |
|
返回顶楼 | |
发表时间:2015-11-27
viscent老师:
您好! 关注您很久,看您出书了,很是期待啊。 |
|
返回顶楼 | |
发表时间:2015-11-27
看您上面说的,volatile修饰的变量可以保证赋值操作的原子性;保证共享可变变量的可见性及禁止重排序。
这些特性可以保证线程的安全性吗? |
|
返回顶楼 | |
发表时间:2015-11-27
不好意思,我想再补充一点:
要想保证线程的安全性,需要遵循哪些原则呢? |
|
返回顶楼 | |
发表时间:2015-11-28
viscent老师,您好!
1、您前面提到的线程池大小最大不超过2NCPU,想请教一下是怎么算出来的呢?可以提供一下公式吗? 2、多线程的上下文切换及锁争用是需要消耗资源的,想请问一这两种情况都消耗哪些资源,如何计算消耗量多少? 谢谢! |
|
返回顶楼 | |
发表时间:2015-11-28
seandeng888 写道 viscent老师:
您好! 关注您很久,看您出书了,很是期待啊。 感谢你的支持!你是在InfoQ上看到我的文章么? |
|
返回顶楼 | |
发表时间:2015-11-28
什么是ThreadLocal类,怎么使用它? 一般在什么样的场景下可以使用ThreadLocal类?
多线程情况下,怎么结合当前资源(硬件资源,如cpu,内存,IO等,缓层服务、软件负载等)进行有效控制,一般如何考虑和规划这方面的设计? 多线程中的锁如何优化,有没有一些设计原则和需要注意的地方? |
|
返回顶楼 | |
发表时间:2015-11-28
seandeng888 写道 不好意思,我想再补充一点:
要想保证线程的安全性,需要遵循哪些原则呢? 线程安全问题表现在原子性、可见性和重排序这三个方面。而这三个方面既有相关性又有一定的独立性,即保证了其中一个,并不意味着其它方面就不用考虑了。 因此,保证线程安全也是从这三个方面入手。保障了原子性不一定能保障可见性。比如,Java 中的赋值操作本身就具有原子性(当然,如果被赋值变量是long/double的除外)。但是,这种情况下,如果被赋值 的变量是多个线程共享的,我们还要保障可见性。而光保证可见性还不够,如果一个线程对多个共享变量进行更新,我们还需要注意 其它线程看到这些变量被更新的顺序可能和代码中指定的顺序不一样,这就是重排序的问题。 volatile关键字需要注意一点,对于原子性,它仅仅是保障赋值的原子性。 也就是,即便一个变量采用了volatile修饰,如果对其进行赋值的语句中包含该变量自身或者其它变量的值 ,那么这个时候的操作就不具有原子性了。此时,要实现原子性要通过加锁。例如, private volatile int a; a+x;//这个仍然不具备原子性 |
|
返回顶楼 | |