包含同步块的实例是否必须是单例?

时间:2017-06-13 12:54:09

标签: java multithreading

我的FileManager公开了一个write方法,其中包含一个synchronized块以防止出现并发问题:

class FileManager{
    Object lock = new Object();

    public void write() {
       synchronized (lock) {
          String id = nextId();
          write(id);
       }
    }
}

但是,如果存在多个FileManager实例,则它们的写入方法仍然可以同时执行。将FileManager作为单例是否可以很好地解决这个可能的并发问题?或者我应该以其他方式使用syncrhonized关键字吗?

3 个答案:

答案 0 :(得分:2)

有几种方法可以做到:

  • 让它成为一个单身,正如你所建议的那样,但这是最不希望的方式(我根本不会推荐它),因为仅仅为同步而制作类单例是一种过度杀戮,而且现在是singeltons气馁。
  • 使您将对象锁定为静态,即static Object lock = new Object();,这样您将始终锁定类级锁定对象。
  • 使write方法成为静态和同步即public static synchronized void write() {,这样再次使用类级锁定对象,但不同的是,这次你将使用{{1的类级别锁定而不是你的锁对象。

其他一些要考虑的要点:

  • 无论您选择类级别锁定还是对象级别锁定,您都可以直接暴露您的锁定(特别是对于类级别锁定),这基本上意味着您的API的某个客户端可以保留锁定以及依赖于您的所有方法对象/类级别锁定将被阻止。例如,在类级别锁定的情况下,API的某个客户端可以执行FileManager,并且所有静态+同步方法都必须等到该客户端已释放锁定,现在如果该客户端正在进行某些密集操作那就是性能损失。
  • 如果您使用某个成员变量进行锁定(例如synchronized(FileManager.class){),那么需要注意的一件事是将其设为private static final Object lock = new Object();,否则它仍然会暴露您的锁定。否则它不会受到我上面提到的问题的困扰。但只有这种方法的另一个原因是 - (1)你不能在方法级别使用它,你通过在private块中包含代码块在方法中使用此锁。 (2.)如果假设您的API的客户端想要与使用此锁的某些同步方法同步,那么他们不能。但是还有更深层次的东西,现在通常这就是锁定发生的方式。
  • 如果您使用的是最新版本的Java,那么有ReentrantLockReadWriteLock等API可为锁定提供更精细的支持。

选择最符合您要求的产品。

在一般注释中,使用synchronizedsynchronized进行锁定是旧的锁定方式,使用Java提供的新并发API,您应该使用ReentrantLock和{{}等API {3}}用于实现同步机制。

答案 1 :(得分:0)

是否使用单例模式取决于各个FileManager实例是否具有自己的状态。如果所有实例基本上共享相同的状态然后是,那么单例模式在这里将是一个不错的选择。另一方面,如果它们可以具有不同的状态,但是您仍然需要跨实例同步方法访问,那么您可以在静态类成员上进行同步,例如:

class FileManager {
    static final Object lock = new Object();
    public void write() {
       synchronized (lock) {
            ...
       }
    }
}

答案 2 :(得分:0)

However, if multiple instances of FileManager exist, their write methods can still be executed concurrently?

Yes. They can run in parallel since lock exists at instance level i.e at object level and not at class level.

Would making FileManager a singleton be a good fix to this possible concurrency issue?

No. Making a class singleton if it had synchronized blocks is not necessary. But if you really need only instance of FileManager as per your design, they you can go for Singleton with static lock.

should I use syncrhonized keyword in another way?

You can use Lock and ReentrantLock as other alternatives.

Refer to below posts for more details:

What does 'synchronized' mean?

Avoid synchronized(this) in Java?

Synchronization vs Lock