用于静态方法的java中的线程安全性

时间:2015-03-11 22:41:24

标签: java multithreading thread-safety 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 as volatile阻止这个?如果是,我不确定静态不稳定 将由JVM解释? 这足以使MyClass线程安全吗?

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

4 个答案:

答案 0 :(得分:1)

  

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

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

  

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

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

ACC_VOLATILE 0x0040 Declared volatile; cannot be cached.

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

  

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

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


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

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

答案 1 :(得分:1)

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

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

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

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

答案 2 :(得分:1)

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

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

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。这消除了不同线程更改值的担心,因为每个线程都有自己的。

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

答案 3 :(得分:1)

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

在回答您的问题之前,有三点:

我。)无法扩展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   因为挥发性阻止这个?如果是,我不确定静态不稳定   将由JVM解释?这足以使MyClass成为现实   线程安全?

  • 通过静态方法CreateMyClass的后续调用,静态_class2变量将被更改。这是运行代码的唯一合理结果。如果您希望避免交错_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是一个局部变量。