Java多线程 - 线程间通信问题,大家进来看看。 本帖最后由 carrd2008 于 2013-06-04 22:54:02 编辑 解决方案 » 免费领取超大流量手机卡,每月29元包185G流量+100分钟通话, 中国电信官方发货 TestWorks work = new TestWorks(); new Thread(new TestVolatile(work, 1)).start(); new Thread(new TestVolatile(work, 2)).start(); 你这里一个TestWorks对象被两个线程所共享,当然线程1对isWorkEnable变量的修改会影响到线程2了 楼上,你看一下我写的蓝色字体的那两条。那是我从其他资料中看到的。上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么volatile修饰符又有什么用?能详细帮我解答一下吗?谢谢!~ 楼上正解,不信可以把 TestWorks work = new TestWorks();放到run()方法里去,这样每个线程就各有一个TestWorks对象,互不影响。 亲!run()方法执行完线程就结束了,接下来看看蓝色的第二条,你懂得!! 楼上,你看一下我写的蓝色字体的那两条。那是我从其他资料中看到的。上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么volatile修饰符又有什么用?能详细帮我解答一下吗?谢谢!~你理解的没问题,上面写的代码还是有问题的。当一个线程欲引用字段的值时,会一次将值从主存储器拷贝到工作存储器上(read->load操作),通过此拷贝得到的值是工作拷贝,拷贝完成以后线程就会使用工作拷贝。当同一线程再度引用同一字段值,线程或许会引用刚才的所制作的工作拷贝,也可能会重新从主存储器拷贝到工作存储器,然后才引用工作拷贝(read->load->use),具体会出现哪一种状况,是由java执行处理系统所决定的。指定(赋值)某个字段时,也是同样的道理,指定有可能只针对工作拷贝,也有可能在每次指定字段值以后,拷贝到主存储器(assign->store->write),会执行哪个操作,是由java执行处理系统所决定的。你这里的代码虽然没有出现问题,但并不代码就不会出现问题,所以isWorkEnable需要用volatile来修饰,它的作用是:1.进行内存的同步2.以atomic的方式来进行long和double的指定。关于volatile用法的文章见此:http://www.ibm.com/developerworks/cn/java/j-jtp06197.html 这种描述不够准确,这是为了优化执行效率才会进行的动作。此外,这种例子一般需要让JVM进行极端优化,至少考虑增加: -server 运行参数。最后,程序中存在sleep()、IO类操作,也即导致线程需要切换上下文的时候,都很可能引发内存的同步。所以最好的例子,就是比较干净的死循环,可以参加:《JAVA并发编程实践》这本书,写的比较不错。 当线程引用volatile字段时,会发生从主存储器到工作存储器的拷贝。将指定的值写给volatile字段时,工作存储器的内容会映像到主存储器。没有出现问题并不代表代码没有问题,一旦出了问题就让人崩溃。 这里有个例子可以说明Volatile的问题public class VolatileTest { private volatile static int MY_INT = 0; public static void main(String[] args) { new ChangeListener().start(); new ChangeMaker().start(); } static class ChangeListener extends Thread { @Override public void run() { int local_value = MY_INT; while (local_value < 5) { if (local_value != MY_INT) { System.out.println("Got Change for MY_INT :" + MY_INT); local_value = MY_INT; } } } } static class ChangeMaker extends Thread { @Override public void run() { int local_value = MY_INT; while (MY_INT < 5) { System.out.println("Incrementing MY_INT to " + (local_value + 1)); MY_INT = ++local_value; try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } } } }}在加上volatile执行结果Incrementing MY_INT to 1Got Change for MY_INT :1Incrementing MY_INT to 2Got Change for MY_INT :2Incrementing MY_INT to 3Got Change for MY_INT :3Incrementing MY_INT to 4Got Change for MY_INT :4Incrementing MY_INT to 5Got Change for MY_INT :5去掉执行结果Incrementing MY_INT to 1Incrementing MY_INT to 2Incrementing MY_INT to 3Incrementing MY_INT to 4Incrementing MY_INT to 5记得vm 参数加上-server可以研究一下这个例子,可以说明一定的问题哦。 楼主那段蓝色的不知道从哪看到了,确实像八楼说的,那段描述不准确,内存的同步不是在线程结束后才进行了,21世纪了,除非是因为性能问题主动的自愿的构造这么一个环境,否则java要是默认这样处理内存早就被淘汰了如果你对这方面感兴趣ifeve上几篇文章可以看看 比如说这一篇同步和Java内存模型 (三)可见性 楼上,你看一下我写的蓝色字体的那两条。那是我从其他资料中看到的。上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么volatile修饰符又有什么用?能详细帮我解答一下吗?谢谢!~你理解的没问题,上面写的代码还是有问题的。当一个线程欲引用字段的值时,会一次将值从主存储器拷贝到工作存储器上(read->load操作),通过此拷贝得到的值是工作拷贝,拷贝完成以后线程就会使用工作拷贝。当同一线程再度引用同一字段值,线程或许会引用刚才的所制作的工作拷贝,也可能会重新从主存储器拷贝到工作存储器,然后才引用工作拷贝(read->load->use),具体会出现哪一种状况,是由java执行处理系统所决定的。指定(赋值)某个字段时,也是同样的道理,指定有可能只针对工作拷贝,也有可能在每次指定字段值以后,拷贝到主存储器(assign->store->write),会执行哪个操作,是由java执行处理系统所决定的。你这里的代码虽然没有出现问题,但并不代码就不会出现问题,所以isWorkEnable需要用volatile来修饰,它的作用是:1.进行内存的同步2.以atomic的方式来进行long和double的指定。关于volatile用法的文章见此:http://www.ibm.com/developerworks/cn/java/j-jtp06197.html谢谢dr8737010的回复,看了你的说明,我终于弄明白了我的问题。 这种描述不够准确,这是为了优化执行效率才会进行的动作。此外,这种例子一般需要让JVM进行极端优化,至少考虑增加: -server 运行参数。最后,程序中存在sleep()、IO类操作,也即导致线程需要切换上下文的时候,都很可能引发内存的同步。所以最好的例子,就是比较干净的死循环,可以参加:《JAVA并发编程实践》这本书,写的比较不错。谢谢ldh911的回复,你的帮助对我非常有用。我对内存模型的理解又深入了一点。 感谢shnulaa给出的例子。其实我也觉得我写的例子估计是有问题的,那么怎么写一个例子能正确验证volatile的功能?这也很头疼。我去试试你的例子。 [线程2]调用的stop()方法修改完isWorkEnable 的值后,线程就结束了,线程结束前会把isWorkEnable 的值更新到主存。因此新的isWorkEnable值对 线程1可见 五子棋项目 重绘不出来 java 字符串 比较,找出不同部分 算法 Connection reset 5天了搞不定 怎末总是编译不出来,老是出错? Google校园招聘的面试题目 新人报道 求JBX中文教程!另外还有100分加送! 为什么会出现这种结果? 最简单的小Applet,在jb中可以运行,离开JB环境后在IE和DOS中就不能运行的问题? Collections.sort多个字段排序为什么没有按预期排? 请问何谓【框架】? 如何用java代码 获取打印机列表
new Thread(new TestVolatile(work, 1)).start();
new Thread(new TestVolatile(work, 2)).start(); 你这里一个TestWorks对象被两个线程所共享,当然线程1对isWorkEnable变量的修改会影响到线程2了
楼上,你看一下我写的蓝色字体的那两条。
那是我从其他资料中看到的。
上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?
线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么volatile修饰符又有什么用?
能详细帮我解答一下吗?
谢谢!~
楼上,你看一下我写的蓝色字体的那两条。
那是我从其他资料中看到的。
上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?
线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么volatile修饰符又有什么用?
能详细帮我解答一下吗?
谢谢!~你理解的没问题,上面写的代码还是有问题的。
当一个线程欲引用字段的值时,会一次将值从主存储器拷贝到工作存储器上(read->load操作),通过此拷贝得到的值是工作拷贝,拷贝完成以后线程就会使用工作拷贝。
当同一线程再度引用同一字段值,线程或许会引用刚才的所制作的工作拷贝,也可能会重新从主存储器拷贝到工作存储器,然后才引用工作拷贝(read->load->use),具体会出现哪一种状况,是由java执行处理系统所决定的。指定(赋值)某个字段时,也是同样的道理,指定有可能只针对工作拷贝,也有可能在每次指定字段值以后,拷贝到主存储器(assign->store->write),会执行哪个操作,是由java执行处理系统所决定的。你这里的代码虽然没有出现问题,但并不代码就不会出现问题,所以isWorkEnable需要用volatile来修饰,它的作用是:
1.进行内存的同步
2.以atomic的方式来进行long和double的指定。关于volatile用法的文章见此:
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html
这种描述不够准确,这是为了优化执行效率才会进行的动作。此外,这种例子一般需要让JVM进行极端优化,至少考虑增加: -server 运行参数。最后,程序中存在sleep()、IO类操作,也即导致线程需要切换上下文的时候,都很可能引发内存的同步。所以最好的例子,就是比较干净的死循环,可以参加:《JAVA并发编程实践》这本书,写的比较不错。
将指定的值写给volatile字段时,工作存储器的内容会映像到主存储器。没有出现问题并不代表代码没有问题,一旦出了问题就让人崩溃。
new ChangeListener().start();
new ChangeMaker().start();
} static class ChangeListener extends Thread {
@Override
public void run() {
int local_value = MY_INT;
while (local_value < 5) {
if (local_value != MY_INT) {
System.out.println("Got Change for MY_INT :" + MY_INT);
local_value = MY_INT;
}
}
}
} static class ChangeMaker extends Thread {
@Override
public void run() { int local_value = MY_INT;
while (MY_INT < 5) {
System.out.println("Incrementing MY_INT to " + (local_value + 1));
MY_INT = ++local_value;
try {
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}在加上volatile执行结果Incrementing MY_INT to 1
Got Change for MY_INT :1
Incrementing MY_INT to 2
Got Change for MY_INT :2
Incrementing MY_INT to 3
Got Change for MY_INT :3
Incrementing MY_INT to 4
Got Change for MY_INT :4
Incrementing MY_INT to 5
Got Change for MY_INT :5
去掉执行结果Incrementing MY_INT to 1
Incrementing MY_INT to 2
Incrementing MY_INT to 3
Incrementing MY_INT to 4
Incrementing MY_INT to 5
记得vm 参数加上-server
可以研究一下这个例子,可以说明一定的问题哦。
21世纪了,除非是因为性能问题主动的自愿的构造这么一个环境,否则java要是默认这样处理内存早就被淘汰了如果你对这方面感兴趣ifeve上几篇文章可以看看 比如说这一篇同步和Java内存模型 (三)可见性
楼上,你看一下我写的蓝色字体的那两条。
那是我从其他资料中看到的。
上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?
线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么volatile修饰符又有什么用?
能详细帮我解答一下吗?
谢谢!~你理解的没问题,上面写的代码还是有问题的。
当一个线程欲引用字段的值时,会一次将值从主存储器拷贝到工作存储器上(read->load操作),通过此拷贝得到的值是工作拷贝,拷贝完成以后线程就会使用工作拷贝。
当同一线程再度引用同一字段值,线程或许会引用刚才的所制作的工作拷贝,也可能会重新从主存储器拷贝到工作存储器,然后才引用工作拷贝(read->load->use),具体会出现哪一种状况,是由java执行处理系统所决定的。指定(赋值)某个字段时,也是同样的道理,指定有可能只针对工作拷贝,也有可能在每次指定字段值以后,拷贝到主存储器(assign->store->write),会执行哪个操作,是由java执行处理系统所决定的。你这里的代码虽然没有出现问题,但并不代码就不会出现问题,所以isWorkEnable需要用volatile来修饰,它的作用是:
1.进行内存的同步
2.以atomic的方式来进行long和double的指定。关于volatile用法的文章见此:
http://www.ibm.com/developerworks/cn/java/j-jtp06197.html谢谢dr8737010的回复,看了你的说明,我终于弄明白了我的问题。
这种描述不够准确,这是为了优化执行效率才会进行的动作。此外,这种例子一般需要让JVM进行极端优化,至少考虑增加: -server 运行参数。最后,程序中存在sleep()、IO类操作,也即导致线程需要切换上下文的时候,都很可能引发内存的同步。所以最好的例子,就是比较干净的死循环,可以参加:《JAVA并发编程实践》这本书,写的比较不错。谢谢ldh911的回复,你的帮助对我非常有用。
我对内存模型的理解又深入了一点。
感谢shnulaa给出的例子。
其实我也觉得我写的例子估计是有问题的,那么怎么写一个例子能正确验证volatile的功能?
这也很头疼。
我去试试你的例子。