53

I have to send out massEmails to all users of a website. I want to use a thread pool for each email that is sent out. Currently I have set the values to :

<property name="corePoolSize" value="500" />
<property name="maxPoolSize" value="1000" />

What is the difference between the two and will it scale. Currently I have approx. 10000 users.

shmosel
  • 49,289
  • 6
  • 73
  • 138
rabbit
  • 531
  • 1
  • 4
  • 3

7 Answers7

64

Here are Sun’s rules for thread creation in simple terms:

  1. If the number of threads is less than the corePoolSize, create a new Thread to run a new task.
  2. If the number of threads is equal (or greater than) the corePoolSize, put the task into the queue.
  3. If the queue is full, and the number of threads is less than the maxPoolSize, create a new thread to run tasks in.
  4. If the queue is full, and the number of threads is greater than or equal to maxPoolSize, reject the task.

Full article

Origin answer

taynguyen
  • 2,961
  • 1
  • 26
  • 26
  • So, could you explain this strange behavior in this `ThreadExecutor` [program](https://stackoverflow.com/questions/52253164/threadpoolexecutor-with-corepoolsize-0-should-not-execute-tasks-until-task-queue). It is not following the 4 cases you mentioned here. – Steve Sep 13 '18 at 13:44
49

The javadoc says it best:

When a new task is submitted [...], and fewer than corePoolSize threads are running, a new thread is created to handle the request, even if other worker threads are idle. If there are more than corePoolSize but less than maximumPoolSize threads running, a new thread will be created only if the queue is full. By setting corePoolSize and maximumPoolSize the same, you create a fixed-size thread pool. By setting maximumPoolSize to an essentially unbounded value such as Integer.MAX_VALUE, you allow the pool to accommodate an arbitrary number of concurrent tasks.

As for your specific situation, sending 500 emails all at the same time is pointless, you'll just overwhelm the mail server. If you need to send a large number of emails, then use a single thread, and send them down the pipe one at a time. The mail server will handle this much more gracefully than 500 separate connections.

skaffman
  • 398,947
  • 96
  • 818
  • 769
  • I am not able to see this happening in this `ThreadExecutor` [program](https://stackoverflow.com/questions/52253164/threadpoolexecutor-with-corepoolsize-0-should-not-execute-tasks-until-task-queue). Can you explain what's different here? – Steve Sep 13 '18 at 13:46
23

corePoolSize is the minimum number of threads used by the pool. The number can increase up to maxPoolSize. When the load goes down, the pool will shrink back to corePoolSize.

Sending email seems to be an I/O bound operation. I do not think having 500 threads will make it faster.

Thilo
  • 257,207
  • 101
  • 511
  • 656
11

We already have so many answers and would suffice, but I always confused every other day. So I came up with a real time example to relate this!

A simple and efficient way to get this is by considering yourself being in a bank.

You ever stood in a line even though there is a window available? Why would the bank keep a customer in a queue if there are available cash-window? That's exactly the case with core pool size. If there are un-used threads, new tasks are directly allocated to them.

enter image description here

One the available window(core pool size) is full, customers are asked to wait in the queue. This is where the queue-capacity comes in picture! New tasks are keep on queueing until no more tasks can be queued.

enter image description here

What if the wait-lounge is full of customers? Bank can allocate/open new cash-windows as they see spike in number of customers(tasks). They had 3 more in reserve. Here comes the max pool size in context.

Once these windows(threads) are all occupied, Bank can no longer server any new customers(tasks). Agree? And henceforth, new tasks are not accepted!

enter image description here

Kumar Ashutosh
  • 1,121
  • 10
  • 33
5

In addition to what @skaffman pointed out from official docs, following also makes it more clear how these sizes are utilized:

Any BlockingQueue may be used to transfer and hold submitted tasks. The use of this queue interacts with pool sizing:

  • If fewer than corePoolSize threads are running, the Executor always prefers adding a new thread rather than queuing.
  • If corePoolSize or more threads are running, the Executor always prefers queuing a request rather than adding a new thread.
  • If a request cannot be queued, a new thread is created unless this would exceed maximumPoolSize, in which case, the task will be rejected.
Mahesha999
  • 22,693
  • 29
  • 116
  • 189
4

You should consider to increase value of queueCapacity than consider to increase value of corePoolSize or maxPoolSize. Those two properties (*PoolSize) are number of pool to execute but each message would be considering in queueCapacity

<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="queueCapacity" value="1000" />
<property name="waitForTasksToCompleteOnShutdown" value="true"/>

If you have 10000 users to send so 1000 * 10 (maxPoolSize) = 10000 but if 1000 for each thread is heavy, we can consider to increase poolSize.

Osify
  • 2,253
  • 25
  • 42
  • 1
    Queue capacity is at Integer.MAX_VALUE by default, so no need to increase it! – rlm Mar 24 '16 at 16:24
  • 2
    @rlm, in that case, no more threads will be created and thread will remain at `corePoolSize`. – Mahadeva Sep 23 '18 at 22:28
  • 1
    @rlm. This contradicts the answer by @taynguyen? According to that answer, a new thread will only be created if queue is full. – BillMan Apr 11 '19 at 16:58
  • @BillMan, answer of taynguyen is sun implementation but developer who ask the question talks about "Spring ThreadPoolTaskExecutor". In "Spring ThreadPoolTaskExecutor", queue capacity is initialized at integer max value and max pool size too so that "allow the pool to accommodate an arbitrary number of concurrent tasks (skaffman)" – rlm Jul 08 '19 at 10:22
  • @rlm: The default configuration is a core pool size of 1, with unlimited max pool size and unlimited queue capacity. This is roughly equivalent to Executors.newSingleThreadExecutor() – BillMan Jul 09 '19 at 14:05
  • @Pant & @BillMan are right. In my experience too, by default in Spring `corePoolSize` will be the maximum number of threads created as default queue capacity is `Integer.MAX_VALUE`. – aksh1618 Sep 27 '19 at 05:39
0

What everyone has explained so clearly is correct. Few things to notice here is that you should always have a limited size for core-pool as well as queue. If the core-pool-size if very high, there can a high chance that many of your threads from pool are remaining unused for certain period of time since for every request new thread gets created until it reaches max-pool-size

But if your machine is going to face large number of request concurrently then you should also consider that the machine size is sufficient enough : example:

If your machine size in 1 GB and queue capacity is at Integer.MAX_VALUE then there is a high chance that your machine will start rejecting the requesting at some point of time because of OutOfMemory which you can monitor in any JVM GUI tool.

SinhaOjas
  • 134
  • 8