调用start方法时,是否需要在java中的run方法内同步方法?

时间:2011-12-02 04:33:32

标签: java multithreading methods runnable

任何人都可以告诉我,如果我是对的吗?我有两个并行运行的线程。

class MyThread extends Thread {

    MyThread() {
    }

    method1() {
    }

    method2() {
    }

    method3() {
    }

    approach(1):

        run() {
            method1();
            method2();
            method3();
        }

    approach(2):

        run() {
            //the code of method1 is here (no method calling)
            //the code of method2 is here (no method calling)
            //the code of method3 is here (no method calling)
        }

}

class Test{
    public static void main(){
        Thread t1 = new Thread();
        t1.start();
        Thread t2 = new Thread();
        t2.start();
    }
}

method1method2method3不访问全局共享数据,但是它们的代码在方法部分中执行局部变量写入,因此我想我不能允许重叠执行在方法部分内。

由此: 在approach(1):我需要让方法(method1method2method3)同步,对吧?

approach(2)中的

:无需同步代码部分,对吧?

如果我在两种方法中都是对的,使用approach(2)会提供更好的表现,对吗?

6 个答案:

答案 0 :(得分:3)

简短回答:您不需要同步。从线程安全角度来看,这两种方法都是等效的。

更长的答案:

退一步并记住同步块的作用可能是值得的。它基本上做了两件事:

  1. 确保如果线程A在对象M上同步的块内,则没有其他线程可以进入在同一对象M上同步的块,直到线程A完成其代码块为止
  2. 确保如果线程A在同步对象M的块中完成了工作,然后完成该块,然后线程B进入也同步的块 M,然后线程B将看到线程A在其同步块内完成的所有操作。这被称为建立发生在之前的关系。
  3. 请注意,synchronized方法只是将方法代码包装在synchronized(this){...}中的简写。

    除了这两件事之外,Java内存模型(JMM)保证在一个线程中,事情会发生,好像它们没有被重新排序一样。 (它们实际上可能因各种原因重新排序,包括效率 - 但不是程序在单个线程中可以注意到的方式。例如,如果你执行“x = 1; y = 2”,编译器可以自由切换y = 2发生在x = 1之前,因为单个线程实际上不能注意到差异。如果多个线程正在访问x和y,那么很可能在没有适当同步的情况下,另一个线程看到y = 2在它看到x = 1之前。)

    所以,回到原来的问题,有几个有趣的笔记。

    首先,由于同步方法是将整个方法放在“synchronized(this){...}”块中的简写,因此t1的方法和t2的方法不会与同一个引用同步,因此不会相对于彼此同步。 t1的方法只与t1对象同步,而t2只与t2同步。换句话说,t1.method1()和t2.method1()同时运行完全没问题。因此,在synchronized关键字提供的这两件事中,第一个(进入块的排他性)是不相关的。事情可能会像:

    1. t1想要输入method1。它需要获取t1监视器,这是不争用的 - 所以它获取它并进入块
    2. T2。想要输入method2。它需要获得11监视器,它不是争用的 - 它获取它并进入块
    3. t1完成方法1并在t1监视器上释放其保留
    4. t2完成method1并在t2监视器上释放它
    5. 至于同步的第二件事(建立发生在之前),使method1()和method2()同步将基本上确保t1.method1()发生在t1.method2()之前。但是由于这两个都发生在同一个线程上(t1线程),JMM无论如何都保证会发生这种情况。

      所以它实际上变得更加丑陋。如果t1和t2确实共享状态 - 也就是说,同步是必要的 - 那么使方法同步将就足够了。请记住,同步方法意味着同步(this){...},因此t1的方法将与t1同步,而t2将与t2同步。你实际上不会在t1的方法和t2之间建立任何先发生过的关系。

      相反,您必须确保在同一参考上同步方法。有很多方法可以做到这一点,但基本上,它必须是对两个线程都知道的对象的引用。

      假设t1和t2都知道相同的参考,LOCK。两者都有类似的方法:

      method1() {
          synchronized(LOCK) {
              // do whatever
          }
      }
      

      现在事情可能会像这样:

      1. t1想要输入method1。它需要获取LOCK监视器,这是不争用的 - 所以它获取它并进入块
      2. t2想要输入method1。它需要获取已被t1保持的LOCK监视器 - 因此t2被置于保持状态。
      3. t1完成方法1并在LOCK监视器上释放其保持
      4. t2现在能够获得LOCK监视器,所以它确实可以,并从方法1的开始
      5. t2完成方法1并释放其在LOCK监视器上的保留

答案 1 :(得分:1)

您说您的方法不访问全局共享数据并且只写本地变量,因此不需要同步它们因为两个线程都有自己的局部变量副本。它们不会重叠或什么的。

在静态/类变量的情况下面临这种问题。如果多个线程试图同时更改静态变量的值,则会出现问题,因此需要进行同步。

答案 2 :(得分:0)

如果您正在调用的方法不写入全局共享数据,则不必同步它们。

在多线程程序中,每个线程都有自己的调用堆栈。每个方法的局部变量在每个线程中都是独立的,不会相互覆盖。

因此,方法1工作正常,不需要同步开销,并且是更好的编程实践,因为它避免了重复的代码。

答案 3 :(得分:0)

线程明确你的确定。方法中的局部变量不在线程之间共享,因为在线程中运行的每个实例都有自己的堆栈。

两种方法之间不会有任何速度提升,它只是更好的代码组织(更简单的方法更容易理解)

如果每个方法都独立于另一个方法,您可能需要考虑它们是否属于同一个类。如果您希望性能增益创建3个不同的类并为每个方法执行多个线程(性能增益取决于可用内核的数量cpu / io ration等。)

答案 4 :(得分:0)

  

因此:在方法(1)中:我需要制作方法(method1,method2   和方法3)同步,对吗?方法(2):没有必要   同步代码部分,对吧?

调用内联方法v / s调用多个方法不会确定方法是否应该同步。我建议您read this,然后要求进一步澄清。

  

如果我在这两种方法中都是正确的,那么使用方法(2)会提供更好的性能,对吗?

以将方法分解为单神方法为代价?当然,但与失去的代码可读性相比,你会看到一个“非常”微小的改进,绝对不推荐。

答案 5 :(得分:0)

方法1,2和3不会同时执行,因此如果它们读/写的变量在运行时没有与其他线程共享,那么就不需要同步,也不需要内联。 / p>

如果他们修改了其他线程在运行的同时读取的数据,那么您需要保护对该数据的访问权限。

如果他们读取其他线程将在其运行的同时写入的数据,那么您需要保护对该数据的访问。

如果期望其他线程读取方法1,2或3修改的数据,则需要使run方法同步(或者在同步块中)以设置门,以便JVM设置memory barrier并确保其他线程在m1,2和3完成后可以看到数据。