Multi-threading is a widespread programming and execution model that allows multiple threads to exist within the context of one process. Each of these threads can run in parallel and these thread share similar address space. Okay let's start from the very beginning.
A thread of execution is the smallest sequence of programmed instructions that can be managed independently by a scheduler, which is typically a part of the operating system. Most times a thread exist within the process and multiple threads can exist within a single process, hence multi-threading.
I'd say a Computer Scientist would see A Thread the same way a Chemist would see an An Atom .
These threads run concurrently and they share resources. The implementation of threads and processes differ between operating systems, but in most cases a thread is a component of a process.
Processes are instances of programs which typically run independent from each other. For Example, if you start a Java program the operating system spawns a new process which runs in parallel to other programs. Inside those processes we can utilize threads to execute code concurrently, so we can make the most out of the available cores of the CPU. Unlike Threads, processes do not share resource with one another. A process is a unit of resources, while a thread is a unit of scheduling and execution.
Creating a brand-new OS thread requires memory allocation and CPU instructions in order to set it up and also kill it down. In order to better handle the usage of a thread and also avoid the creation of new ones, the operating systems or platforms reckon with a Thread Pool feature, which allows the application to take an already existing thread to use. That’s a much more efficient way to handle multiple threads without dealing with its creation or destruction. Furthermore, the OSs know when a thread from the thread pool is not actively in use thus, they can automatically “skip” it during the threads’ iteration.
The class Executors provides convenient factory methods for creating different kinds of executor services. In the sample below we use an executor with a thread pool of size one.
The result looks similar to the above sample but when running the code you'll notice an important difference, the java process never stops! Executors have to be stopped explicitly - otherwise they keep listening for new tasks.
An ExecutorService provides two methods for that purpose: shutdown() waits for currently running tasks to finish while shutdownNow() interrupts all running tasks and shut the executor down immediately. This service is mostly used when working with socket connections, to facilitate asynchronous calls(Sink-Source connections).
For the example above, we utilize Java 8 lambda expressions to print the current threads name to the console. First we execute the runnable directly on the main thread before starting a new thread. See the sample outputs below.
Hello main Hello Thread-0 Done!