2013-11-07 2 views
0

У меня есть программа с писателями и читателями, и их право доступа контролируется монитором.Могут ли многоуровневые блокировки вызывать взаимоблокировки в многопоточных программах?

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

Мой вопрос: являются ли эти многоуровневые замки ответственными за тупик? Если нет, что вызывает это ?!

import java.io.*; 
    import java.io.IOException; 
    import java.util.*; 

    public class Writer extends Thread{ 

    private int number; 

    public Writer(int number) 
    { 
     this.number = number; 
    } 

    public int getNumber() 
    { 
     return number; 
    } 

     public static void Write(String filename){ 

     try { 

      String content = RandomString(); 


      File f = new File(filename); 

      if (!f.exists()) 
      { 
       f.createNewFile(); 
      } 


      PrintWriter out = new PrintWriter(new BufferedWriter(new FileWriter("Task1out.txt", true))); 
      out.println(content); 
      out.close(); 


     } catch (IOException e) { 
      e.printStackTrace(); 
     } 
    } 

    public static String RandomString(){ 

     String chars = new String("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); 
     int n = chars.length(); 

     String randomString = new String(); 
     Random r = new Random(); 

      for (int i=0; i<100; i++) 
      { 
       randomString = randomString + chars.charAt(r.nextInt(n)); 
      } 

     System.out.println("RandomString() generated: " + randomString); 

     return randomString; 

    } 



    public void run(){ 

     try{ 

     //FileControl fc = new FileControl(); 

      int number = this.getNumber(); 


      for(int i = 0; i <1000; i++) //CHANGE IT TO 1000 
      { 
       main.fc.WriterEntry(number); 

       //write file random characters (must append) 

       Write("Task1out.txt"); 

       main.fc.WriterExit(number); 

      } 
     } catch(InterruptedException e) 
     { 
      System.out.println("Interrupted Exception caught"); 
     } 

    } 


} 

Это класс писателя.

import java.io.BufferedWriter; 
    import java.io.BufferedReader; 
    import java.io.File; 
    import java.io.FileWriter; 
    import java.io.FileReader; 
    import java.io.IOException; 
    import java.util.*; 
    import java.util.concurrent.locks.Condition; 
    import java.util.concurrent.locks.Lock; 
    import java.util.concurrent.locks.ReentrantLock; 



public class Reader extends Thread{ 


    private int number; 

    public Reader(int number) 
    { 
     this.number = number; 
    } 


    public int getNumber() 
    { 
     return number; 
    } 

     public static synchronized void Read(String filename)throws InterruptedException{ 

     BufferedReader br = null; 





      main.lock.lock(); //lock 
     try{ 




     try { 


      String line; 
      char[] chars = new char[100]; 
      int readIndex2 = 0; 
      int addToIndex = 0; 



      br = new BufferedReader(new FileReader(filename)); 


      int initialReadIndex = main.getIndex(); 




      System.out.println("initial read index: " + initialReadIndex); 

      while ((line = br.readLine()) != null && readIndex2 < initialReadIndex+100 && addToIndex < 100) { 

       for(int i = 0; i< 100; i++) 
       { 
        if (initialReadIndex == readIndex2 || initialReadIndex < readIndex2) 
        { 

         if(line.length() > addToIndex) 
         { 




         chars[i] = line.charAt(i); 
         addToIndex++; 
         } 


        } 
        else 
        { 
         readIndex2++; 
        } 
       } 
       System.out.println(chars); 
      } 

      if(line == null) 
      { 
       System.out.println("nothing to read"); 
      } 



      main.incrementIndex(addToIndex); 


      System.out.println("current read index: " + (initialReadIndex + addToIndex)); 





     } catch (IOException e) { 
      e.printStackTrace(); 
      System.out.println("buffered reader exception"); 
     } finally { 


      try { 


       if (br != null) 
        { 

        br.close(); 
        } 
      } catch (IOException ex) { 
       ex.printStackTrace(); 
       System.out.println("exception during closing"); 
      } 
     } 
     }finally{ 
      main.lock.unlock(); //lock 

     } 

     } 


    public void run(){ 

     try{ 


     //FileControl fc = new FileControl(); 


     int number = this.getNumber(); 


      for(int i = 0; i <1000; i++) //CHANGE IT TO 1000 
      { 
       main.fc.ReaderEntry(number); 

       //read file 

       Read("Task1out.txt"); 

       main.fc.ReaderExit(number); 
      } 
     } catch(InterruptedException e) 
     { 
      System.out.println("Interrupted Exception caught"); 
     } 

    } 



     } 

Это класс читателя.

import java.io.BufferedWriter; 
    import java.io.BufferedReader; 
    import java.io.File; 
    import java.io.FileWriter; 
    import java.io.FileReader; 
    import java.io.IOException; 
    import java.util.concurrent.locks.Condition; 
    import java.util.concurrent.locks.Lock; 
    import java.util.concurrent.locks.ReentrantLock; 

    public class main{ 

    public static FileControl fc = new FileControl(); 

    final static Lock lock = new ReentrantLock(); 

    public static int readIndex; 

    public static void incrementIndex(int increment) { 


       readIndex = readIndex + increment; 

    } 

    public static int getIndex() 
    { 
     return readIndex; 
    } 



    public static void main(String[] args) throws InterruptedException { 



      Writer [] writer = new Writer[10]; 
      Reader [] reader = new Reader[10]; 

      for(int i = 0; i < 10; i++) 
      { 
       reader[i] = new Reader(i); 
       writer[i] = new Writer(i); 
       //creating readers and writers 

      } 

      for(int i = 0; i < 10; i++) 
      { 
       //anonymous threads 
       //(new Thread(new Writer())).start(); 
       //(new Thread(new Reader())).start(); 

       reader[i].start(); 
       writer[i].start(); 

      } 




      for(int i = 0; i < 10; i++) 
      { 
       try{ 
        reader[i].join(); 
        writer[i].join(); 
       } catch(InterruptedException e){ 
        e.printStackTrace(); 
       } 


      } 






     } 

} 

Это основной класс.

import java.util.concurrent.locks.Condition; 
    import java.util.concurrent.locks.Lock; 
    import java.util.concurrent.locks.ReentrantLock; 


    public class FileControl { 
    final Lock lock = new ReentrantLock(); 
    final Condition writers = lock.newCondition(); 
    final Condition readers = lock.newCondition(); 
    int activereaders = 0; 
    int waitingwriters = 0; 
    boolean writing = false; 

    public void WriterEntry(int number)throws InterruptedException{ 
     lock.lock(); 
     try{ 
       if(writing == true || activereaders > 0){ 
        waitingwriters++; 
        System.out.println("Writer thread " + number + " : waiting to write"); 
        writers.await(); 
        waitingwriters--; 
       } 
       System.out.println("Writer thread " + number + " : ready to write"); 

       writing = true; 
      } 
     finally{ 
      lock.unlock(); 
     } 


    } 



    public void WriterExit(int number)throws InterruptedException{ 
     lock.lock(); 
     try{ 
      System.out.println("Writer thread " + number + " : finished to write"); 

      System.out.println("writers " + waitingwriters + "readers " + activereaders); //test 

      if(waitingwriters > 0) 
       writers.signal(); 
      else{ 
       writing = false; 
       readers.signal(); 
      } 
     } 
     finally{ 
      lock.unlock(); 
     } 

    } 


    public void ReaderEntry(int number)throws InterruptedException{ 
     lock.lock(); 
     try{ 

      if(writing == true || waitingwriters > 0){ //remove activereaders > 0 
       System.out.println("Reader thread " + number + " : waiting to read"); 
       readers.await(); 
       activereaders++; 
      } 


      System.out.println("Reader thread " + number + " : ready to read"); 
     } 
     finally{ 
      lock.unlock(); 
     } 

    } 

    public void ReaderExit(int number)throws InterruptedException{ 
     lock.lock(); 
     try{ 



     activereaders--; 



     System.out.println("Reader thread " + number + " : finished to read"); 

     System.out.println("writers " + waitingwriters + "readers " + activereaders); //test 

      if(activereaders == 0) 
      { 
       if(waitingwriters > 0) 
       { 
        writers.signal(); 
       } 
       else 
       { 
        readers.signal(); 
       } 
      } 
     } 
     finally{ 
      lock.unlock(); 
     } 

    } 


} 

Это монитор.

pseudocode for the monitor

+1

Я удивлен, что на самом деле у вас есть ответы ... Надеемся на многое, в будущем попробуйте ввести только соответствующие детали. –

ответ

4

Всякий раз, когда у вас есть несколько замков A, B и C вы можете иметь тупиковую ситуацию, если не гарантировать, что ваш код пытается получить блокировку сказала в том же порядке.

final Lock A = new ReentrantLock(); 
final Lock B = new ReentrantLock(); 
final Lock C = new ReentrantLock(); 

A, B, C или C, B, A или A, C, B - это не имеет значения до тех пор, пока заказ соответствует.

Проблема возникает, когда у вас есть один путь кода: A, B, C И еще одна попытка для C, B, A.

Как вы, вероятно, можете догадаться, что оба A и C удерживаются, один из двух получит B, а затем оба будут тупиковыми. (Ака у вас есть цикл в ресурс запирающего графа)

Формально тупиковый может возникнуть только если все из следующих условий:

  1. Нет Вытеснение: Система не будет свободных ресурсов после выделения; их можно освободить только в процессе удержания.
  2. Circular Wait: Обсуждалось выше.
  3. Взаимное исключение: только один процесс может использовать ресурс в любой момент времени.
  4. Resource Holding: В настоящее время процесс удерживает хотя бы один ресурс и запрашивает/ожидает дополнительные ресурсы, которые хранятся другим процессом.

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

Ресурсы:

  • Вот практическая статья на анализе тупиковой ситуации в Java, что вы может быть интересно: http://www.journaldev.com/1058/java-deadlock-example-and-how-to-analyze-deadlock-situation
  • Вы можете также использовать инструменты с открытым исходным кодом, как JCarder найти затор: http://www.jcarder.org/, который для программ с большими дампами может быть проще, чем пытаться архивировать файлы дампа.

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

+0

Порядок должен быть таким же, поскольку все они приобретают блокировку A, которая в общем случае и затем заблокируйте B, который находится в Readers. Таким образом, порядок не мог быть причиной. Я очень озадачен тем, что вызвало тупик. – user2967016

0

Это, безусловно, возможно. Также вы можете проверить во время выполнения!

Первый шаг - получить дамп потока. Вот три метода:

  • Если вы откроете процесс в VisualVM и перейдите на вкладку «threads», он скажет вам, обнаруживает ли он этот тупик. Затем вы можете создать дамп потока (там есть кнопка), которая расскажет вам, что делает каждый поток, а также любые блокировки, которыми он владеет, и любой, который блокирует (если есть), который он пытается приобрести.
  • В Linux или Mac вы можете получить стек, выпустив kill -3 <pid>, где <pid> - это ваш идентификатор java-процесса. Он сбросит ту же самую дампу потока в stderr. В нижней части этого дампа потока также будет содержаться сводка о блокировках, которые он обнаруживает. Я не знаю, как это сделать в Windows.
  • Вы также можете вызвать jstack <pid>, в котором будет напечатано дамп потока в стандартный вывод (jstack 's stdout, а не оригинальный процесс java').

Я написал пример программы, которая блокирует и запускает ее (см. my gist). Соответствующий раздел отвала резьбы является:

Found one Java-level deadlock: 
============================= 
"Thread-2": 
    waiting for ownable synchronizer 7f42b0f38, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), 
    which is held by "Thread-1" 
