【问题标题】:Using wait() inside a synchronized block在同步块中使用 wait()
【发布时间】:2018-10-16 21:41:38
【问题描述】:

我偶然发现了一个 Android Service 类中的一段代码,它有一个带等待语句的同步块。 代码如下:

    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块?我知道sleepThread 置于阻塞状态。这与Thread 调用wait() 相同吗? 基本上,当我将文本传递给Service 时,我希望Service 等待10 秒,然后在LogCat 中显示消息。 我从来没有使用过wait(),所以有人可以向我解释一下上面的代码在做什么吗?

【问题讨论】:

    标签: java android concurrency android-service java-threads


    【解决方案1】:

    您的说法“任意数量的线程都可以进入synchronized 块”是错误的。

    理论上,如果一个线程在synchronized 块内,这会阻止其他线程进入。这在 IntentService 的情况下是不可能的,因为 IntentService 使用单个工作线程来处理工作负载。

    调用wait()是线程同步方式,不是延迟方式。这与调用sleep() 不同,后者只是在特定时间内阻塞线程。当您调用wait() 时,这会阻塞线程,直到另一个线程调用notify(),它用于协调多个线程之间的活动。 wait(10000) 阻塞线程,直到从另一个线程调用 notify() 或直到超时到期(在本例中为 10 秒)。所以这看起来应该有另一个线程在 IntentService 对象上调用 notify() 来唤醒它。

    这里有一个额外的复杂性与notify()wait() 的使用相关。为了调用这些方法中的任何一个,必须首先获得对象监视器上的锁(通过使用synchronized)。这意味着对wait()notify() 的调用必须在synchronized 块内或在对象上synchronizedsynchronized 方法内。

    调用wait()的线程实际上释放了对象上的锁。这意味着线程在 synchronized 块/方法中被阻塞,但它在等待时没有锁定对象。一旦调用notify()(或超时),线程将重新获得对该对象的锁定并继续执行。

    有关在 Java 中使用 notify()wait() 的更多信息,请搜索这些术语并阅读相关内容。


    如果它应该做的只是延迟 10 秒然后向 logcat 写入一些东西,那么这段代码就相当复杂了。您可以只调用sleep() 而不是wait(),后者不需要synchronized 语句。然而,正如另一张海报所指出的,如果这个Service 被频繁调用,这将创建一个积压,因为对onHandleIntent() 的每次调用都会延迟10 秒,并且由于只有1 个工作线程,所有调用都会被序列化.示例:在 10:00:00 调用 startService(),logcat 中的条目将出现在 10:00:10。如果在 10:00:01 再次调用 startService(),则该条目直到 10:00:20 才会出现在 logcat 中,因为对 onHandleIntent() 的第二次调用要到 10:00:10 才会发生。

    【讨论】:

      【解决方案2】:

      您的部分问题涉及多线程,这是一个非常复杂的主题。我建议从tutorial like this one 开始以获得这些答案。

      上面的代码将日志条目延迟 10 秒。但是,IntentService 只有 1 个工作线程,因此如果连续请求出现的频率超过每 10 秒一次,则会积压。

      因为只涉及 1 个工作线程,所以使用同步确实是错误的答案。为什么不放弃 IntentService,只在 UI 线程上使用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 的built-in message queueing system 来创建并行执行的外观,而无需多线程的复杂性。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-11
        • 1970-01-01
        相关资源
        最近更新 更多