synchorized,sleep 也能达到volatile 线程可见性的目的?

小乞丐 发布于 2016/04/17 11:13
阅读 1K+
收藏 1


package com.test;

import java.util.concurrent.TimeUnit;

public class test1 {
	
	private static boolean is = true;
	public static void main(String[] args) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				int i = 0;
				while(test1.is){
					i++;
					//synchronized 会强制刷新住内存的变量值到线程栈?
					/*synchronized (this) {
					}*/
					//println 是synchronized 的,会强制刷新住内存的变量值到线程栈?
					//System.out.println("1");
					/* 
					 * sleep 会使is的值 在线程栈中失效,从新load 住内存的值? 
					 try {
						TimeUnit.MICROSECONDS.sleep(1);
					} catch (InterruptedException e) {
						e.printStackTrace();  
					}*/
				} 
			}
		}).start();
		 try {
			TimeUnit.SECONDS.sleep(1);
			} catch (InterruptedException e) {
				e.printStackTrace();  
			}
		new Thread(new Runnable() {
			@Override
			public void run() {
				is = false;
				
			}
		}).start();
		
	}
}


如上述代码,在循环中添加synchorized ,println、sleep 都会使循环退出。

是为什么呢?synchronized 会强制刷新住内存的变量值到线程栈?sleep 会干什么呢?? 

请大神解答。。

加载中
0
Ambitor
Ambitor

引用来自“Ambitor”的评论

如上述代码,在循环中添加synchorized ,println、sleep 都会使循环退出。是为什么呢?synchronized 会强制刷新住内存的变量值到线程栈?sleep 会干什么呢?? 请大神解答。。

volatile是会存在线程的副本的,只是每次线程读的时候不从工作内存中读取,直接从主存中读。

所有同步操作synchorized、Lock都必须保证两个要素:1、原子性 2、可见性,所以同步操作相当于操作在主存,而volatile只保证了可见性并禁止指令重排。以及随现在的硬件发展,JVM优化程度已经到了 即便不是volatile修饰的属性 只要不是很复杂的操作 都会马上写到主存中。

至于你说的sleep,它只会释放CPU资源而不会释放对象的锁资源。


回复 @小乞丐 : 的确,static是我曲解了,是在主存中只存在一份,至于为什么不会停止,是因为你一直在for循环没有休息 迭代运算会大量占用CPU,导致CPU没空闲去刷缓存?你可以在i++;后面加上

for(int k=0;k<100000;k++){

 new Object();

}
以上代码没有同步的操作,仅仅是为了不用迭代做CPU运算占用大量CPU,new这么多对象 对比CPU更慢的应该是分配内存的操作,CPU可以空闲
然后程序会停止,正如我前面说的,即便不是volatile修饰的变量也会很快刷到与主存一致
Ambitor
Ambitor
这也是为什么sleep之后程序会停止,因为它让出了CPU执行时间!
0
卧枝会中田
卧枝会中田
......你先看看书吧 多线程可见性是什么意思? volatile  修饰的意思是这个对象 不允许拷贝到线程自己的内存空间操作。这和synchronized  sleep 有什么可比性?!
0
小乞丐
小乞丐

引用来自“卧枝会中田”的评论

......你先看看书吧 多线程可见性是什么意思? volatile  修饰的意思是这个对象 不允许拷贝到线程自己的内存空间操作。这和synchronized  sleep 有什么可比性?!

当然没有可比性,通过上面的例子,只能说明volatile 更加及时 , 指令重排序的作用。

难道你没发现sleep 也会让线程A重新去load 主内存的数据的吗?这和volatile 的可见性有什么区别。

另外 volatile  修饰的意思是这个对象 不允许拷贝到线程自己的内存空间操作,不是这个意思吧。是volatile变量发生改变都会通知其它使用volatile变量的线程,使其变量在线程栈中失效并重新去load这个变量到工作空间。

你应该在去好好看看volatile 变量到底在干什么。

0
xpbob
xpbob
synchronized可以,用这个关键字本来就保证了两个属性,可见性和原子性,只要在synchronized块里一定会从内存取值,sleep java好像没有明确指出有这个功效,但是理论上并没有这个功能,因为sleep不会让出CPU使用权,所以cpu寄存器里的值应该没有被清除
0
行业协汇袁斌
行业协汇袁斌

sleep是释放cpu占用的(但是不释放锁)...

这时下面那个thread就有机会启动了.

置is = false, 退出.

控制权回到上一个线程循环继续,

is=true, 循环结束...

和sleep的作用类似, println(I/O), synchronize(临界调度)都可以暂时释放cpu占用

