2014-12-12 3 views
-1

Я создал объект базы данных в соответствии с шаблоном singleton. Объект базы данных содержит 2 метода: connect() и update().Как синхронизировать два метода в одноэлементном шаблоне

Обновление должно выполняться многопоточным, что означает, что я не могу поставить в подпись метода update (я хочу, чтобы пользователи одновременно обращались к нему не по одному).

Моя проблема заключается в том, что я хочу, чтобы убедиться, что 2 сценария в соответствии с этим потоков:

  1. Нить 1 (user1) является первой для создания экземпляра БД и нити 2 (user2) вызывает методи update() этой БД - не должен давать NullPointerException, даже если к моменту, когда пользователь2 делает update(), соединение с user1 не выполняется.

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

} 
+1

Что в методе обновления, пожалуйста, покажите код –

+0

без дополнительной информации, я могу только предположить, что ваша база данных представляет собой массив строк, хранящихся в одноэлементном режиме. В любом случае это будет сделано. У двух клиентов нет возможности обновлять одну и ту же строку, в то же время это имеет смысл. Разумеется, нет двух способов одновременного добавления строк в вектор. Что-то где-то должно быть блокирующим. –

+0

добавлен код выше. спасибо за любую помощь – Matoy

ответ

1

Примечание. Сообщение ниже было написано в перспективе обоих пользователей, используя те же учетные данные (скрытые от них) для подключения к базе данных. Если пользователи используют разные учетные данные, идея объекта singleton db нецелесообразна, каждый пользователь должен иметь свой собственный объект подключения, и, конечно, данные соединения передаются от пользователя к Db через все, что представляет пользователя в программе (здесь по-видимому, экземпляры потока).

Основная проблема в реализации, которую вы указали, заключается в том, что метод getinstance требует, чтобы вызывающий пользователь знал подробности подключения или предположил, что соединение уже выполнено. Но ни потоки не могли и не знали заранее, если Db уже открыт, и дизайн мудрый, это ошибка, чтобы передать им ответственность за его прямое открытие. Эти потоки являются рабочими потоками, они не должны беспокоиться о деталях конфигурации Db.

Единственный разумный способ справиться с этой ситуацией - иметь эти параметры конфигурации, находящиеся непосредственно у объекта Db, или, лучше, еще один объект, отвечающий за его предоставление (это шаблон factory).

Однако, если вы хотите, чтобы ваш код работал с минимальными изменениями, избавитесь от метода less getinstance, у вас есть нить, требующая, чтобы объект Db использовал оставшийся вариант этого метода, передавая правильные параметры и измените его, чтобы вернуть экземпляр, если он существует, или создать его иначе, не создавая исключения. Я считаю, что это то, что @Dima пытался объяснить в своем ответе.

+0

Спасибо! как вы рекомендуете использовать здесь заводскую модель? У меня есть 2 случая: пользователь впервые подключается с именем и проходом (тогда нам нужно подключиться()), и пользователь подключается без имени и пароля после того, как он знает, что datavase открыта (нет необходимости подключаться()). – Matoy

+0

Добавил эту ссылку в свой ответ http://en.wikipedia.org/wiki/Factory_method_pattern – didierc

+0

Спасибо ... просто чтобы быть понятным - вы говорите, что, поскольку шаблон singelton - это дать экземпляр общего объекта и * не требует от пользователей singelton *, чтобы узнать, являются ли они первыми, кто создал этот объект, или им предоставлен существующий объект, который уже был создан, - я не должен пытаться отменить метод getInstance(), и поток должен быть " глупым "с точки зрения неизвестности, создают ли они или просто возвращают объект, который был создан? Thansk! – Matoy

1

Connect один раз, при создании синглтон (в конструкторе, возможно). У вас есть synchronized static method (getInstance или что-то еще), который проверяет, существует ли экземпляр, создает и подключается при необходимости и возвращает экземпляр. Следуя этому протоколу, вы гарантируете, что потоки всегда будут иметь подключенный объект Db, готовый к использованию.

Пользователи будут вызывать этот метод, чтобы получить экземпляр singleton, и вызвать update или что бы они ни хотели на нем, его не нужно синхронизировать.

+0

Я добавил код выше. проблема в том, что метод обновления на самом деле зависит от статического конструктора БД - это означает, что если thread1 не закончил конструктор или метод getInstance() (хотя это был первый поток, который должен быть выполнен), тогда поток 2 получит исключение nullpointer , поэтому мне нужно убедиться, что, если вызывается конструктор, обновление будет ждать завершения (но обновление() все равно будет выполняться с синхронизированным сигналом). – Matoy

+2

'update' - это метод экземпляра. Чтобы вызвать метод экземпляра, вам нужен экземпляр для его вызова, правильно? Пока конструктор не закончит, экземпляр не существует. Поэтому никто не может вызвать 'update' до завершения конструктора. Думаю об этом. – Dima

Смежные вопросы