我刚遇到错误类同步的代码:
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
字段未正确封装。作为公共成员,不会阻止外部代码读取它,而是以线程不安全的方式写入它。此代码不应该用作正确的线程安全单例示例,因为它不是它的原因。
答案 0 :(得分:1)
对于上述代码模式,在同步块上使用方法同步是否存在任何缺陷?
从那以后:
public static synchronized void setIfNull(Test newInstance) {
...
}
...与此完全相同(JLS 8.4.3.6):
public static void setIfNull(Test newInstance) {
synchronized (Test.class) {
...
}
}
...您真正要问的是:“在其他类对象WRONG.class
和This.class
上进行同步有什么区别?”。
唯一需要注意的是代码中的其他内容是否决定在Test.class
上进行同步。
答案 1 :(得分:-1)
1)同步方法和块之间的一个显着区别是,同步块通常会减少锁定范围。由于锁定范围与性能成反比,因此最好只锁定关键的代码段。使用synchronized块的最好例子之一是在Singleton模式中进行双重检查锁定,而不是锁定整个getInstance()方法,我们只锁定用于创建Singleton实例的关键代码段。这大大提高了性能,因为锁定只需要一到两次。
2)同步块提供对锁定的精细控制,因为您可以使用任意锁定来为关键部分代码提供互斥。另一方面,如果是静态同步方法,synchronized方法总是锁定此关键字或类级别锁定所代表的当前对象。
3)如果作为参数提供给块的表达式求值为null,则同步块可以抛出抛出java.lang.NullPointerException,这与synchronized方法不同。
4)在同步方法的情况下,当进入方法时,线程获取锁定,当它离开方法时,通常或通过抛出异常来释放锁定。另一方面,在同步块的情况下,线程在进入同步块时获取锁定,在离开同步块时释放。
答案 2 :(得分:-1)
当你同步整个方法时,我无法记住任何陷阱。当然,那些更贵,更贵的#34;只是锁定某些区域。
如果您不确定我会一直使用同步方法,直到您遇到瓶颈为止。
为了避免阻塞错误的对象,只需创建一个实例变量:
private final Object block = new Object();
在需要同步时使用它。无论如何,当你这样做时,请记住,不同线程调用的其他方法不要尊重这一点,你会得到副作用。所以你走这条路时要小心。 我读了很多关于这些主题的书籍,这些书籍确实很难被标记为正确答案。
我建议您阅读" Java Concurrency in Practice"来自Brian Goetz。
另外" Java Core"来自Angelika Langer,Klaus Kreft(这本书在使用volatile关键词时会深入讨论)(德语书籍,仍然很好奇,没有人将其翻译成英语,因为它是该领域的杰作)。
如果您愿意,也可以使用重入锁来获得公平锁定。