500个工作线程,什么样的线程池?

时间:2010-05-19 18:24:33

标签: java multithreading threadpool executorservice

我想知道这是否是最好的方法。我有大约500个无限期运行的线程,但Thread.sleep在完成一个处理周期后会持续一分钟。

   ExecutorService es = Executors.newFixedThreadPool(list.size()+1);
   for (int i = 0; i < list.size(); i++) {
      es.execute(coreAppVector.elementAt(i)); //coreAppVector is a vector of extends thread objects
   }

正在执行的代码非常简单,基本上就是这个

class aThread extends Thread {
   public void run(){
      while(true){
         Thread.sleep(ONE_MINUTE);
         //Lots of computation every minute
      }
   }
}

我确实需要为每个正在运行的任务提供单独的线程,因此不能选择更改架构。我尝试使我的threadPool大小等于Runtime.getRuntime()。availableProcessors(),它试图运行所有500个线程,但只允许8(4xhyperthreading)执行。其他线程不会投降,让其他线程轮到他们。我尝试输入wait()并通知(),但仍然没有运气。如果有人有一个简单的例子或一些提示,我将不胜感激!

嗯,这个设计可能存在缺陷。线程实现了遗传编程或GP,一种学习算法。每个线程分析高级趋势进行预测。如果线程完成,则学习将丢失。那就是说,我希望sleep()允许我分享一些资源,而一个线程不是“学习”

所以实际要求是

  

如何安排维护的任务   状态并且每2分钟运行一次,但是   控制一次执行的次数。

11 个答案:

答案 0 :(得分:13)

如果您的线程没有终止,则这是线程内代码的错误,而不是线程池。有关更详细的帮助,您需要发布正在执行的代码。

另外,为什么要在完成后让每个线程都进入休眠状态;让它完成不是更好吗?

此外,我认为您通过使多个线程等于您希望执行的任务数来滥用线程池。线程池的要点是对所使用的资源数量进行约束;这种方法并不比根本不使用线程池好。

最后,您不需要将Thread的实例传递给ExecutorService,只需传递Runnable的实例。 ExecutorService维护自己的线程池,这些线程无限循环,从内部队列中拉出工作(工作是你提交的Runnable

答案 1 :(得分:10)

为什么不使用ScheduledExecutorServiceschedule each task to run once per minute,而不是让所有这些线程空闲一整分钟?

ScheduledExecutorService workers = 
  Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
for (Runnable task : list) { 
  workers.scheduleWithFixedDelay(task, 0, 1, TimeUnit.MINUTES);
}

你是什么意思,“改变架构不是一种选择”?如果您的意思是无法完全修改任务(具体而言,任务必须循环,而不是运行一次,并且无法删除对Thread.sleep()的调用),那么“良好的表现也不是一种选择”。

答案 2 :(得分:3)

我不确定你的代码在使用线程池的方式上是否正确。 ExecutionService在内部创建和管理线程,客户端应该只提供Runnable的一个实例,其run()方法将在一个池化线程的上下文中执行。您可以查看my example。另请注意,每个正在运行的线程为堆栈占用~10Mb的系统内存,而在linux上,java-to-native线程的映射是1对1。

答案 3 :(得分:2)

您应该让它返回并使用ThreadPoolexecutor执行每分钟发布到您工作队列的工作,而不是踩踏板。

答案 4 :(得分:2)

回答你的问题,什么类型的线程池?

我发表了我的评论,但这确实应该解决您的问题。您的计算可能需要2秒才能完成。您有许多任务(500),您希望尽快完成。假设没有IO和/或网络流量,您可以实现的最快吞吐量具有Runtime.getRuntime().availableProcessors()个线程数。

如果将数量增加到500个线程,则每个任务将在其自己的线程上执行,但操作系统会每隔一段时间安排一个线程以提供给另一个线程。在任何给定点都有125个上下文切换。每个上下文切换都会增加每个任务运行的时间。

这里的大图是,当您超过处理器数量时,添加更多线程并不等于更高的吞吐量。

编辑:快速更新。你不需要在这里睡觉。当您使用8个处理器执行500个任务时,每个任务将在2秒内完成,完成后运行的线程将完成下一个任务并完成该任务。

答案 5 :(得分:1)

8线程是您的系统可以处理的最大值,而且您通过上下文切换减慢了自己的速度。

查看这篇文章http://www.informit.com/articles/article.aspx?p=1339471&seqNum=4它将为您提供如何操作的概述。

答案 6 :(得分:1)

这应该做你想要的,但不是你要求的:-)你必须拿出Thread.sleep()

ScheduledRunnable.java

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class ScheduledRunnable
{
    public static void main(final String[] args)
    {
        final int numTasks = 10;
        final ScheduledExecutorService ses = Executors.newScheduledThreadPool(Runtime.getRuntime().availableProcessors());
        for (int i = 0; i < numTasks; i++)
        {
            ses.scheduleAtFixedRate(new MyRunnable(i), 0, 10, TimeUnit.SECONDS);
        }
    }

    private static class MyRunnable implements Runnable
    {
        private int id;
        private int numRuns;

        private MyRunnable(final int id)
        {
            this.id = id;
            this.numRuns = 0;
        }

        @Override
        public void run()
        {
            this.numRuns += 1;
            System.out.format("%d - %d\n", this.id, this.numRuns);
        }
    }
}

