Understanding JVM threads

Threads are the building blocks of asynchronous concurrent applications on JVM (and other platforms, too). A JVM thread is, most of the time, backed by a hardware thread (such as a core inside a processor). A hardware thread can support several software threads (a JVM thread is a kind of software thread), but only one software thread is executed at any given time.

The OS (or the JVM) decides which software thread is executed on each hardware thread and switches quickly among the live threads, thereby, giving the appearance that there are several software threads executing at the same time, when in reality there are as many active software threads being executed as there are hardware threads. But, in most circumstances, it is useful to think that all software threads are being performed at the same time. 

Threads in JVM are very fast and responsive, but they come at a cost. Each Thread has a cost in CPU time and memory on creation, disposal (when garbage is collected), and context switch (the process to store and recover a thread's state when it becomes the executing thread or stops being it). Because this cost is relatively high, a JVM application can't have a significant number of threads.

A JVM application on a typical development machine can easily handle 100 threads:

fun main(args: Array<String>) {
val threads = List(100){
thread {
Thread.sleep(1000)
print('.')
}
}
threads.forEach(Thread::join)
}

If you use any external application to monitor the JVM application, such as VisualVM or JConsole (among others), you'll see a graphic like this:

We can increase our threads to 1,000 as shown in the following screenshot:

The amount of memory is growing at a fast rate, reaching more than 1.5 GB.

Can we increase our threads to 10,000? Take a look at the following screenshot:

The answer is a blunt no; around 2,020 threads were created when the application died with OutOfMemoryError (this application was running with default settings; those settings can be changed at startup time).

Let's try with 1,900, a fair estimate of what we can execute safely:

Yes, we can run 1,900 concurrent threads.

In modern JVM applications, creating and destroying threads is considered a bad practice; instead, we use Executor, an abstraction that lets us manage and reuse threads, reducing the cost of creation and disposal:

import java.util.concurrent.Executors
import java.util.concurrent.TimeUnit

fun
main(args: Array<String>){
val executor = Executors.newFixedThreadPool(1024)
repeat(10000){
executor.submit {
Thread.sleep(1000)
print('.')
}
}
executor.shutdown()
}

We created an executor value that, internally, has a thread pool of up to 1,024 threads. Then, we submit 10,000 tasks; at the end, we shut down Executor. When we shut down Executor, it can't accept new tasks and executes all pending ones as follows:

There are many options to fine-tune and play with, Executor, such as the number of threads and the type of pool or its actual implementation.

There is a lot more theory on JVM threads than is possible to cover in this book. If you want to read and learn more about threads and concurrency, we recommend the classic book, Java Concurrency in Practice (2006) by Dough Lea, David Holmes, Joseph Bower, Joshua Block, Tim Peierls, and Brian Goetz, from Addison-Wesley Professional. We also recommend Programming Concurrency on the JVM (2011) by Venkat Subramanian from Pragmatic Bookshelf, and the Java Concurrency LiveLessons (2015) video by Douglas Schmidt from Addison-Wesley Professional. Last but not least,  we suggest the series of books and videos, Java Concurrency by Javier Fernández Gonzáles, published by Packt. 
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.149.228.138