[英]How to synchronize two methods in a singleton pattern
我已經根據單例模式創建了一個數據庫對象。 數據庫對象包含2個方法: connect()
和update()
。
該更新應運行在多線程上,這意味着我無法在update
方法簽名中放置synchronized
(我希望用戶一次不能同時訪問它)。
我的問題是,我要確保根據以下流程確定兩個方案:
線程1(user1)是第一個創建數據庫實例的線程,線程2(user2)對此數據庫調用connect()
和update()
方法-即使在user2正在執行操作時,也不應提供NullPointerException
從user1進行的update()
連接未完成。
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.