我应该选择哪种方式使类线程安全?

时间:2018-12-18 04:51:59

标签: java multithreading concurrency thread-safety

我有一个如下的代码

class classA {

   int sum = 0;
   void recursiveMethodA {
       sum = sum +1; // For simplicity
   }
}

基本上,我在递归方法中执行一些操作,该方法运行10次并在类级别更改状态。但是此类不是线程安全的。

现在为了使其线程安全,我在考虑以下选项。

class class B {
    // Public exposed method
    public void methodB {
        ClassA classA = new ClassA();
        classA.recursiveMethodA();
    }
}

我的逻辑是,由于我是在类B的方法内创建类A的对象,因此它是在堆栈而不是堆上创建的,因此它是线程安全的。

这种逻辑正确吗?请提出其他选择。

2 个答案:

答案 0 :(得分:2)

  

我的逻辑是,由于我是在类B的方法内创建类A的对象,因此它是在堆栈而不是堆上创建的,因此它是线程安全的。

您的逻辑和/或术语不正确。实际上,ClassA的实例在堆中。所有常规的Java对象都在堆中创建!

您的代码实际上是线程安全的,但是正确的解释如下:

  1. 对象已创建,并且对其的唯一引用已分配给局部变量。
  2. 通过检查,没有其他线程可以看到该变量。
  3. 通过检查,对象引用永远不会传递给另一个线程;即它不是发布
  4. 点1.,2和3.表示对象在其整个生命周期内都是线程受限 1
  5. 由于对象是线程受限的,因此不必考虑线程安全性。

仅仅说使用局部变量是不够的:

  • 在某些情况下,另一个线程可以看到局部变量的值。例如如果创建了lambda或内部方法闭包并将其传递给另一个线程。

  • 还必须考虑可以将变量中的值传递给另一个线程。

完成和参考处理将在另一个线程上完成。但是,这不应引起线程安全问题。 (JLS规范负责终结,而Reference的javadocs负责引用。在两种情况下,在适当的位置都出现了开头。)


1-这是一种奇特的说法,即没有其他线程可以看到该对象及其引用。


  

您是说classA本身是线程安全的吗?

不。它本身不是线程安全的。在这种特定情况下,它是线程安全的。或更重要的是,在这种情况下,线程安全是无济于事的……因为实例是线程受限的。

  

我不是应该在所有上下文中都将该类定义为线程安全的吗?我不知道明天怎么用?

由您决定!

但是请考虑以下问题:大量标准Java类并非设计成线程安全的。确实,经典情况是StringBuilder,它是StringBuffer的非线程安全重新实现。其他java.util中的大多数集合类型!

基本上,您有两种选择:

  • 使类具有固有的线程安全性,并接受运行时开销。

  • 使类本质上是非线程安全的,并确保仅使用适当地处理该类的方式。 (一种方法是线程限制实例。另一种方法是使用某种形式的外部同步。)

答案 1 :(得分:-3)

拥有线程安全功能的最简单方法是将关键字“ synchronized”放入

synchronized public void method() {
  //do something
}