簡體   English   中英

如何以單例模式同步兩種方法

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

我已經根據單例模式創建了一個數據庫對象。 數據庫對象包含2個方法: connect()update()

該更新應運行在多線程上,這意味着我無法在update方法簽名中放置synchronized (我希望用戶一次不能同時訪問它)。

我的問題是,我要確保根據以下流程確定兩個方案:

  1. 線程1(user1)是第一個創建數據庫實例的線程,線程2(user2)對此數據庫調用connect()update()方法-即使在user2正在執行操作時,也不應提供NullPointerException從user1進行的update()連接未完成。

  2. update()不應包含synchronized (由於上述原因)。 感謝所有的幫手!

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

}

創建單例時,連接一次(也許在構造函數中)。 有一個同步的靜態方法( getInstance或其他方法),該方法檢查實例是否存在, 根據需要創建連接,然后返回該實例。 通過遵循此協議,可以確保線程始終獲得准備使用的已連接Db對象。

用戶將調用該方法來獲取單例實例,並調用update或他們想要在其上進行的任何操作,而無需同步該實例。

注意:下面的帖子是針對兩個用戶使用相同憑據(從他們隱藏)來連接到數據庫的角度而寫的。 如果用戶使用不同的憑據,則單例db對象的想法是沒有目的的,每個用戶都應該有自己的連接對象,然后,當然,連接詳細信息會通過程序中代表用戶的內容從用戶傳遞到Db(此處線程實例)。

您提供的實現中的主要問題是getinstance方法要求其調用者知道連接詳細信息,或者假定連接已經完成。 但是,線程既不能也不應該事先知道是否已經打開了Db-從設計的角度來看,讓他們承擔明確打開它的責任是錯誤的。 這些線程是工作線程,它們不必關心Db配置細節。

處理這種情況的唯一明智的方法是直接將這些配置參數保存在Db對象中,或者最好由另一個負責提供它的對象(這是工廠模式)保存。

但是,如果您希望首先使代碼以最小的更改來工作,請刪除參數較少的getinstance方法,讓任何線程要求Db對​​象使用該方法的其余變體,傳遞正確的參數,然后將其更改為返回實例(如果存在),否則創建它,而不會引發異常。 我相信這就是@Dima一直試圖在他的回答中解釋的內容。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM