Java中的线程结构

时间:2014-07-18 11:07:08

标签: java multithreading

我对Java中的线程问题很少。这是代码:

TestingThread类:

public class TestingThread implements Runnable {
    Thread t;
    volatile boolean pause = true;
    String msg;

    public TestingThread() {
        t = new Thread(this, "Testing thread");
    }


    public void run() {
        while (pause) {
            //wait
        }

        System.out.println(msg);
    }

    public boolean isPause() {
        return pause;
    }

    public void initMsg() {
        msg = "Thread death";
    }

    public void setPause(boolean pause) {
        this.pause = pause;
    }

    public void start() {
        t.start();
    }

}

主线程类:

public class Main {
    public static void main(String[] args) {
        TestingThread testingThread = new TestingThread();
        testingThread.start();
        testingThread.initMsg();
        testingThread.setPause(false);


    }
}

问题清单:

  1. tvolatile吗?
  2. msg应该volatile吗?
  3. setPause()synchronized?
  4. 这是一个良好的线程结构的好例子吗?

3 个答案:

答案 0 :(得分:4)

你的第2号问题非常微妙。

非常具体的案例中,您:

  1. 首先从主线程中写msg;
  2. 然后从主线程写出volatile pause;
  3. 然后从子线程中读取volatile pause;
  4. 然后从子线程中读取msg
  5. 因此,您在msg的写入和读取之间已经过渡性地建立了发生之前的关系。因此msg本身不一定是易变的

    然而,在现实生活中的代码中,你应该避免依赖于这种微妙的行为:更好的过度volatile并平静地睡觉。

    以下是Java Language Specification的一些相关引用:

      
        
    • 如果x和y是同一个线程的动作,并且x在程序顺序中位于y之前,那么hb(x,y)。

    •   
    • 如果动作x与后续动作y同步,那么我们也有hb(x,y)。

    •   

    请注意,在我的操作列表中

    • 1按程序顺序排在第2位之前;
    • 3和4相同;
    • 2 3同步。

    至于你的其他问题,

    • ad 1:t不必是volatile,因为它是在创建线程之前编写的,之后从未发生过变化。启动一个线程会导致发生在之前;
    • ad 3:setPause不必同步,因为它只是设置了volatile var。

答案 1 :(得分:2)

<强>&GT; msg应该是不稳定的吗?

是。它是否必须在这个例子中,不是。但是我还是敦促你使用它,因为代码的正确性变得更加清晰;)请注意,我假设我们正在讨论Java 5或更高版本,之后无论如何都要破坏volatile。

要理解的棘手部分是为什么这个例子可以在没有msg被声明为volatile的情况下逃脱。

考虑main()的这个顺序部分。

    testingThread.start();    // starts the other thread

    testingThread.initMsg();  // the other thread may or may not be in the 
                              // while loop by now msg may or may not be 
                              // visible to the testingThread yet
                              // the important thing to note is that either way
                              // testingThread cannot leave its while loop yet

    testingThread.setPause(false);   // after this volatile, all data earlier 
                                     // will be visible to other threads.  
                                     // Thus even though msg was not declared 
                                     // volatile it will piggy back the pauses
                                     // use of volatile; as described [here](http://www.cs.umd.edu/~pugh/java/memoryModel/jsr-133-faq.html#volatile) 

                                     // and now the testingThread can leave 
                                     // its while loop

所以如果我们现在考虑testingThread

    while (pause) {   // pause is volatile, so it will see the change as soon 
                      // as it is made
        //wait
    }

    System.out.println(msg);  // this line cannot be reached until the value 
                              // of pause has been set to false by the main 
                              // method.  Which under the post Java5
                              // semantics will guarantee that msg will 
                              // have been updated too.

<强>&GT;应该是不稳定的?

没关系,但我建议将其作为私人决赛。

<强>&GT; setPause()应该同步吗?

在Java 5之前,然后是。在Java 5读取之后,volatile与进入同步块具有相同的内存屏障。写入易失性具有与同步块结束时相同的内存屏障。因此,除非你需要同步块的范围,在这种情况下你不需要,然后你可以使用volatile。

Java 5中对volatile的更改由更改here的作者记录。

答案 2 :(得分:1)

1和2 易失性可以被视为“变量同步”,虽然方式不同,但结果是相似的,以确保它是读取一致的。

3。 我觉得它不需要,因为this.pause = pause应该是一个原子声明。

4。 如果你把Thread.sleep放在里面,这会导致繁忙的等待,这是一个不好的例子(true){什么都不做},这可能会有所帮助。请参阅http://en.wikipedia.org/wiki/Busy_waiting

执行类似“等到被唤醒”之类的更合适的方法之一是使用监视器对象(java中的Object是监视器对象),或者使用条件对象和锁来执行此操作。您可能需要参考http://docs.oracle.com/javase/7/docs/api/java/util/concurrent/locks/Condition.html

另外,我认为你的自定义Runnable中有一个本地线程文件也不是一个好主意。请参阅Seelenvirtuose的评论。