2016-04-05 4 views
0

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

Я хочу сделать так, чтобы прошла повторная проверка подлинности первого потока, а остальные ждут завершения и продолжения, как обычно.

Это мой оригинальный код проверки перед внедрением решения:

public class Main { 

    public static void main(String args[]){ 
     new Main(); 
    } 

    public Main(){ 
     AuthManager authClass = new AuthManager(); 

     for (int i = 0; i < 5; i++) { 
      Thread thr = new Thread(() -> { 
       int count = 0; 

       while(count < 2) { // Bad practice but just for the example. 
        if(count == 1){ 
         if(authClass.reAuthenticate()) { 
          System.out.println("Reauthenticated."); 

          authClass.doStuff(); 
         } 
        } else { 
         authClass.doStuff(); 
        } 

        count++; 
       } 
      }); 

      thr.start(); 
     } 

     // Keep the program running for 30 seconds. 
     try { 
      Thread.sleep(10 * 1000); 
     } catch (InterruptedException e) { 
      // ignored 
     } 
    } 

    private class AuthManager { 

     public boolean reAuthenticate(){ 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // ignored 
      } 

      System.out.println("Reauthenticating.."); 

      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // ignored 
      } 

      return true; // or false when no success in the real application. 
     } 

     public void doStuff(){ 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // ignored 
      } 

      System.out.println("Doing stuff."); 
     } 

    } 
} 

Ответ:

Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Reauthenticating.. 
Reauthenticating.. 
Reauthenticating.. 
Reauthenticating.. 
Reauthenticating.. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 

Ответ Я хочу:

Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Reauthenticating.. 
Reauthenticated. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 

Как я могу добиться этого?

Редактировать Я сделал это сейчас с ответом @haifzhan, но это не будет работать, когда я должен проверить подлинность позже.

public class Main { 

    public static void main(String args[]){ 
     new Main(); 
    } 

    public Main(){ 
     AuthManager authClass = new AuthManager(); 

     for (int i = 0; i < 5; i++) { 
      Thread thr = new Thread(() -> { 
       int count = 0; 

       while(count < 4) { // Bad practice but just for the example. 
        if(count == 1 || count == 3){ 
         if(authClass.reAuthenticate()) { 
          System.out.println("Reauthenticated."); 

          authClass.doStuff(); 
         } 
        } else { 
         authClass.doStuff(); 
        } 

        count++; 
       } 
      }); 

      thr.start(); 
     } 

     // Keep the program running for 30 seconds. 
     try { 
      Thread.sleep(10 * 1000); 
     } catch (InterruptedException e) { 
      // ignored 
     } 
    } 

    private class AuthManager { 

     private final AtomicBoolean isAuthorized = new AtomicBoolean(); 

     public synchronized boolean reAuthenticate() { 
      if(!isAuthorized.get()) { 
       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        // ignored 
       } 

       System.out.println("Reauthenticating.."); 

       try { 
        Thread.sleep(1000); 
       } catch (InterruptedException e) { 
        // ignored 
       } 

       isAuthorized.set(true); 
       return isAuthorized.get(); 
      } 

      return isAuthorized.get(); 
     } 

     public void doStuff(){ 
      try { 
       Thread.sleep(1000); 
      } catch (InterruptedException e) { 
       // ignored 
      } 

      System.out.println("Doing stuff."); 
     } 

    } 
} 

Ответ:

Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Reauthenticating.. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Reauthenticated. 
Doing stuff. 
Reauthenticated. 
Doing stuff. 
Reauthenticated. 
Doing stuff. 
Reauthenticated. 
Doing stuff. 
Reauthenticated. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 

Ответ Я хочу:

Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Reauthenticating.. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Reauthenticating.. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Reauthenticated. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
Doing stuff. 
+1

Вы сделали 5 потоков, которые делают то же самое (переутверждайте и делайте что-нибудь), вы должны попытаться сделать только один поток, который аутентифицируется, и другие, которые только делают вещи, или попытаются проверить, прошел ли поток уже аутентифицирован и в этом случае пропустить reauthenticate stuff. – aleb2000

+0

