简体   繁体   English

如何以单例模式同步两种方法

[英]How to synchronize two methods in a singleton pattern

I have created a database object according to the singleton pattern. 我已经根据单例模式创建了一个数据库对象。 The database object contains 2 methods: connect() and update() . 数据库对象包含2个方法: connect()update()

The update should run multithreaded, meaning that I cannot put synchronized in the update method signature (I want users to access it simultaneously not one at a time). 该更新应运行在多线程上,这意味着我无法在update方法签名中放置synchronized (我希望用户一次不能同时访问它)。

My problem is that I want to make sure that 2 scenarios according to this flows: 我的问题是,我要确保根据以下流程确定两个方案:

  1. A thread 1 (user1) is the first to create an instance of the DB and thread 2 (user2) is calling the connect() and update() method to this DB - should not give NullPointerException even if by the time that user2 is doing the update() the connect from user1 is not done. 线程1(user1)是第一个创建数据库实例的线程,线程2(user2)对此数据库调用connect()update()方法-即使在user2正在执行操作时,也不应提供NullPointerException从user1进行的update()连接未完成。

  2. update() should not include synchronized (because of the reason I mentioned above). update()不应包含synchronized (由于上述原因)。 Thanks for all the helpers! 感谢所有的帮手!

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

    }

Main 主要

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

}

Connect once, when creating the singleton (in the constructor, perhaps). 创建单例时,连接一次(也许在构造函数中)。 Have a synchronized static method ( getInstance or something), that checks if an instance exists, creates and connects as necessary, and returns the instance. 有一个同步的静态方法( getInstance或其他方法),该方法检查实例是否存在, 根据需要创建连接,然后返回该实例。 By following this protocol, you ensure that threads always get a connected Db object ready to be used. 通过遵循此协议,可以确保线程始终获得准备使用的已连接Db对象。

The users will call that method to get the singleton instance, and call update or whatever they want on it, it does not need to be synchronized. 用户将调用该方法来获取单例实例,并调用update或他们想要在其上进行的任何操作,而无需同步该实例。

Note: The post below was written in the perspective of both users using the same credentials (hidden from them) to connect to the database. 注意:下面的帖子是针对两个用户使用相同凭据(从他们隐藏)来连接到数据库的角度而写的。 If users employ different credentials, the idea of a singleton db object is purposeless, each user should have their own connection object, and of course then connection details are passed on from the user to the Db via whatever represents the user in the program (here the thread instances apparently). 如果用户使用不同的凭据,则单例db对象的想法是没有目的的,每个用户都应该有自己的连接对象,然后,当然,连接详细信息会通过程序中代表用户的内容从用户传递到Db(此处线程实例)。

The main issue in the implementation you provided is that the getinstance method requires its caller to know the connection details, or assume that the connection has already been done. 您提供的实现中的主要问题是getinstance方法要求其调用者知道连接详细信息,或者假定连接已经完成。 But neither threads could nor should know in advance if the Db has been opened already -- and design wise it's a mistake to hand them the responsibility of explicitely opening it. 但是,线程既不能也不应该事先知道是否已经打开了Db-从设计的角度来看,让他们承担明确打开它的责任是错误的。 These threads are work threads, they shouldn't be concerned about Db configuration details. 这些线程是工作线程,它们不必关心Db配置细节。

The only sane way to handle this situation is to have these configuration parameters held by the Db object directly, or better yet another object in charge of providing it (it's the factory pattern). 处理这种情况的唯一明智的方法是直接将这些配置参数保存在Db对象中,或者最好由另一个负责提供它的对象(这是工厂模式)保存。

However, if you want first your code to work with minimal changes, get rid of the parameter less getinstance method, have any thread requiring the Db object to use the remaining variant of that method, passing along the correct parameters, and change it to return the instance if it exists, or create it otherwise, without raising an exception. 但是,如果您希望首先使代码以最小的更改来工作,请删除参数较少的getinstance方法,让任何线程要求Db对​​象使用该方法的其余变体,传递正确的参数,然后将其更改为返回实例(如果存在),否则创建它,而不会引发异常。 I believe that it's what @Dima has been trying to explain in his answer. 我相信这就是@Dima一直试图在他的回答中解释的内容。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM