2017-02-19 4 views
0

У меня есть класс, который FileReader как этотJava ждать оповещать двух потоков в одном классе

public class FileReader extends Thread 
{ 

private final Object lock = new Object(); 

public FileReader(String path, FileReaderCallback callback) 
{ 
    super(path); 

    this.path = path; 
    this.callback = callback; 
} 

@Override 
public void run() 
{ 
    try 
    { 
     BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(path))); 
     String info; 

     while ((info = reader.readLine()) != null) 
     { 
       synchronized (lock) 
       { 
        callback.onDone(path, info); 

        try 
        { 
         lock.wait(); 
        } 
        catch (Exception ignored) 
        { 
        } 
       } 
      } 
     } 
    } 
    catch (Exception e) 
    { 
     e.printStackTrace(); 
    } 
} 

public void next() 
{ 
    synchronized (lock) 
    { 
     try 
     { 
      lock.notify(); 
     } 
     catch (Exception e) 
     { 
      e.printStackTrace(); 
     } 
    } 
} 

} 

И у меня есть два экземпляра этого FileReader, потому что я хочу прочитать два файла построчно одновременно. Проблема в том, что мой код читает только одну строку из обоих файлов, а затем он приостанавливается.

I Вызов функции обратного вызова на мой, как этот

public void onDone(String path, String info) 
{ 
    reader1.next(); 
    reader2.next(); 
} 

Так в чем проблема ?!

Заранее спасибо

+0

Где ваши замки? Как вы их получите? Пожалуйста, включите все ваши занятия. – ram

+0

@ram Я редактировал свой пост. моя блокировка находится внутри класса FileReader, но функция onDone находится где-то в другом месте (в моем основном классе) – strings95

+0

'lock.wait()' ваш текущий код находится в этой строке, и вы никогда не показываете, где вы вызываете 'next()' – nachokk

ответ

1

Ваш lock объект, который вы синхронизируете метод next() к также используется в вашей while петли в методе run. Поэтому код вашего метода next() не может быть вызван из другого потока.

Просто предположим следующий программный поток:

  1. Вы начинаете reader1 нить
  2. Вы начинаете reader2 нить

В какой-то момент один из этих двух потоков запуска. Давайте предположим, что reader1 нить начинает первый:

  1. синхронизирует его lock объекта
  2. Он считывает строку из файла
  3. Он называет свою функцию обратного вызова, то есть вызовы next() на reader1 и reader2. Этот вызов успешно (но на самом деле нет-op)
  4. Он называет wait на своем lock объекте. И ждет ...

В более позднее время reader2 поток начинает

  1. синхронизирует его lock объекта
  2. Он считывает строку из файла
  3. Он называет свою функцию обратного вызова, однако , при вызове reader1.next() он пытается синхронизировать с reader1 свой объект lock из другого потока, тем самым помещая вашу программу в состояние взаимоблокировки.

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

0

Вы звоните слушателю перезвонить, удерживая блокировку на одном объекте lock. Это позволит вызывать уведомление перед вызовом wait. Это заставит ваш поток ждать навсегда.

Вы должны,

  1. Использование java.util.CountDownLatch для этой проблемы.
  2. Используйте ThreadPool. Расширение от потока - это старый способ сделать это и подвержен ошибкам.
0

Вы столкнулись с классическим сценарием тупика. Пусть первый замок будет lock1, а второй замок - lock2. В своем первом случае, состояние блокировки может быть выражена следующим образом:

synchronized (lock1) { 
    // start of onDone 
    synchronized (lock1) { 

    } 
    synchronized (lock2) { 

    } 
    // end of onDone 
} 

и второй, это так:

synchronized (lock2) { 
    // start of onDone 
    synchronized (lock1) { 

    } 
    synchronized (lock2) { 

    } 
    // end of onDone 
} 

Вы должны уточнить свою логику, как и другие ответы предполагают.

Другой недостаток в вашем дизайне; вы также не рассматриваете возможные побочные пробуждения. Как правило, вы должны поместить свои wait() звонки в цикл while.