Использование wait () внутри синхронизированного блока - PullRequest
0 голосов
/ 17 октября 2018

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

    public class MyService extends IntentService{

     protected void onHandleIntent(Intent intent){
     synchronized(this){
        try{
           wait(10000);         
           }catch(InterruptedException e){
           e.printStackTrack();
         }
        String message = intent.getStringExtra("Message");
        showMessage(message);
       }
      }
    }

Означает ли приведенный выше код, что любое количество потоков может войти в блок synchronized?Я знаю, что sleep переводит Thread в заблокированное состояние.Это то же самое с Thread, вызывающим wait()?Обычно, когда я передаю текст в Service, я хочу, чтобы Service подождал 10 секунд, а затем отобразил сообщение в LogCat.Я никогда не использовал wait() в любое время, поэтому кто-нибудь может мне объяснить, что делает приведенный выше код?

Ответы [ 2 ]

0 голосов
/ 17 октября 2018

Ваше утверждение "любое количество потоков может войти в блок synchronized" ложно.

Теоретически, если один поток находится внутри блока synchronized, это препятствует проникновению других потоков.Это невозможно в случае IntentService, поскольку IntentService использует один рабочий поток для обработки рабочей нагрузки.

Вызов wait() - это метод синхронизации потоков, а не метод задержки.Это отличается от вызова sleep(), который просто блокирует поток на определенное время.Когда вы вызываете wait(), это блокирует поток, пока другой поток не вызовет notify(), который используется для координации действий между несколькими потоками.wait(10000) блокирует поток до тех пор, пока либо notify() не будет вызван из другого потока ИЛИ, пока не истечет время ожидания (в данном случае 10 секунд).Таким образом, похоже, что где-то должен быть другой поток, который вызывает notify() объекта IntentService, чтобы разбудить его.

Здесь есть дополнительная сложность, связанная с использованием notify() и wait().Чтобы вызвать любой из этих методов, сначала необходимо получить блокировку на мониторе объекта (с помощью synchronized).Это означает, что вызовы wait() и notify() должны быть в пределах synchronized блоков или в synchronized методах, которые synchronized для объекта.

Поток, который вызывает wait(), фактически освобождаетзамок на объекте.Это означает, что поток заблокирован внутри synchronized блока / метода, но у него нет блокировки объекта во время ожидания .После вызова notify() (или по истечении времени ожидания) поток восстановит блокировку объекта и продолжит выполнение.

Для получения дополнительной информации об использовании notify() и wait() в Java найдитеэти термины и читайте об этом.


Этот код довольно запутанный, если все, что он должен сделать, это задержать 10 секунд, а затем написать что-то для logcat.Вы можете просто позвонить sleep() вместо wait(), что не потребует оператора synchronized.Однако, как отметил другой автор, если этот Service вызывается очень часто, это создаст отставание, так как каждый вызов onHandleIntent() будет задерживаться на 10 секунд, и поскольку существует только 1 рабочий поток, все вызовы сериализуются.,Пример: звонок на startService() сделан в 10:00:00, запись в logcat появится в 10:00:10.Если в 10:00:01 будет сделан другой вызов startService(), эта запись не будет отображаться в журнале до 10:00:20, поскольку второй вызов onHandleIntent() не произойдет до 10:00: 10.

0 голосов
/ 17 октября 2018

Часть вашего вопроса касается многопоточности, которая является довольно сложной темой.Я рекомендую начать с учебника, подобного этому , чтобы получить эти ответы.

Приведенный выше код будет служить для задержки записей журнала на 10 секунд.Однако IntentService имеет только 1 рабочий поток, поэтому последующие запросы будут задерживаться, если они происходят чаще, чем раз в 10 секунд.

Поскольку задействован только 1 рабочий поток, использование синхронизации действительно является неправильным ответом.Почему бы не отказаться от IntentService и просто сделать все это в потоке пользовательского интерфейса, используя CountDownTimer?

final String msg = intent.getStringExtra("Message");

new CountDownTimer(10000, 10000) {
    @Override
    public void onTick(long millisUntilFinished) {}

    @Override
    public void onFinish()  {
        showMessage(msg);
    }
}.start();

Таким образом, вы можете создать внешний вид параллельного выполнения, безсложность многопоточности благодаря использованию встроенной в Android системы очередей сообщений .

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...