导致其他的线程有一线喘息的机会设置is.

这个和volatile什么的变量在哪里没什么关系, 因为你在循环中只是反复读取而不是交替读写.

这种情况下jvm应该是不会优化这个类变量到寄存器的.可以说大多数情况下, 作为堆变量都不会被优化到寄存器的.这个是猜测, 没读过bytecode不好100%确定.

0
景愿
景愿
这个涉及到JVM底层实现,估计要找JVM真专家才能解答了
0
小乞丐
小乞丐

引用来自“IT老太爷”的评论

sleep是释放cpu占用的(但是不释放锁)...

这时下面那个thread就有机会启动了.

置is = false, 退出.

控制权回到上一个线程循环继续,

is=true, 循环结束...

和sleep的作用类似, println(I/O), synchronize(临界调度)都可以暂时释放cpu占用

导致其他的线程有一线喘息的机会设置is.

这个和volatile什么的变量在哪里没什么关系, 因为你在循环中只是反复读取而不是交替读写.

这种情况下jvm应该是不会优化这个类变量到寄存器的.可以说大多数情况下, 作为堆变量都不会被优化到寄存器的.这个是猜测, 没读过bytecode不好100%确定.

这个意思是,只要当前线程让出了CPU,后续再次被cpu调度的时候,就会重新load is变量吗?

那么就算我在while中什么都不做,是一个死循环,也会让出cpu的呀,cpu 是不会让某个线程长期独占的,那理论上也应该会退出循环呀,所以 应该不是因为释放了cpu的占用,导致线程会重新load 主内存的is变量值。

但是具体是什么原因,我也不清楚。

行业协汇袁斌
行业协汇袁斌
骚年你用线程是不是不多啊... 简单的死循环是会吃死cpu的... 同一个虚拟机下其他的线程很难抢到cpu... 你说的那个再次被调度就是load is我猜是的,因为它是static的, 而且是堆变量,jvm应该会重新load...
0
小乞丐
小乞丐

引用来自“IT老太爷”的评论

sleep是释放cpu占用的(但是不释放锁)...

这时下面那个thread就有机会启动了.

置is = false, 退出.

控制权回到上一个线程循环继续,

is=true, 循环结束...

和sleep的作用类似, println(I/O), synchronize(临界调度)都可以暂时释放cpu占用

导致其他的线程有一线喘息的机会设置is.

这个和volatile什么的变量在哪里没什么关系, 因为你在循环中只是反复读取而不是交替读写.

这种情况下jvm应该是不会优化这个类变量到寄存器的.可以说大多数情况下, 作为堆变量都不会被优化到寄存器的.这个是猜测, 没读过bytecode不好100%确定.

引用来自“小乞丐”的评论

这个意思是,只要当前线程让出了CPU,后续再次被cpu调度的时候,就会重新load is变量吗?

那么就算我在while中什么都不做,是一个死循环,也会让出cpu的呀,cpu 是不会让某个线程长期独占的,那理论上也应该会退出循环呀,所以 应该不是因为释放了cpu的占用,导致线程会重新load 主内存的is变量值。

但是具体是什么原因,我也不清楚。

while(true) 先不管cpu会不会死,cpu一定会让while的线程挂起,可能当前所有活动线程中,并没有比while线程更加需要执行的线程,那么cpu会再次执行while线程。

那么这就意味着这个while线程会在次load is 变量的值?

那么不加sleep 或者 synchorized 也会退出循环。

所以我认为应该不是因为cpu 让当前线程挂起,再次执行当前线程 就会load 主内存的变量 到 线程栈。

0
Ambitor
Ambitor
如上述代码,在循环中添加synchorized ,println、sleep 都会使循环退出。是为什么呢?synchronized 会强制刷新住内存的变量值到线程栈?sleep 会干什么呢?? 请大神解答。。

volatile是会存在线程的副本的,只是每次线程读的时候不从工作内存中读取,直接从主存中读。

所有同步操作synchorized、Lock都必须保证两个要素:1、原子性 2、可见性,所以同步操作相当于操作在主存,而volatile只保证了可见性并禁止指令重排。以及随现在的硬件发展,JVM优化程度已经到了 即便不是volatile修饰的属性 只要不是很复杂的操作 都会马上写到主存中。

至于你说的sleep,它只会释放CPU资源而不会释放对象的锁资源。


小乞丐
小乞丐
如你所说、线程B修改变量is后、操作的是主内存、那线程A的while循环 应该读取的也是主内存的is变量、那么当is为false时、线程A循环中什么也不做、应该会跳出循环才对、但事实缺没有、说明线程A没有读取主内存中的is、
返回顶部
顶部