在Java中同步单例

时间:2015-10-31 18:02:18

标签: java android

我刚遇到错误类同步的代码:

public class Test
{
    public static volatile Test instance = null;

    public static void setIfNull(Test newInstance)
    {
        synchronized (WRONG.class) // should be synchronized (Test.class)
        {
            if (newInstance == null)
                throw new IllegalArgumentException("newInstance must not be null.");
            if (instance == null) instance = newInstance;
        }
    }
}

如果整个方法同步,则​​不会发生上述错误:

public class Test
{
    public static volatile Test instance = null;

    public static synchronized void setIfNull(Test newInstance)
    {
        if (newInstance == null) 
           throw new IllegalArgumentException("newInstance must not be null.");
        if (instance == null) instance = newInstance;
    }
}

我看到它的方式,第二段代码比第一段更容易出错。

对于上述代码模式,在同步块上使用方法同步是否存在任何缺陷?

警告:上面的代码instance字段未正确封装。作为公共成员,不会阻止外部代码读取它,而是以线程不安全的方式写入它。此代码不应该用作正确的线程安全单例示例,因为它不是它的原因。

3 个答案:

答案 0 :(得分:1)

  

对于上述代码模式,在同步块上使用方法同步是否存在任何缺陷?

从那以后:

public static synchronized void setIfNull(Test newInstance) {
    ...
}

...与此完全相同(JLS 8.4.3.6):

public static void setIfNull(Test newInstance) {
    synchronized (Test.class) {
        ...
    }
}

...您真正要问的是:“在其他类对象WRONG.classThis.class上进行同步有什么区别?”。

唯一需要注意的是代码中的其他内容是否决定在Test.class上进行同步。

答案 1 :(得分:-1)

1)同步方法和块之间的一个显着区别是,同步块通常会减少锁定范围。由于锁定范围与性能成反比,因此最好只锁定关键的代码段。使用synchronized块的最好例子之一是在Singleton模式中进行双重检查锁定,而不是锁定整个getInstance()方法,我们只锁定用于创建Singleton实例的关键代码段。这大大提高了性能,因为锁定只需要一到两次。

2)同步块提供对锁定的精细控制,因为您可以使用任意锁定来为关键部分代码提供互斥。另一方面,如果是静态同步方法,synchronized方法总是锁定此关键字或类级别锁定所代表的当前对象。

3)如果作为参数提供给块的表达式求值为null,则同步块可以抛出抛出java.lang.NullPointerException,这与synchronized方法不同。

4)在同步方法的情况下,当进入方法时,线程获取锁定,当它离开方法时,通常或通过抛出异常来释放锁定。另一方面,在同步块的情况下,线程在进入同步块时获取锁定,在离开同步块时释放。

了解详情:http://java67.blogspot.com/2013/01/difference-between-synchronized-block-vs-method-java-example.html#ixzz3qAc5gOJy

答案 2 :(得分:-1)

当你同步整个方法时,我无法记住任何陷阱。当然,那些更贵,更贵的#34;只是锁定某些区域。

如果您不确定我会一直使用同步方法,直到您遇到瓶颈为止。

为了避免阻塞错误的对象,只需创建一个实例变量:

private final Object block = new Object();

在需要同步时使用它。无论如何,当你这样做时,请记住,不同线程调用的其他方法不要尊重这一点,你会得到副作用。所以你走这条路时要小心。 我读了很多关于这些主题的书籍,这些书籍确实很难被标记为正确答案。

我建议您阅读" Java Concurrency in Practice"来自Brian Goetz。

另外" Java Core"来自Angelika Langer,Klaus Kreft(这本书在使用volatile关键词时会深入讨论)(德语书籍,仍然很好奇,没有人将其翻译成英语,因为它是该领域的杰作)。

如果您愿意,也可以使用重入锁来获得公平锁定。