如果singleton getInstance()方法未同步,Junit测试将失败

Mat*_*toy 3 java junit multithreading synchronization thread-safety

我有这个我建立的singelton数据库,以及我创建的这个Junit测试:

独生子

package SingeltonDBVersion1;

import GlobalSetting.User;

/****************************************************************************
 * This is the SingeltonDB. it warps the object DBconn according to the
 * Singleton pattern. it receive name and password (i.e. DBConn parameters) and
 * if it is the first time that a UserContorll try to get an instance it connect
 * to the database. After that, the DBConn instance will be return to the user.
 *****************************************************************************/
public class SingeltonDB {
    private static DBconnImpl db = null;
    private static SingeltonDB singalDb = null;
    boolean first = true;

    private SingeltonDB(String username, String password) {
        if (first) {
            try {
                System.out.println("first");
                Thread.sleep(5000);

            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            first = false;
        }
        db = new DBconnImpl();
    }

    public static SingeltonDB getInstance(String username, String password)
            throws Exception {
        if (db != null) {
            return singalDb;
        }

        singalDb = new SingeltonDB(username, password);
        System.out.println("The database is now open");
        db.connect(username, password);
        System.out.println("The database was connected");
        return singalDb;
    }

    public void create(String tableName) throws Exception {
        db.create(tableName);
    }

    public User query(String tableName, int userID) throws Exception {
        if (db == null) {
            System.out.println("Error: the database is not open");
            return null;
        }
        return (db.query(tableName, userID));
    }

    public void update(String tableName, User user) throws Exception {
        if (db == null) {
            System.out.println("Error: the database is not open");
            return;
        }
        db.update(tableName, user);
    }

    public void disconnect() throws Exception {
        db.disconnect();
    }

    public static void reset() throws Exception {
        db = null;
    }
}
Run Code Online (Sandbox Code Playgroud)

Junit的

package Tests;

import java.util.concurrent.CountDownLatch;

import org.junit.Test;

import SingeltonDBVersion1.SingeltonDB;


public class SingeltonDBVersion1Tests {

        @Test
        public synchronized void testSynch() throws Exception {
            int num=2;
            CountDownLatch doneSignal = new CountDownLatch(num);

            MySingeltonDB[] instances = new MySingeltonDB[num];
            for (int i = 0; i < num; i++) {
                instances[i]=new MySingeltonDB(doneSignal);

            }
            SingeltonDB.reset();
            for (int i = 0; i < num; i++) {
                instances[i].run();

            }
             doneSignal.await(); 
                for (int i = 0; i < num; i++) { 
                    for (int j = i; j < instances.length; j++) {
                        if (instances[i].getDB()!=instances[j].getDB())
                        {
                            throw (new Exception());
                        }
                    }
                }
        }


    class MySingeltonDB implements Runnable {
        SingeltonDB db;
        CountDownLatch doneSignal;

        public MySingeltonDB(CountDownLatch doneSignal) {
            this.db = null;
            this.doneSignal = doneSignal;
        }

        public SingeltonDB getDB() {
            return db;
        }

        @Override
        public void run() {
            try {
                this.db = SingeltonDB.getInstance("MyAccount", "123");
                doneSignal.countDown();
                System.out.println("----------------------------------------->"+db );
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

    }
}
Run Code Online (Sandbox Code Playgroud)

我在这里要做的是创建2个单例实例,当且仅当singletonDB方法:getInstance()未同步时.但由于某种原因,即使我不在方法中使用synchronized,单例也能正常工作并返回该类的一个实例.

Evg*_*eev 5

尝试我的测试,它重现了问题:

static class Singleton {
    static Singleton i;

    static Singleton getInstance() {
        if (i == null) {
            i = new Singleton();
        }
        return i;
    }
}

public static void main(String[] args) throws Exception {
    Callable<Singleton> c = new Callable<Singleton>() {
        @Override
        public Singleton call() throws Exception {
            return Singleton.getInstance();
        }
    };
    ExecutorService ex = Executors.newFixedThreadPool(2);
    for(;;) {
        Future<Singleton> f1 = ex.submit(c);
        Future<Singleton> f2 = ex.submit(c);
        if (f1.get() != f2.get()) {
            System.out.println("!!!");
        }
        Singleton.i = null;
    }
}
Run Code Online (Sandbox Code Playgroud)