Threads are expensive. They need lot of system resources, it’s time-consuming to create them and JVM is able to manage only few of them. Therefore we need asynchronous processing, new libraries, new languages etc. At least, that’s what I am hearing from everywhere. But is it really true? Are threads really so expensive? There is only one way how to find out. Just ask the machine.
The only thing we need to do is write a simple test. We just have to create as many sleeping or waiting threads as possible and we will see when it breaks. The reason why we are interested only in waiting threads is obvious. It has no sense to create thousands of active threads if we have only few CPUs. We want to simulate a situation, when threads are waiting for database data, incoming message or other CPU unrelated tasks.
public class CreateThreads { /** * Thread class * @author Lukas Krecan * */ private static final class MyThread extends Thread { private final int number; public MyThread(int number) { this.number = number; } @Override public void run() { if (shouldPrintMessage(number)) { System.out.println("Thread no. "+number+" started."); } try { //sleep forever Thread.sleep(Long.MAX_VALUE); } catch (InterruptedException e) { e.printStackTrace(); } } } /** * @param args */ public static void main(String[] args) { long startTime = System.currentTimeMillis(); final int noOfThreads = 1000000; int i=0; try { for (i=0; i < noOfThreads; i++) { if (shouldPrintMessage(i)) { System.out.println("Creating thread "+i+" ("+(System.currentTimeMillis()-startTime)+"ms)"); } new MyThread(i).start(); } } catch (Throwable e) { System.out.println("Error thrown when creating thread "+i); e.printStackTrace(); } } private static boolean shouldPrintMessage(int i) { return i % 100 == 0; } }
As you can see, there is nothing special in the test. Every hundredth threads prints a message to console and all of them sleep forever. In the main method we are creating threads and when an exception is thrown, number of created threads is printed out.
Even though there is nothing special on the test, the results are really interesting and for me quite surprising. I will let you guess the results. The test is executed on my two years old laptop with Intel Core 2 Duo T8100 2.1GHz. There is 64bit Linux 2.6.31 and OpenJDK (IcedTea6 1.6.1) running on top of it.
Try to guess how many threads are created if I do not tinker with JVM arguments at all. Is it 1K? Or 10K? Or even more? Well, it's slightly more than 32K. Usually you see result like this
Creating thread 31900 (37656ms) Thread no. 31900 started. Creating thread 32000 (37736ms) Thread no. 32000 started. Creating thread 32100 (37818ms) Thread no. 32100 started. Error thrown when creating thread 32172 java.lang.OutOfMemoryError: unable to create new native thread at java.lang.Thread.start0(Native Method) at java.lang.Thread.start(Thread.java:614) at CreateThreads.main(CreateThreads.java:43)
The question is, why we can not create more threads. Are we constrained by the heap size? Or maybe stack size? No in fact, 32K is apparently Linux kernel limit. Therefore there is nothing we can do in Java, Linux is just not able to handle more threads. But I think that 32K threads is not bad at all. Please keep in mind, that you will be able to reproduce the results only on modern operating systems and JVMs. I am afraid that Windows XP results will be much worse.
Please also note, that once the threads are created and all of them sleep, the application does not consume any CPU and it consumes around 800MB of memory. I am not sure why so much memory is used, it has to be investigated.
As we have seen, in this artificial scenario threads are relatively cheap. I do not want to say, that we should not use new libraries or tools. Most of them are quite useful. The only thing I want to say is that we should think twice before prematurely optimizing our applications. Sometimes the most straightforward and easiest approach is the best.
Next time, I will try to do some more real life example with servlet container.