"Thread-1": 
    waiting for ownable synchronizer 7f42ba170, (a java.util.concurrent.locks.ReentrantLock$NonfairSync), 
    which is held by "Thread-2" 

И соответствующие состояния резьбы являются:

"Thread-2" prio=5 tid=7fc01c911000 nid=0x113d18000 waiting on condition [113d17000] 
    java.lang.Thread.State: WAITING (parking) 
    at sun.misc.Unsafe.park(Native Method) 
    - parking to wait for <7f30c3528> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) 
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:842) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1178) 
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186) 
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262) 
    at Locky$Boomer.run(Locky.java:22) 
    at java.lang.Thread.run(Thread.java:680) 

    Locked ownable synchronizers: 
    - <7f30c3558> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) 

"Thread-1" prio=5 tid=7fc01d06c800 nid=0x113c15000 waiting on condition [113c14000] 
    java.lang.Thread.State: WAITING (parking) 
    at sun.misc.Unsafe.park(Native Method) 
    - parking to wait for <7f30c3558> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) 
    at java.util.concurrent.locks.LockSupport.park(LockSupport.java:156) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.parkAndCheckInterrupt(AbstractQueuedSynchronizer.java:811) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireQueued(AbstractQueuedSynchronizer.java:842) 
    at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquire(AbstractQueuedSynchronizer.java:1178) 
    at java.util.concurrent.locks.ReentrantLock$NonfairSync.lock(ReentrantLock.java:186) 
    at java.util.concurrent.locks.ReentrantLock.lock(ReentrantLock.java:262) 
    at Locky$Boomer.run(Locky.java:22) 
    at java.lang.Thread.run(Thread.java:680) 

    Locked ownable synchronizers: 
    - <7f30c3528> (a java.util.concurrent.locks.ReentrantLock$NonfairSync) 

Это не будет работать на всех тупиков. Например, тупики из-за ожидания внешних ресурсов не будут пойманы. Но это поймает Lock-основанные тупики, а также synchronized.

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