java 中静态方法的线程安全

Thread safety in java for static methods

我有以下代码

//编辑:- 使用@Riaz 的回答更新了代码(这段代码现在应该是线程安全的)

public final class MyClass
{
    private static MyClass2 _class2;
    private MyClass()
    {

    }

    public static synchronized  MyClass CreateMyClass1(String arg0 , ArrayList<MyClass3> class3) throws Exception
    {
        MyClass myClass = new MyClass();        
        _class2 = new Class2(arg0 , class3);
        return myClass;
    }

    public static synchronized  MyClass CreateMyClass2(InputStream arg0 , ArrayList<MyClass3> class3) throws Exception
    {
        MyClass myClass = new MyClass();        
        _class2 = new Class2(arg0 , class3);
        return myClass;
    }

    //EDIT :- added a third method that accesses methods of the _class2 object
    public Object getSomething() //don't need synchronized for methods that don't change the state of the object
    { 
       return MyClass._class2.someMethod();
    }

    public synchronized  Object doSomethingToClass2() 
    { 
       //change state of the _class2 variable 
    }
}

我已经阅读了一些解释静态方法的线程安全的帖子,但我有几个问题:

  1. 据我了解,除非两个线程可以改变状态 一个共享的可变对象,我不需要担心线程 安全。 (假设我没有泄露 "this" 参考。)

    那么在使用MyClass时,thread1是否可以调用CreateMyClass1和thread2 调用 CreateMyClass2,这意味着 _class2 变量将 由 thread2 更改,这是我想避免的。遗嘱制作 _class2 作为 volatile 防止这个?如果是,我不确定静态易失性如何 将由 JVM 解释? 这足以使 MyClass 线程安全吗?

  2. 是否在两个静态方法中返回 MyClass class 的对象 导致违反线程安全?

So when using MyClass , can thread1 call CreateMyClass1 and thread2 call CreateMyClass2 which will mean that the _class2 variable will be changed by thread2 which is what i want to avoid.

静态变量在线程之间有效共享。但是,一个线程中的更改不会自动被其他线程看到。将变量声明为 volatile 保证可见性。

I am not sure how static volatile will be interpreted by the JVM?

这里有一段来自 JVM 规范的粘贴,解释了这一切并确认您的理解。

ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.

参数依次为flag name, value, interpretation.

Does returning an object of the MyClass class in both the static methods cause any violation of thread-safety?

根本不在方法范围内实例化对象,它将像处理任何其他对象一样处理。


有关线程安全的更多详细信息可能有用

  • 每个线程都有自己的堆栈。
  • 局部变量被添加到堆栈并自动线程安全。
  • 线程安全的问题是当你试图在它们之间共享数据时,这里不是这种情况。
  • 如果你想在多个线程之间共享简单的值,你可能想看看 atomic variables 而不是使用 synchronized 关键字。
  • 数据静态与否无所谓,只有共享才会出问题
  • synchronized添加到静态方法时,Class对象将被锁定。

static 表示不与包含 class 的实例相关联。这意味着您的所有对象(和静态方法)共享同一个变量。

volatile 只是意味着该值可能会在没有警告的情况下被其他线程更改。

将变量声明为 volatile(无论是否为静态)表明该变量将被多个线程频繁访问。在 Java 中,这归结为指示线程它们不能缓存变量的值,但必须在更改后立即写回,以便其他线程看到更改。 (Java 中的线程默认可以自由缓存变量)。

所以这两个修饰符的效果是完全正交的。您可以将变量初始化为 static volatile

考虑一下各种关键字do/mean;

volatile - 它保证任何读取该字段的线程将看到最多的 up-to-date/recently 写入值(这仅在您有需要共享的可变数据时才有用)

synchronized - 获取同步内容的锁,这样其他线程就无法执行任何操作,直到拥有锁的线程完成其需要的操作

鉴于您的 CreateMyClass1CreateMyClass2 方法都将 _class2 重新分配给 MyClass2 的新实例,不同的线程可能会更改 _class2.

如果您想为每个线程设置一个单独的值,您可以考虑查看 ThreadLocal。也许使用静态 ThreadLocal 可能适合您的需要;

private static ThreadLocal<MyClass> threadLocal = new ThreadLocal<MyClass>();

这将取代 private static MyClass2 _class2;

然后在创建方法中你可以设置每个线程 MyClass2;

public static MyClass CreateMyClass1(String arg0 , ArrayList<MyClass3> class3) throws Exception
{
    Test myClass = new Test();        
    threadLocal.set(new MyClass2(arg0 , class3));
    return myClass;
}

为了获得每个线程 MyClass2,您所要做的就是使用 threadLocal.get() 方法,您将拥有当前线程 MyClass2。这消除了不同线程更改值的担忧,因为每个线程都有自己的值。

希望这对您有所帮助,如果我误解了您的问题,请告诉我:)

这个实现可能是多线程考试或面试问题中的一个很好的坏例子!

在回答您的问题之前,先说明三点:

I.) A final class 无法扩展。它根本不意味着 class 的实例是不可变的。因此,就这里的多线程问题而言,这是一个转移注意力的问题。

II.) CreateMyClass1/2 具有相同的实现,违反了我最喜欢的编码原则不要重复自己 (DRY)。通常你会使用关键字synchronized来防止一个方法被不同的线程同时调用那么你只需要一个方法。 synchronized 关键字防止 _class2 变量被两个线程交错:

public static synchronized MyClass CreateMyClass(InputStream arg0 , ArrayList<MyClass3> class3) 抛出异常{ MyClass myClass = new MyClass();<br> _class2 = new Class2(arg0, class3); return 我的班级; }

III.) 更严格的方法是在 class 变量周围使用同步块,因为您需要保护的唯一共享变量是静态 _class2 变量,并且它是 class 资源。局部变量 myClass 不在单独的线程之间共享,因此您不必担心它被交错:
public static MyClass CreateMyClass(InputStream arg0 , ArrayList<MyClass3> class3) throws Exception { MyClass myClass = new MyClass(); synchronized (MyClass.class){ _class2 = new Class2(arg0 , class3); }; return myClass; }

现在你的问题:
1)

Unless two threads can change the state of a shared mutable object , i don't need to worry about thread safety

  • 正确。

So when using MyClass , can thread1 call CreateMyClass1 and thread2 call CreateMyClass2 which will mean that the _class2 variable will be changed by thread2 which is what i want to avoid. Will making _class2 as volatile prevent this? If yes , I am not sure how static volatile will be interpreted by the JVM? Will this be enough to make MyClass thread-safe?

  • 静态 _class2 变量将被静态方法 CreateMyClass 的后续调用更改。这是 运行 代码的唯一合理结果。相反,如果您想避免交错分配 _class2 以防止它进入错误状态,因为该方法被不同的线程调用,那么是的,volatile 将解决这个问题,并修复您的问题多线程问题(就此片段而言),允许删除同步块:
    public final class MyClass { private static volatile MyClass2 _class2; public static MyClass CreateMyClass(String arg0 , ArrayList<MyClass3> class3) throws Exception { MyClass myClass = new MyClass(); _class2 = new Class2(arg0 , class3); return myClass; } }

但是,随后使用 _class2 的非可变方法的任何其他 MyClass 方法将不是线程安全的。由于 volatile 仅同步 _class2 的赋值。

2)
不,myClass 是一个局部变量。