本帖最后由 carrd2008 于 2013-06-04 22:54:02 编辑

解决方案 »

  1.   

            TestWorks work = new TestWorks();
            new Thread(new TestVolatile(work, 1)).start();
            new Thread(new TestVolatile(work, 2)).start(); 你这里一个TestWorks对象被两个线程所共享,当然线程1对isWorkEnable变量的修改会影响到线程2了
      

  2.   


    楼上,你看一下我写的蓝色字体的那两条。
    那是我从其他资料中看到的。
    上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?
    线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么volatile修饰符又有什么用?
    能详细帮我解答一下吗?
    谢谢!~
      

  3.   

    楼上正解,不信可以把 TestWorks work = new TestWorks();放到run()方法里去,这样每个线程就各有一个TestWorks对象,互不影响。
      

  4.   

    亲!run()方法执行完线程就结束了,接下来看看蓝色的第二条,你懂得!!
      

  5.   


    楼上,你看一下我写的蓝色字体的那两条。
    那是我从其他资料中看到的。
    上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?
    线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么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
      

  6.   


    这种描述不够准确,这是为了优化执行效率才会进行的动作。此外,这种例子一般需要让JVM进行极端优化,至少考虑增加: -server 运行参数。最后,程序中存在sleep()、IO类操作,也即导致线程需要切换上下文的时候,都很可能引发内存的同步。所以最好的例子,就是比较干净的死循环,可以参加:《JAVA并发编程实践》这本书,写的比较不错。
      

  7.   

    当线程引用volatile字段时,会发生从主存储器到工作存储器的拷贝。
    将指定的值写给volatile字段时,工作存储器的内容会映像到主存储器。没有出现问题并不代表代码没有问题,一旦出了问题就让人崩溃。
      

  8.   

    这里有个例子可以说明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 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
    可以研究一下这个例子,可以说明一定的问题哦。
      

  9.   

    楼主那段蓝色的不知道从哪看到了,确实像八楼说的,那段描述不准确,内存的同步不是在线程结束后才进行了,
    21世纪了,除非是因为性能问题主动的自愿的构造这么一个环境,否则java要是默认这样处理内存早就被淘汰了如果你对这方面感兴趣ifeve上几篇文章可以看看 比如说这一篇同步和Java内存模型 (三)可见性
      

  10.   


    楼上,你看一下我写的蓝色字体的那两条。
    那是我从其他资料中看到的。
    上面不是说,线程对变量的操作只是针对工作内存中的拷贝的吗?
    线程既然还没结束,怎么会影响到主存中的变量?如果我对两个线程传入同一个引用就可以实现线程间的通信,那么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的回复,看了你的说明,我终于弄明白了我的问题。
      

  11.   


    这种描述不够准确,这是为了优化执行效率才会进行的动作。此外,这种例子一般需要让JVM进行极端优化,至少考虑增加: -server 运行参数。最后,程序中存在sleep()、IO类操作,也即导致线程需要切换上下文的时候,都很可能引发内存的同步。所以最好的例子,就是比较干净的死循环,可以参加:《JAVA并发编程实践》这本书,写的比较不错。谢谢ldh911的回复,你的帮助对我非常有用。
    我对内存模型的理解又深入了一点。
      

  12.   


    感谢shnulaa给出的例子。
    其实我也觉得我写的例子估计是有问题的,那么怎么写一个例子能正确验证volatile的功能?
    这也很头疼。
    我去试试你的例子。
      

  13.   

    [线程2]调用的stop()方法修改完isWorkEnable 的值后,线程就结束了,线程结束前会把isWorkEnable 的值更新到主存。因此新的isWorkEnable值对 线程1可见