@ aleb2000 имеет хороший момент, но если вы упрям, существуют различные способы сокращения доступа к ресурсу, например - [Семафоры] (https://docs.oracle.com/javase/7/docs/api/java/ Util/параллельный/Semaphore.html). Просто помните, что это добавляет сложности. – zec

+0

Я не знал о Семафорах, да, правильно использовал это. @ Zec – aleb2000

ответ

2

Вот возможное решение:

  Thread thr = new Thread(() -> { 
       int count = 0; 

       while(count < 2) { // Bad practice but just for the example. 
        if (count == 1 && authClass.reAuthenticate()) { 
         System.out.println("Reauthenticated."); 

         authClass.doStuff(); 
        } else { 
         authClass.doStuff(); 
        } 

        count++; 
       } 
      }); 

В первом фрагменте кода, я изменил немного логики для вызова auth.doStuff() в случае reAuthenticate возвращается false.

private class AuthManager { 

     private volatile boolean reAuthenticate; 
     public boolean reAuthenticate(){ 
      if (!reAuthenticate) { 
       synchronized (this) { 
        if (!reAuthenticate) { 
         try { 
          Thread.sleep(1000); 
         } catch (InterruptedException e) { 
          // ignored 
         } 

         System.out.println("Reauthenticating.."); 

         try { 
          Thread.sleep(1000); 
         } catch (InterruptedException e) { 
          // ignored 
         } 
         return this.reAuthenticate = true; 
        } 
       } 
      } 
      return false; 
     } 

Во втором фрагменте кода, я полагаюсь на летучем переменной, чтобы иметь возможность делать двойную проверку значения reAuthenticate для того, чтобы вызвать его только один раз и не имея приобретать любой замок для последующих обращений к reAuthenticate ,

+0

@HovercraftFullOfEels Это лучше сейчас? –

+0

@HovercraftFullOfEels Я понимаю, что вы можете проголосовать, если ответ не так, как ожидалось, но вы также должны дать очки, когда ответ будет выполнен в соответствии с просьбой, пожалуйста, не забывайте, что мы бесплатно проводим время, чтобы помочь людям –

+0

Пожалуйста, позвольте меня время от моего компьютера. –

1

Вы можете использовать synchronized метод для изменения AtomicBooleanisAuthorized, это гарантирует, что поточно-, и другой поток не будет повторная проверка подлинности.

Во-вторых, вам необходимо улучшить свое правило, что вы действительно хотите аутентифицироваться, и как отображать аутентифицированные и отображаемые doStuff. В вашем цикле while, независимо от того, удовлетворяет ли условие или условие, как print doStuff, вам нужно найти способ отличить результат.

public synchronized boolean reAuthenticate(){ 
    if(! isAuthorized.get()){ 
     System.out.println("Reauthenticating.."); 
     try { 
      Thread.sleep(1000); 
     } catch (InterruptedException e) { 
      // ignored 
     } 
     isAuthorized.set(true); 
     return isAuthorized.get(); 
    } 

    return isAuthorized.get(); // or false when no success in the real application. 
} 

Выходные:

else doStuff 
    else doStuff 
    else doStuff 
    else doStuff 
    else doStuff 
    Doing stuff 
    Reauthenticating.. 
    Doing stuff 
    Doing stuff 
    Doing stuff 
    Doing stuff 
    Reauthenticated. 
    Reauthenticated. 
    Reauthenticated. 
    Reauthenticated. 
    Reauthenticated. 
    Doing stuff 
    Doing stuff 
    Doing stuff 
    Doing stuff 
    Doing stuff 

Reauthenticated печататься несколько раз, потому что ваш код печати эта строка один раз reAuthenticate() верно.

+0

Спасибо за это, я сейчас на полпути. Я отредактировал оригинальный вопрос, не могли бы вы взглянуть на это? – Mike

+0

Почему вы используете 'AtomicBoolean' внутри блока, который уже является атомарным? Вы хотите, чтобы 'isAuthorized' был доступен другим методам? – erickson

+0

@erickson youre правый. я отредактировал свой первый пост с 'synchronized' и оставил без изменений. – haifzhan

1

Используйте read-write lock.

Потоки могут «делать вещи», когда они приобретают блокировку чтения, но должен получить блокировку записи для того, чтобы повторно проверить.

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

+0

Могу ли я заставить другого подождать и использовать тот же ответ, что и тот, который приобрел замок, когда он закончил с этим? – Mike

+0

Да. По сути, блокировка будет защищать доступ к идентификатору сеанса. Все потоки могут получить блокировку и прочитать идентификатор сеанса, но когда он окажется недействительным, один поток получит блокировку записи и исключит других из чтения, пока он не будет обновлен. – erickson

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