ReentrantLock позволяет потокам входить в блокировку на ресурсе более одного раза - PullRequest
0 голосов
/ 08 января 2019

ReentrantLock позволяет потокам входить в блокировку ресурса более одного раза,

Как это дает преимущества с точки зрения исполнения / эффективности / функциональности?

Ссылка на эту ссылку, https://www.geeksforgeeks.org/reentrant-lock-java/

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

КОД:

import java.text.SimpleDateFormat; 
import java.util.Date; 
import java.util.concurrent.ExecutorService; 
import java.util.concurrent.Executors; 
import java.util.concurrent.locks.ReentrantLock; 

class worker implements Runnable 
{ 
  String name; 
  ReentrantLock re; 
  public worker(ReentrantLock rl, String n) 
  { 
    re = rl; 
    name = n; 
  } 
  public void run() 
  { 
    boolean done = false; 
    while (!done) 
    { 
      //Getting Outer Lock 
      boolean ans = re.tryLock(); 

      // Returns True if lock is free 
      if(ans) 
      { 
        try
        { 
          Date d = new Date(); 
          SimpleDateFormat ft = new SimpleDateFormat("hh:mm:ss"); 
          System.out.println("task name - "+ name 
                     + " outer lock acquired at "
                     + ft.format(d) 
                     + " Doing outer work"); 
          Thread.sleep(1500); 

          // Getting Inner Lock 
          re.lock(); 
          try
          { 
            d = new Date(); 
            ft = new SimpleDateFormat("hh:mm:ss"); 
            System.out.println("task name - "+ name 
                       + " inner lock acquired at "
                       + ft.format(d) 
                       + " Doing inner work"); 
            System.out.println("Lock Hold Count - "+ re.getHoldCount()); 
            Thread.sleep(1500); 
          } 
          catch(InterruptedException e) 
          { 
            e.printStackTrace(); 
          } 
          finally
          { 
            //Inner lock release 
            System.out.println("task name - " + name + 
                       " releasing inner lock"); 

            re.unlock(); 
          } 
          System.out.println("Lock Hold Count - " + re.getHoldCount()); 
          System.out.println("task name - " + name + " work done"); 

          done = true; 
        } 
        catch(InterruptedException e) 
        { 
          e.printStackTrace(); 
        } 
        finally
        { 
          //Outer lock release 
          System.out.println("task name - " + name + 
                     " releasing outer lock"); 

          re.unlock(); 
          System.out.println("Lock Hold Count - " + 
                       re.getHoldCount()); 
        } 
      } 
      else
      { 
        System.out.println("task name - " + name + 
                      " waiting for lock"); 
        try
        { 
          Thread.sleep(1000); 
        } 
        catch(InterruptedException e) 
        { 
          e.printStackTrace(); 
        } 
      } 
    } 
  } 
} 

public class test 
{ 
  static final int MAX_T = 2; 
  public static void main(String[] args) 
  { 
    ReentrantLock rel = new ReentrantLock(); 
    ExecutorService pool = Executors.newFixedThreadPool(MAX_T); 
    Runnable w1 = new worker(rel, "Job1"); 
    Runnable w2 = new worker(rel, "Job2"); 
    Runnable w3 = new worker(rel, "Job3"); 
    Runnable w4 = new worker(rel, "Job4"); 
    pool.execute(w1); 
    pool.execute(w2); 
    pool.execute(w3); 
    pool.execute(w4); 
    pool.shutdown(); 
  } 
}

Ответы [ 2 ]

0 голосов
/ 08 января 2019

Предположим, в классе у вас есть два метода m1 и m2, оба синхронизированы, а m2 вызывает m1. В этом случае, если поток a взял блокировку на m1 и повторная блокировка не разрешена, тогда этот поток продолжит ждать вызова m2 (так как он уже имеет блокировку)

подробнее:

https://stackoverflow.com/questions/18596080/java-concurrent-reentrantlock-why-we-want-to-acquire-the-same-lock-multiple-ti

0 голосов
/ 08 января 2019

Я думаю, что излишне объяснять, как это работает, мы должны использовать только один из методов lock () или tryLock ():

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

То есть вы должны использовать tryLock () over lock (), когда поток имеет или может получить дополнительную работу, независимую от получения блокировки.

----- Дополнительно:

Допустим, у вас есть четыре задачи, с 1 по 3, которые выполняются пулом потоков с двумя рабочими потоками, A и B. Задачи 1 и 2 совместно используют ресурс, к которому должен обращаться один поток за раз, чтобы предотвратить коррупция.

Теперь, если вы просто заблокируете без пробной версии, вы можете попасть в следующую ситуацию:

  1. Тема A запускает задание 1;

  2. Поток A получает блокировку ресурса;

  3. Тема B запускает задачу 2;
  4. Нить B пытается получить блокировку, но блокируется (спит);
  5. Нить А завершает задание 1, освобождая замок;
  6. Нить B просыпается, но нить A еще не переключилась;
  7. Тема A запускает задание 3;
  8. Поток B пытается снова получить блокировку;
  9. Поток B получает блокировку и завершает задачу 2;
  10. Тема А завершает задание 3.

Обратите внимание, что lock () приостанавливает поток до тех пор, пока блокировка не будет снята, поэтому поток B совершенно бесполезен, пока поток A не снимет блокировку. Поток B мог бы запустить задачу 3 вместо ожидания блокировки и завершить ее тем временем.

Алгоритм, использующий try-lock, может выполняться следующим образом:

  1. Тема A запускает задание 1;
  2. Поток A тестирует и получает блокировку;
  3. Тема B запускает задачу 2;
  4. Поток B тестирует блокировку, пропуская задачу 2;
  5. Тема B запускает задание 3;
  6. Нить А завершает задание 1, освобождая блокировку;
  7. Тема A запускает задачу 2;
  8. Тема B завершает задание 3;
  9. Нет больше задач, поэтому нить B спит;
  10. Нить А завершает задание 2, снимая блокировку.

Обратите внимание, что tryLock () не приостанавливает вызывающий поток, поэтому задача блокировки может быть пропущена, и поток B вместо этого выполняет задачу блокировки. Если задачи 1 и 2 были длинными, и было несколько других коротких неблокирующих задач, все они могли бы быть завершены до завершения задачи 1 или запуска задачи 2.

Конечно, реализация пулов потоков и управление задачами немного сложнее, чем простая блокировка: задачи могут быть приостановлены и возвращены в пул; и спящие незанятые нити должны быть разбужены при снятии любых блокировок.

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

...