如何在单例模式中同步两个方法

时间:2014-12-12 15:30:01

标签: java multithreading synchronization

我根据单例模式创建了一个数据库对象。数据库对象包含两个方法:connect()update()

更新应该运行多线程,这意味着我无法将synchronized放入update方法签名中(我希望用户同时访问它而不是一次访问)。

我的问题是我想确保根据此流程的两个场景:

  1. 线程1(user1)是第一个创建数据库实例,而线程2(user2)正在调用此数据库的connect()update()方法 - 不应该给出NullPointerException即使在user2正在执行update()时,仍未完成来自user1的连接。

  2. update()不应包含synchronized(因为我上面提到的原因)。 感谢所有帮助者!

  3. SingeltonDB

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReentrantLock;
    
        public class SingeltonDB {
            private static DBconnImpl db = null;
            private static SingeltonDB singalDb = null;
              Lock dbLock;
    
            private SingeltonDB(String username, String password) {
                db = new DBconnImpl();
            }
    
            public static boolean isOpen() {
                return (db != null);
            }
    
            public synchronized static SingeltonDB getInstance(String username,
                    String password) throws Exception {
    
                if (db != null) {
                    throw (new Exception("The database is  open"));
                } else {
                    System.out.println("The database is now open");
                    singalDb = new SingeltonDB(username, password);
                }
                db.connect(username, password);
                System.out.println("The database was connected");
    
                return singalDb;
            }
    
            public synchronized static SingeltonDB getInstance() throws Exception {
                if (db == null) {
                    throw (new Exception("The database is not open"));
                }
    
                return singalDb;
            }
    
            public void create(String tableName) throws Exception {
                dbLock = new ReentrantLock();
                dbLock.lock();
                db.create(tableName);
                dbLock.unlock();
            }
    
            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);
            }
    
        }
    

    主要

    public class Main {
        public static void main(String[] args) throws Exception {
    
            Creator cr= new Creator(new UserContorller());
            Thread t1 = new Thread(cr);
            t1.start();
            Producer pr = new Producer(new UserContorller());
            Thread t2 = new Thread(pr);
            t2.start();
    
            /*
             * Consumer cn = new Consumer(new UserContorller()); Thread t2 = new
             * Thread(cn); t2.start();
             */
        }
    }
    
    class Creator implements Runnable {
        UserContorller uc;
    
        public Creator(UserContorller uc) {
            this.uc = uc;
        }
    
        @Override
        public void run() {
            try {
                uc = new UserContorller("MyAccount", "123");
                uc.createTable("table1");
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
    
    class Producer implements Runnable {
        UserContorller uc;
    
        public Producer(UserContorller uc) {
            this.uc = uc;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    uc.saveUser("table1", i, "User", i);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    
    class Consumer implements Runnable {
        UserContorller uc;
    
        public Consumer(UserContorller uc) {
            this.uc = uc;
        }
    
        @Override
        public void run() {
            for (int i = 0; i < 100; i++) {
                try {
                    System.out.println(uc.getUser("table1", i));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    
    }
    

2 个答案:

答案 0 :(得分:1)

连接一次,创建单例时(可能在构造函数中)。 有一个同步静态方法(getInstance或其他),用于检查实例是否存在,根据需要创建连接,并返回实例。通过遵循此协议,您可以确保线程始终获得准备好使用的已连接Db对象。

用户将调用该方法来获取单例实例,并调用update或他们想要的任何内容,它不需要同步。

答案 1 :(得分:1)

注意:以下帖子是以两个用户的角度编写的,使用相同的凭据(隐藏起来)连接到数据库。如果用户使用不同的凭证,单个数据库对象的想法是没有用的,每个用户应该有自己的连接对象,当然然后连接细节通过代表程序中用户的任何内容从用户传递到Db(这里显然是线程实例)。

您提供的实现中的主要问题是getinstance方法要求其调用者知道连接详细信息,或者假设连接已完成。但是,如果已经打开了Db,那么线程既不能也不应该事先知道 并且设计明智的做法是将他们明确地打开它的责任交给他们是错误的。这些线程是工作线程,它们不应该关注Db配置细节。

处理这种情况的唯一理智方法是直接使用Db对象保存这些配置参数,或者更好的是另一个负责提供它的对象(它是factory模式)。

但是,如果您希望首先使用最少的更改来处理代码,请删除参数less getinstance方法,让任何线程要求Db对​​象使用该方法的其余变体,并传递正确的参数,并将其更改为返回实例(如果存在),否则创建它,而不会引发异常。我相信这是@Dima在他的回答中试图解释的内容。