我应该保持长时间运行线程还是使用Executors?

时间:2017-12-12 07:54:49

标签: java multithreading

我正在使用一个使用多个长时间运行的工作线程的流式java应用程序。应用程序接收数据,对其进行处理,然后使用SDK将其发送给第三方。有一个 Engine 类可以接收数据并将其提交给 Workers 。只要应用程序运行,工作线程就会活动,这可能是几个月甚至几年。

我已经包含了代表此问题的关键部分的示例代码。

import java.util.Map;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.TimeUnit;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class BarEngine implements Engine
{
   static Logger log = LoggerFactory.getLogger(BarEngine.class);

   private static final int WORKER_COUNT = 5;
   private BlockingQueue<Map<String, Object>> queue;

   private FooWorker[] workers = new FooWorker[WORKER_COUNT];

   public BarEngine()
   {
      for (int i = 0; i < WORKER_COUNT; i++)
      {
         workers[i] = new FooWorker(i, queue);
         workers[i].start();
      }
   }

   // From Engine Interface
   @Override
   public void sendEvent(final Map<String, Object> data)
   {
      try
      {
         queue.put(data);
      }
      catch (InterruptedException e)
      {
         log.error("Unexpected Exception", e);
      }
   }

   // From Engine Interface
   @Override
   public void shutDown()
   {
      // Shuts down engine

   }

   public static class FooWorker extends Thread
   {
      static Logger log = LoggerFactory.getLogger(FooWorker.class);

      private volatile boolean run = true;
      private int id;
      private BlockingQueue<Map<String, Object>> queue;
      private Client client;

      public FooWorker(int id, BlockingQueue<Map<String, Object>> queue)
      {
         this.id = id;
         this.queue = queue;
         client = Client.build(id);
      }

      @Override
      public void run()
      {
         setName("FooWorker-" + id);
         while (run)
         {
            try
            {
               Map<String, Object> data = queue.poll(5, TimeUnit.SECONDS);
               if (null != data)
               {
                  sendEvent(data);
               }
            }
            catch (Throwable e)
            {
               log.error("Unexpected Exception", e);
            }
         }
      }

      private void sendEvent(Map<String, Object> data)
      {
         try
         {
            client.submit(data);
         }
         catch (Throwable e)
         {
            log.error("Unexpected Exception", e);
         }
      }

      // dummy client classs
      public static class Client
      {
         public void submit(Map<String, Object> data)
         {
            // submits data
         }

         public static Client build(int id)
         {
            // Builds client
            return new Client();
         }
      }
   }
}

我一直在做一些研究,但我没有找到满意的答案。

我的问题是:我应该保持这些长时间运行Threads 吗?如果没有,我应该用什么替换它(如ExecutorService或其他东西)?

2 个答案:

答案 0 :(得分:0)

回答你的问题,如果你的线程与应用程序的生命周期相同,在我看来,如果你使用的是Thread或Executer服务(它再次使用下面的Threads)并不重要你正确地管理线程的生命周期。

从设计的角度来看,您的应用程序属于软件类别,我们称之为&#34;中间件&#34;。通常,中间件应用程序应该既高效又可扩展,这两者都是此类服务器的基本特性,但您忽略了这两者。您的应用程序的线程在每个线程上运行busy-wait循环,始终保持CPU忙。即使输入负载非常低,这种情况仍然存在。这种应用的质量不高。

或者,我建议您使用ThreadPool实施,例如ThreadPoolExecutor已经解决了您要在此处尝试完成的任务。如果此时所有最初启动的线程都忙,ThreadPoolExecutor会利用BlockingQueue的功能。如果负载较低,它也可以停止线程,如果需要,它可以再次启动。我编写了我提出的设计结构。看看下面的代码。我假设Client不是线程安全的,所以我每个线程构造一个Client。如果您的真实客户端实现是线程安全的,则可以在所有线程中使用一个客户端。

import java.util.Map;
import java.util.concurrent.*;

public class BarEngine implements Engine {

    private static final int WORKER_COUNT = 5;
    private ExecutorService threadPool;

    public BarEngine() {
        this.threadPool = new ThreadPoolExecutor(1, WORKER_COUNT, 10, TimeUnit.MILLISECONDS, new ArrayBlockingQueue<Runnable>(100));
    }

    // From Engine Interface
    @Override
    public void sendEvent(final Map<String, Object> data) {
        threadPool.submit(new FooWorker(data));
    }

    // From Engine Interface
    @Override
    public void shutDown() {
        this.threadPool.shutdown();
        // Shuts down engine

    }

    public static class FooWorker implements Runnable {

        private final Client client;
        private final Map<String, Object> data;

        public FooWorker(Map<String, Object> data) {
            client = Client.build(Thread.currentThread().getId());
            this.data = data;
        }

        @Override
        public void run() {
            try {
                if (null != data) {
                    sendEvent(data);
                }
            } catch (Throwable e) {
                //todo log
            }
        }

        private void sendEvent(Map<String, Object> data) {
            try {
                client.submit(data);
            } catch (Throwable e) {
                //todo log
            }
        }

        // dummy client classs
        public static class Client {

            public void submit(Map<String, Object> data) {
                // submits data
            }

            public static Client build(long id) {
                // Builds client
                return new Client();
            }
        }
    }
}

答案 1 :(得分:0)

是的,您发布的内容正是ExecutorService的内容。一些建议:

  • Executor有两个主要接口,ExecutorService。
  • 如果您不需要ExecutorService接口上的shutdown / shutdownNow方法,则应该终止执行程序。如果没有,你将面临memleaks。
  • 通过执行程序创建ExecutorService,例如:

    Executors.newFixedThreadPool(5);

如果您使用Executor服务,您可以通过Runnable将数据直接推送到ExecutorService,因为ExecutorService自行排队并将任务拆分为其工作人员......