【问题标题】:Create a JUnit that fail if getInstnace() not synchronized in Java Spring implemation of singelton如果 getInstance() 在 Java Spring 的单例实现中未同步,则创建一个失败的 JUnit
【发布时间】:2014-12-19 10:55:24
【问题描述】:

在一次工作回顾期间,我被分配了一项任务,以实现一个玩具 Singelton 数据库和一个用户控制器,该控制器通过使用 Spring 依赖项注入来使用该数据库。 我推出了这个版本:

辛格尔顿数据库

package SingeltonDBVersion2;

import GlobalSetting.User;

public class SingeltonDB {
    private static DBconnImpl db = null;
    private static SingeltonDB singalDb = null;

    private SingeltonDB(String username, String password) {
        db = new DBconnImpl();
    }

    public synchronized static SingeltonDB getInstance(String username,
            String password) throws Exception {

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

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

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

    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);
    }

}

用户控制器

package SingeltonDBVersion2;

import GlobalSetting.IUserContorller;
import GlobalSetting.User;

/****************************************************************************
 * This is class is an implementation of a UserContorller for the version with
 * Spring, because here we use inversion of control. The UserContorller
 * constructor takes the an object of SingeltonDB which we are passing using
 * Spring Dependency Injection in our UnitTests
 *****************************************************************************/
public class UserContorller implements IUserContorller {
    SingeltonDB db;

    public UserContorller(SingeltonDB db) throws Exception {
        this.db = db;
    }

    @Override
    public void createTable(String table) throws Exception {
        db.create(table);

    }

    @Override
    public void saveUser(String table, int id, String name, int age)
            throws Exception {

        db.update(table, new User(id, name, age));
    }

    @Override
    public User getUser(String table, int id) throws Exception {

        return db.query(table, id);
    }

}

spring.xml

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="MyUserContorller" class="SingeltonDBVersion2.UserContorller">
        <constructor-arg ref="MySingeltonDB" />
    </bean>
    <bean id="MySingeltonDB" class="SingeltonDBVersion2.SingeltonDB"
        factory-method="getInstance">
        <constructor-arg value="MyAccount" />
        <constructor-arg value="123" />
    </bean>

</beans>

我可以这样使用:

        ApplicationContext contex = new ClassPathXmlApplicationContext("spring.xml");
        UserContorller user1 = (UserContorller) contex.getBean("MyUserContorller");
        saveUser("table1", 1, Mike, 44); //add user with id 1 and age of 44

现在,我被要求编写一个测试(比如 Junit),如果 SingeltonDB 的 getinstance() 方法不同步,该测试将失败... 我真的不知道该怎么做......

有人可以帮忙提供灵魂吗? 谢谢!

【问题讨论】:

    标签: java spring design-patterns asynchronous junit


    【解决方案1】:

    默认情况下,所有单例 bean 都会在您创建上下文时实例化。所以在你的情况下避免这种情况:

    <bean id="MyUserContorller" class="SingeltonDBVersion2.UserContorller" lazy-init="true">
     ....
    <bean id="MySingeltonDB" class="SingeltonDBVersion2.SingeltonDB" factory-method="getInstance" lazy-init="true">
    

    现在创建多个线程,如下所示:

    class MyThread extends Thread {
         private ApplicationContext context;
         private int hashCode;
         public MyThread(ApplicationContext context) {
             this.context = context;
         }
         public void run() {
             hashCode = System.identityHashCode(context.getBean("MySingeltonDB"));
         }
         public int getHashCode() { 
             return hashCode;
         }
    }
    

    现在创建多个线程实例并一起启动它们,并在哈希码上断言形成每个线程的不相等性。如果你得到平等,那么你的测试就会失败,而你想要什么。

    【讨论】:

    • 谢谢这是一个很好的处理方式,但是我有一个问题...在下面单独写了一条消息。
    【解决方案2】:

    almas shaikh,这是我写的代码:

    package Tests;
    
    import java.util.concurrent.CountDownLatch;
    
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    
    
    public class SingeltonDBVersion2TestSynch {
    
        static ApplicationContext Context;
    
        @Test
        public void testSynch() throws Exception {
    
            int num = 100;
    
            Context = new ClassPathXmlApplicationContext("spring.xml");
    
            CountDownLatch startSignal = new CountDownLatch(1);
            CountDownLatch doneSignal = new CountDownLatch(num);
            MyThread[] threads = new MyThread[num];
            for (int i = 0; i < threads.length; i++) {
                threads[i] = new MyThread(Context, startSignal, doneSignal);
            }
            for (int i = 0; i < threads.length; i++) {
                threads[i].start();
            }
            startSignal.countDown();
            doneSignal.await();
            for (int i = 0; i < threads.length; i++) {
                System.out.println(threads[i].getHashCode());
            }
        }
    }
    
    class MyThread extends Thread {
        private ApplicationContext context;
        private int hashCode;
        CountDownLatch startSignal;
        CountDownLatch doneSignal;
    
        public MyThread(ApplicationContext context, CountDownLatch startSignal,
                CountDownLatch doneSignal) {
            this.context = context;
            this.startSignal = startSignal;
            this.doneSignal = doneSignal;
        }
    
        public void run() {
            try {
                startSignal.await();
                hashCode = System
                        .identityHashCode(context.getBean("MySingeltonDB"));
                doneSignal.countDown();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
    
        }
    
        public int getHashCode() {
            return hashCode;
        }
    }
    

    我只是试图打印哈希码以进行检查...但它们都具有相同的哈希码(我已经更改了 spring xml 并从 getInstacnec() 中删除了同步化的)。 myabe 我在 CountDownLacths 中没有使用正确的方法?

    【讨论】:

    • 首先,您通过在 getInstance 方法中打印来引入一些同步,因为多个线程必须在输出流上进行同步。其次,仅仅运行大量线程并希望某个条件会发生可能是行不通的。如果你想在测试中显示它,你可能必须在测试 null 之后在 getInstance() 中添加一个睡眠,这样就有一个合理的机会,多个线程会到达同一点。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多