2016-04-21 76 views
6

从我所了解下面的代码效率不高:我应尽量避免静态同步方法

class Foo { 
    static Resource resource1; 
    static Resource resource2; 

    static synchronized void methodA() { 
     resource1.add("abc"); 
    } 

    static synchronized void methodB() { 
     resource2.add("abc"); 
    } 
} 

从就我的理解,这两种方法锁定在一个对象(类对象Foo.class ),所以我猜以下是一个很好的优化?

class Foo { 
    static Resource resource1; 
    static Resource resource2; 

    static void methodA() { 
     synchronized(resource1) { 
      resource1.add("abc"); 
     } 
    } 

    static void methodB() { 
     synchronized(resource2) { 
      resource2.add("123"); 
     } 
    } 
} 

只要两个资源不相互依赖。

什么时候应该考虑使用静态同步方法?

回答

7

当你的类抽象化访问单个critical resource时,使用static synchronized构造,因此对类的锁定在语义上是正确的。

如果您的类抽象化访问多个关键资源,那么您必须使用更好的锁定,如您的示例中所示。

您可以将方法的​​修饰符视为语法糖,除了锁定类(或者实例,如果方法不是静态的)之外,没有额外的黑魔法。

在第一个示例中,如果单个类提供对两个不同关键资源的访问(如果它们完全不相关),这是值得怀疑的。也许你可以将关键部分自己移动到资源类。

5

您的优化是正确的。

Foo.class

上两个不同的对象的第二个密码锁的第一码锁:resource1resource2

视觉上,你可以想像这个

首先代码:

Thread 1    Thread 2 
------------------------------ 

Foo.methodA() 

         Foo.methodB() 
// A call to methodB needs to wait for completion of methodA 

二码:

Thread 1    Thread 2 
------------------------------ 

Foo.methodA()   Foo.methodB()  
// At the same time in a machine with at least a dual core 

你应该考虑使用静态同步方法只有当你有一个资源同步。

0

优化是好的,但要注意可能的死锁。在例子中,有时你会决定访问两个资源:

class Foo { 
    static Resource resource1; 
    static Resource resource2; 

    static void methodA() { 
     synchronized(resource1) { 
      resource1.add("abc"); 
      synchronized(resource2) { 
       resource2.add("abc"); 
      } 
     } 
    } 

    static void methodB() { 
     synchronized(resource2) { 
      resource2.add("123"); 
      synchronized(resource1) { 
       resource1.add("123"); 
      } 
     } 
    } 
} 

这会导致死锁:

  1. 线程A试图执行了methodA();
  2. 线程B尝试同时执行methodB();
  3. 线程A获得资源1锁,线程B获得资源2锁定
  4. 线程A试图获得资源2锁定,并开始等待锁释放
  5. 线程B试图获得资源1锁定,并开始等待锁释放
  6. 死锁

为了避免这种情况,你可以让你的资源类是线程安全的:

class Resource { 
    private final Object mLock = new Object(); 
    ... 
    public void add(String str) { 
     synchronized(mLock) { 
      //do stuff 
     } 
    } 
} 
0

第二种方法(锁定对象)是更可取的,因为它可以让您更好地控制锁定发生的时间。更重要的是,它可以防止你的班级的外部参与者无限期地锁定你的班级,阻止你自己的方法执行。

考虑以下几点:试想一下,一些外部代码包含以下语句:

synchronized (Foo.class) { 
    Thread.sleep(10000); 
} 

现在,如果你用在类的方法本身同步与方法1,其他类同时试图调用了methodA或的methodB会被阻止直到睡眠完成。如果你在方法2中使用内部对象的内部锁定,那么其他类不需要等待。

由于上述原因,我不会真的建议方法1.

PS我刚刚注意到,在方法2内的锁并没有声明为final。如果在方法忙于锁定它们时重新分配这些,这将成为一个问题(然后锁将处于不同的实例化中)。为了防止出现这种情况,最好如下所示将它们声明为:

final static Resource resource1 = new Resource(...); 
final static Resource resource2 = new Resource(...); 

总之,从不同步非最终对象。