繁体   English   中英

用于静态方法的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将如何解释静态volatile? 这足以使MyClass线程安全吗?

  2. 在两个静态方法中返回MyClass类的对象是否会导致任何违反线程安全的行为?

因此,当使用MyClass时,thread1可以调用CreateMyClass1,而thread2调用CreateMyClass2,这意味着_class2变量将被thread2更改,这是我想要避免的。

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

我不确定JVM如何解释静态volatile?

这里有一个来自JVM规范的粘贴,可以解释所有内容并确认您的理解。

ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.

在各自的顺序中,参数是标志名称,值,解释。

在两个静态方法中返回MyClass类的对象是否会导致任何违反线程安全的行为?

根本不在方法范围中实例化对象,它将作为任何其他对象处理。


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

  • 每个线程都有自己的堆栈。
  • 局部变量被添加到堆栈并自动线程安全。
  • 线程安全问题是当您尝试在它们之间共享数据时,这不是这种情况。
  • 如果要在多个线程之间共享简单值,可能需要查看原子变量而不是使用synchronized关键字。
  • 无论数据是静态的还是没有差别,只有在共享时才会出现问题。
  • synchronized添加到静态方法时,将锁定Class对象。

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

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

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

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

考虑各种关键词的含义/含义;

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

synchronized - 获取对synchronized的锁定,以便其他线程无法执行任何操作,直到具有锁定的线程完成所需的操作

鉴于您的CreateMyClass1CreateMyClass2方法均重新分配_class2到的一个新实例MyClass2有可能不同的线程改变_class2

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

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

这将取代private static MyClass2 _class2;

然后在create方法中你可以设置每个线程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.) final堂课不能延期。 它根本不暗示类的实例是不可变的。 所以这是多线程的问题,这是一个红色的鲱鱼。

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

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

III。)更严格的方法是在类变量周围使用synchronized块,因为你需要保护的唯一共享变量是静态_class2变量,它是一个类资源。 局部变量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)

除非两个线程可以更改共享可变对象的状态,否则我不需要担心线程安全

  • 正确。

因此,当使用MyClass时,thread1可以调用CreateMyClass1,而thread2调用CreateMyClass2,这意味着_class2变量将被thread2更改,这是我想要避免的。 将_class2设为volatile会阻止这种情况吗? 如果是,我不确定JVM将如何解释静态volatile? 这足以使MyClass线程安全吗?

  • static _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是一个局部变量。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM