как сериализовать функцию в зависимости от того, какой экземпляр объекта вызывает ее, если тот же экземпляр вызова в потоке, то сериализовать не иначе - PullRequest
1 голос
/ 15 сентября 2011

У меня есть функция депозита, которая вызывается несколькими клиентами одновременно. Я хочу, чтобы функция депозита синхронизировалась, когда тот же клиент (скажем, клиент с id = someUniqueNo) вызывает ее снова, но не синхронизировалась, когда другой клиент это вызывал? Как я могу этого добиться. Использование Spring Framework в проекте, это круто, даже если какое-либо решение с фреймворком предусматривает такой шаблон.

Ответы [ 6 ]

2 голосов
/ 15 сентября 2011

Хотелось бы что-нибудь подобное?

private Map<String, ReentrantLock> lockMap = new HashMap<String, ReentrantLock>();

public void deposit(String clientId) {
    updateLockMap(clientId);

    ReentrantLock lock = lockMap.get(clientId);
    try {
        if(lock.tryLock(1, TimeUnit.MINUTES)) {
            // do deposit stuff
        }
    } catch (InterruptedException e) {
        // log
    } finally {
        lock.unlock();
    }
}

private void updateLockMap(String clientId) {
    if(!lockMap.keySet().contains(clientId)) {
        lockMap.put(clientId, new ReentrantLock());
    }
}
2 голосов
/ 15 сентября 2011
public void deposit(String clientId){
   String id = clientId;
   while(id.equals("someClientID")){
         synchronized(lockObject){
             // Do your stuff here in a sync'd way
          }
   }
   else
   {
     // Do your stuff here in a NON synch'd way 
   }

 }

Вторая попытка: (не проверено!)

public void deposit(String clientId){
    String id = clientId;
   // callers is a ConcurrenthashMap<String,String>
     while(callers.get(id) != null){  
     synchronized(callers.get(id)){  // client already in call, so wait
        wait(); 
     }
   }       
      callers.put(id,id);  // client is ready to run a new call, so setup 
      // Do your stuff here in a NON synch'd way 
 synchronized(callers.get(id)){  // client done with a call , 
                      //so notify others who are calling
    callers.put(id,null);
    notifyAll();
       }
}  

Третья попытка (проверено и работает правильно)

    public class SyncTest {
     static int[] balances = new int[]{ 1,10,100,1000,10000};
public static void main(String[] args) {
    // TODO Auto-generated method stub
    SyncTest test = new SyncTest();
    Client c1 = new Client("0", test,1);
    Client c2 = new Client("1", test,10);
    Client c3 = new Client("2", test,100);
    Client c4 = new Client("3", test,1000);
    Client c5 = new Client("4", test,10000);

    Thread[] threads = new Thread[5];
    threads[0] = new Thread(c1);
    threads[1] = new Thread(c2);
    threads[2] = new Thread(c3);
    threads[3] = new Thread(c4);
    threads[4] = new Thread(c5);

    for(int i=0;i<5;i++){
        threads[i].start();
    }
    for(int i=0;i<5;i++){
        try{
            threads[i].join();
        }
        catch(InterruptedException ex){
        }
    }


    System.out.println("Final balances are ");
    for(int i=0;i<5;i++){
        System.out.print(balances[i] + " , " );
    }
}

ConcurrentHashMap<String,String> callers = new ConcurrentHashMap<String, String>();

public void deposit(Client c) throws InterruptedException{
    String id = c.id;
    int amount = c.amount;
    if(id == null) return ;

   // callers is a ConcurrenthashMap<String,String>
    System.out.println("Client " + id + " in deposit. Checking if already running..");
    while( callers.get(id) != null && !(callers.get(id).equals("X"))) {
       synchronized(id){
        System.out.println("Client " + id + " in deposit. Found already running,going to wait..");
        id.wait(); 
     }
   }
    if(callers == null){
        throw new NullPointerException("Callers is null!!");
    }

    System.out.println("Client " + id + " in deposit. Found not running already,  Now running..");
    callers.put(id,id);
    int index = Integer.parseInt(id);
    balances[index] += amount;
    Thread.sleep(2000);
    synchronized(id){
        String old = callers.put(id,"X");
        id.notifyAll();
       System.out.println("Client " + id + " in deposit. Finished running ,  Notifying..");
    }
}

static class Client implements Runnable {
    String id ;
    SyncTest test;
    int amount;
    Client c = this;

    Client(String id , SyncTest t, int am){
        this.id = id;
        test = t;
        amount = am;
    }

    public void run(){
        process();
    }

    void process(){
        System.out.println("Client " + id + " processing ...");
        try{
            Thread.sleep(1000);
            Thread t1 = new Thread(
                new Runnable(){
                    public void run(){
                        try{
                            test.deposit(c);
                        }
                        catch(InterruptedException ex){
                        }
                    }
                }
            );
            Thread t2 = new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                test.deposit(c);
                            }
                            catch(InterruptedException ex){
                            }
                        }
                    }
                );

            Thread t3 = new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                test.deposit(c);
                            }
                            catch(InterruptedException ex){
                            }
                        }
                    }
                );

            Thread t4 = new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                test.deposit(c);
                            }
                            catch(InterruptedException ex){
                            }
                        }
                    }
                );

        /*  Thread t5 = new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                test.deposit(c);
                            }
                            catch(InterruptedException ex){
                            }
                        }
                    }
                );

            Thread t6 = new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                test.deposit(c);
                            }
                            catch(InterruptedException ex){
                            }
                        }
                    }
                );
            Thread t7 = new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                test.deposit(c);
                            }
                            catch(InterruptedException ex){
                            }
                        }
                    }
                );
            Thread t8 = new Thread(
                    new Runnable(){
                        public void run(){
                            try{
                                test.deposit(c);
                            }
                            catch(InterruptedException ex){
                            }
                        }
                    }
                );
        */  
            t1.start();t2.start();t3.start();t4.start();//t5.start();t6.start();t7.start();t8.start();
            t1.join();t2.join();t3.join();t4.join();//t5.join();t6.join();t7.join();t8.join();

        }
        catch(InterruptedException ex){
            System.out.println(" Exception " + ex.getMessage());
        }
        System.out.println("Client " + id + " done #####");
    }
}

}

1 голос
/ 15 сентября 2011

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

Если у вас происходит обновление, вы должны убедиться, что все методы дляОбновление И ЧТЕНИЕ значения исключительно заблокированы, в противном случае возникает хаос.Если у вас нет этих блокировок, вы можете получить частичные или неправильные результаты.

Скажем, у вас есть такой процесс, как:

  1. Обновление списка транзакций
  2. Обновление баланса

Поток 1 запускает процесс и выполняет 1. Второй поток ЧИТАЕТ (просто читает) список транзакций и баланс.Поток 2 завершает процесс и завершается.

Теперь поток 2 имеет противоречивое представление о состоянии учетной записи.Список не соответствует балансу.Это всего лишь пример, но он иллюстрирует проблему.

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

1 голос
/ 15 сентября 2011

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

1 голос
/ 15 сентября 2011

Использовать блокировку для каждого клиента. То есть вместо синхронизации метода напрямую используйте явную блокировку через объекты блокировки. Сделайте так, чтобы у вас была одна блокировка на клиента, которую вы используете для синхронизации доступа к функции депозита.

0 голосов
/ 15 сентября 2011

Вы можете сделать:

Client lock = yourClient;
synchronized (lock) {

}

Получить блокировку на клиенте по той же ссылке.

...