【问题标题】:concurrency issue when implementing a simple httprequest queue for android app为android应用程序实现一个简单的httprequest队列时的并发问题
【发布时间】:2013-01-22 00:09:46
【问题描述】:

当我尝试从头开始为 http 请求实现队列时遇到问题。抱歉,这对某些人来说可能是一个非常幼稚的并发问题。

基本上,我希望我的应用程序在任何时候都只执行一个请求。额外的请求进入队列并稍后执行。

我知道其他高级的东西,例如 FutureTask 和 Execution pool,但我想要答案,因为我很好奇如何解决基本的并发问题。以下是我的类维护 requestQueue

private Queue<HttpRequest> requestQueue;
private AsyncTask myAsyncTask=null;

public boolean send(HttpRequest hr){
   //if there isn't existing task, start a new one, otherwise just enqueue the request
   //COMMENT 1.
   if(myAsyncTask==null){
     requestQueue.offer(hr);
     myAsyncTask= new RequestTask();
     myAsyncTask.execute(null);
     return true;
   }
   else{
     //enqueue
     //COMMENT 2
     requestQueue.offer(hr);
   }

}

//nested class
RequestTask extends AsyncTask<boolean,void,void>{
      protected HttpResponse doInBackground(void... v){
           //send all request in the queue
           while(requestQueue.peek != null){
              HttpResquest r= requestQueue.poll
              //... leave out code about executing the request
           }
           return true;
      }

      protected void doPostExecute(boolean success){


           //COMMENT 3: if scheduler stop here just before myAsyncTask is set to null
           myAsyncTask=null;

      }
}

问题是,如果线程调度程序在 COMMENT 3 点停止后台线程(就在 myAsyncTask 设置为 null 之前)。

      //COMMENT 3: if scheduler stop here just before myAsyncTask is set to null
       myAsyncTask=null;

此时,其他线程碰巧到 COMMENT 1 点,进入 if ... else ... 块。因为 myAsyncTask 没有设置为 null,所以任务在 else 块中排队(注释 2),但不会创建新的 asyncTask,这意味着队列会卡住!

   //COMMENT 1.
   if(myAsyncTask==null){
     requestQueue.offer(hr);
     myAsyncTask= new RequestTask;
     myAsyncTask.execute(null);
     return true;
   }
   else{
     //enqueue
     //COMMENT 2
     requestQueue.offer(hr);
   }

我希望它很清楚。队列有可能停止处理。我很想知道如何避免这种情况。提前谢谢你

【问题讨论】:

    标签: java android concurrency request queue


    【解决方案1】:

    我通常实现这样的方式是创建一个扩展线程的类。这将包含一个队列对象(使用您喜欢的任何一个)并具有添加作业的方法。我会使用同步来保证所有线程的安全。可以使用通知和等待来避免轮询。

    这是一个可能有帮助的例子......

        import java.util.*;
    
        public class JobProcessor extends Thread
        {
           private Queue queue = new LinkedList();
    
           public void addJob(Object job)
           {
               synchronized(queue)
               {
                  queue.add(job);
                  queue.notify(); // lests the thread know that an item is ready
               }
           }
    
    
           @Overide
           public void run()
           {
              while (true)
              {
                  Object job = null;
    
                  synchronized(queue) // ensures thread safety
                  {
                     // waits until something is added to the queue.
                     try
                       while (queue.isEmpty()) queue.wait();
                     catch (InterruptedException e)
                       ; // the wait method can throw an exception you have to catch.
                     // but can ignore if you like.
    
                     job = queue.poll();
                  }
                  // at this point you have the job object and can process it!
                  // with minimal time waiting on other threads.
                  // be sure to check that job isn't null anyway!
                  // in case you got an InterruptedException.
    
                  ... processing code ...
    
                  // job done loop back and wait for another job in the queue.
              }
           }
        }
    

    您几乎只需像这样实例化一个类并启动线程,然后开始插入对象来处理作业。当队列为空时,等待会导致该线程休眠(并且还会暂时释放同步锁),addJob 方法中的 notify 会在需要时将其唤醒。同步是一种确保只有一个线程可以访问队列的方法。如果您不确定它是如何工作的,请在 java SDK 参考中查找。

    您的代码中没有任何线程安全代码(同步的东西),这就是您的问题所在。它可能有点过于复杂,也无法帮助您调试它。但最主要的是你需要添加同步块,但要确保它们尽可能短。

    【讨论】:

    • 谢谢。我知道同步块,但我认为这无法解决我的代码中的问题。您的代码中对我来说新的是关键字通知和等待。我在我的代码中使用了一个标志(AsyncTask==null?),而不是我需要尝试通知并等待,再次感谢你:)
    • 如果您不热衷于使用同步块,那么在 java.util.concurrent 中有一个特殊的队列用于处理线程安全,称为 ConcurrentLinkedQueue,它将确保您在访问无论如何都要排队
    • 您好,感谢您的回复。我调查了同步块,我认为你确实是对的。还有一个问题,我认为可能还需要在代码中的“if (queue.isEmpty()) queue.wait()”上放置一个同步块,.假设线程 1 到达“if (queue.isEmpty())”(这是真的)。就在调用“queue.wait();”之前,线程 2 调用“addjob()”并且调用在“queue.wait();”之前返回在线程 1 中执行。然后“queue.notify()”将不起作用,因为线程1没有调用“queue.wait();”。当 Thread 1 开始 wait() 时,最后一个作业会卡在队列中。
    • 啊,是的,你在技术上是正确的,另一个同步块是个好主意。请注意,wait 方法会释放同步,直到线程重新启动。我将编辑代码以向您展示它应该如何工作。
    • 嗯,你的笔记给我留下了另一个问题。如果将通知和等待都放在同步块中。在我看来,这是一个死锁。如果一个线程开始等待,则没有其他线程可以调用 notify。
    猜你喜欢
    • 1970-01-01
    • 2015-02-12
    • 1970-01-01
    • 2011-06-21
    • 1970-01-01
    • 2015-08-26
    • 1970-01-01
    • 1970-01-01
    • 2014-07-15
    相关资源
    最近更新 更多