这会每隔10个SECONDS安排Runnables来显示行为。 如果您确实需要等待一段固定的时间 AFTER 处理完成后,您可能需要使用所需的.scheduleXXX方法。我认为无论执行时间是多少,fixedWait都会每N次运行一次。

答案 7 :(得分:0)

  

我确实需要为每个正在运行的任务提供单独的线程,因此不能选择更改架构。

如果为真(例如,调用外部阻塞函数),则为它们创建单独的线程并启动它们。您无法创建具有有限数量线程的线程池,因为其中一个线程中的阻塞函数将阻止任何其他runnable被放入其中,并且不会为每个任务创建一个包含一个线程的线程池。 / p>

  

我尝试使我的threadPool大小等于Runtime.getRuntime()。availableProcessors(),它试图运行所有500个线程,但只允许执行8个(4xhyperthreading)。

当您将正在创建的Thread对象传递给线程池时,它只会看到它们实现Runnable。因此,它将运行每个Runnable完成。任何停止run()方法返回的循环都不允许下一个排队的任务运行;例如:

public static void main (String...args) {
    ExecutorService executor = Executors.newFixedThreadPool(2);

    for (int i = 0; i < 10; ++i) {
        final int task = i;

        executor.execute(new Runnable () {
        private long lastRunTime = 0;
            @Override
            public void run () {

                for (int iteration = 0; iteration < 4; )
                {
                    if (System.currentTimeMillis() - this.lastRunTime > TIME_OUT)
                    {
                        // do your work here
                        ++iteration;
                        System.out.printf("Task {%d} iteration {%d} thread {%s}.\n", task, iteration, Thread.currentThread());

                        this.lastRunTime = System.currentTimeMillis();
                    }
                    else
                    {
                        Thread.yield(); // otherwise, let other threads run
                    }
                }
            }
        });
    }

    executor.shutdown();
}

打印出来:

Task {0} iteration {1} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {1} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {2} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {2} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {3} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {3} thread {Thread[pool-1-thread-2,5,main]}.
Task {0} iteration {4} thread {Thread[pool-1-thread-1,5,main]}.
Task {2} iteration {1} thread {Thread[pool-1-thread-1,5,main]}.
Task {1} iteration {4} thread {Thread[pool-1-thread-2,5,main]}.
Task {3} iteration {1} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {2} thread {Thread[pool-1-thread-1,5,main]}.
Task {3} iteration {2} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {3} thread {Thread[pool-1-thread-1,5,main]}.
Task {3} iteration {3} thread {Thread[pool-1-thread-2,5,main]}.
Task {2} iteration {4} thread {Thread[pool-1-thread-1,5,main]}.
...

显示第一个(线程池大小)任务在下一个任务调度之前运行完成。

您需要做的是创建运行一段时间的任务,然后让其他任务运行。你如何构建这些取决于你想要实现的目标

  • 您是否希望所有任务同时运行,所有任务等待一分钟,然后所有任务再次同时运行,或者任务是否彼此不同步
  • 您是否真的希望每个任务以一分钟的间隔运行
  • 您的任务是否可能阻塞,因此确实需要单独的线程
  • 如果任务阻止的时间长于预期的运行窗口
  • ,则会出现什么行为
  • 如果任务阻止的时间超过重复率(阻止超过一分钟),则会发生什么行为

根据这些问题的答案,ScheduledExecutorService,信号量或互斥量的某些组合可用于协调任务。最简单的情况是非阻塞,非同步任务,在这种情况下,使用ScheduledExecutorService直接每分钟运行一次runnables。

答案 8 :(得分:0)

您是否可以使用某些基于代理的并发框架重写项目,例如Akka

答案 9 :(得分:-1)

你需要一个信号量。

class AThread extends Thread {
   Semaphore sem;
   AThread(Semaphore sem) {
     this.sem = sem;
   }
   public void run(){
      while(true){
         Thread.sleep(ONE_MINUTE);
         sem.acquire();
         try {
           //Lots of computation every minute
         } finally {
           sem.release();
         }
      }
   }
}

实例化AThreads时,需要传递相同的信号量实例:

Semaphore sem = new Semaphore(MAX_AVAILABLE, true);

编辑:谁投了下来可以解释原因?我的解决方案有问题吗?

答案 10 :(得分:-1)

通过减少系统可以实际处理的线程数,您当然可以在吞吐量方面找到一些改进。您是否愿意稍微改变线程的设计?它会解除调度程序的负担,将睡眠状态放入队列中,而不是实际上有数百个睡眠线程。

class RepeatingWorker implements Runnable {

private ExecutorService executor;
private Date lastRan;

//constructor takes your executor

@Override
public void run() {

  try {
    if (now > lastRan + ONE_MINUTE) {
      //do job
      lastRan = now;
    } else {
      return;
  } finally {
    executor.submit(this);
  }
}
}

这保留了“无限期地重复作业的核心语义,但在执行之间至少等待一分钟”,但现在您可以将线程池​​调整为机器可以处理的内容,而将那些不起作用的内容放在队列中在调度程序中游荡作为睡眠线程。如果没有人真正做任何事情,有一些等待忙碌的行为,但我假设从你的帖子中,应用程序的整个目的是运行这些线程,并且它目前正在处理你的处理器。如果必须为其他事情做空间,你可能需要调整它:)

相关问题