单例上的同步访问

时间:2015-06-07 09:46:53

标签: java multithreading synchronization singleton

我对单身人士的同步有疑问。假设我有一个像这样的单例模式:

public class Singleton {

    private final Object obj;

    private static Singleton instance;

    private Singleton(Object obj) {
        this.obj = obj;
    }

    public static synchronized Singleton getInstance(Object obj) {
        if (instance == null) {
            instance = new Singleton(obj);
        }
        return instance;
    }

    public synchronized void methodA() {
        // ...
    }

    public synchronized void methodB() {
        // ...
    }
}

我想同步它的访问权限。我有两个假设,我需要验证。

  1. 假设: 由于所有方法都在同步,包括初始化程序,因此Singleton上的每次访问都是线程安全和同步的。

  2. 假设: 当我想确保一个线程想要调用methodA()然后立即methodB()而没有另一个线程调用单例的方法时,在这样的实例上同步是否正确?

  3. Singleton singleton = Singleton.getInstance(obj);
    
    synchronized (singleton) {
        singleton.methodA();
        singleton.methodB();
    }
    

    说明:

    1.假设是否正确,因为非静态方法上的synchronized在对象本身上同步,并且因为它始终是同一个对象,所以访问是同步的?对getInstance(obj)的调用会在类本身上同步吗?

    2.假设是否正确,因为getInstance(obj)每个线程获得相同的对象,因此同步是正确的,因为另一个线程将等待同步块(...methodA(); methodB();)退出?

6 个答案:

答案 0 :(得分:1)

假设我理解正确,您的假设是正确的。我只想指出,在大多数情况下,您可以使用更简单的单例模式:

public class Singleton {

    private static Singleton instance = new Singleton();

    private Singleton() {
    }

    public static Singleton getInstance() {
        return instance;
    }
}

该字段将以线程安全的方式在第一个静态引用(例如getInstance())上初始化,而无需显式同步。

此外,instance应为final

答案 1 :(得分:0)

您的第一个假设是正确的,但是,通过专用锁定对象而不是使用synchronized方法进行同步是一种很好的做法。这样做可以将并发问题排除在API之外,并允许您稍后从 java.util.concurrent 包中替换更有效的实现。您还会阻止deadlocks,因为其他对象无法获取相同的锁定对象。

public class Example {

  // leaving out the singleton aspect here

  // consider java.util.concurrent.locks instead
  private Object lock = new Object();

  public synchronized void methodA() {
    synchronized(lock) {
      // ...
    }
  }

  public synchronized void methodB() {
    synchronized(lock) {
      // ...
    }
  }
}

客户端仍然可以在实例上进行同步:

synchronized(instance) {
  instance.methodA();
  instance.methodB();
}

例如,查看SynchronizedCollection类中java.util.Collections的实现,它也使用内部锁对象。

关于在单例上进行同步的一个问题:如果应用程序中有很多并发线程(例如,在Web应用程序中),则此中央锁定很快就会成为性能瓶颈,因为所有线程都必须等待彼此

答案 2 :(得分:0)

两者都是真的。

测试它的最佳方法(测试解决方案总是好的,而不是依赖于假设)将在调用方法之间等待一段时间,并从不同的线程调用methodA,然后检查执行顺序。例如:

public synchronized void methodA() {
    println("methodA");
}

public synchronized void methodB() {
    println("methodB");
}

然后,在测试用例或主要方法中:

new Thread(){
    public void run(){
           Singleton singleton = Singleton.getInstance();

           synchronized (singleton) {
           singleton.methodA();
           singleton.wait(5000L);//this gives us 5 seconds to call it from different thread
           singleton.methodB();
       } 
    }
}.start();

new Thread(){
    public void run(){
           Singleton.getInstance().methodA();
       } 
    }
}.start();

答案 3 :(得分:0)

如你所知,synchronized关键字用于获取锁定  宾语。如果你想在执行后调用你愿意的方法  一种方法。然后我们可以从同步方法调用方法  在代码之下,但Thread在它出来之前不会发布  第一个同步块。

intertools

这里Thread将获取锁定进入同步method1A at    最后它将调用另一个synchronized方法1B。线程是    在完成method1A执行后将释放锁。

答案 4 :(得分:0)

你们两个假设都是真的。但是,为了创建Singleton,我将建议以下方法,它是100%线程安全且没有同步问题,因为它利用了Java的类加载机制。

就像这个类一样,Provider只会加载一次,因此只存在一个Network类实例,并且没有两个线程可以在同一个JVM中创建2个实例,因为类不会被加载两次。

public class Network {
    private Network(){

    }

    private static class Provider {
        static final Network INSTANCE = new Network();
    }

    public static Network getInstance() {
        return Provider.INSTANCE;
    }
    //More code...
}

答案 5 :(得分:0)

线程安全单例样本;

public class Singleton {

        private static Singleton instance;

        private Singleton(){

        }

        public static Singleton getInstance(){
            synchronized (Singleton.class){
                if(instance == null){
                    instance = new Singleton();
                }
            }
            return instance;
        }
    }