thread
thread
In Java, multithreading is the concurrent execution of two or more threads to maximize the
utilization of the CPU. Java's multithreading capabilities are part of the java.lang package, making it
easy to implement concurrent execution.
In Single core environment, java’s multithreading is managed by the JVM and OS, which switch
between threads to give illusion of concurrency. The thread shares single core, and time slicing is
used to manage thread execution.
In multi core environment, java’s multithreading can take full advantage of the available cores. The
JVM can distribute threads across multiple cores, allowing true parallel execution of thread.
What Is Thread?
It is a lightweight process, the smallest unit of processing. Java supports multithreading through its
java.lang package
When a Java program starts, one thread begins running immediately, which is called the main thread.
This thread is responsible for executing the main method of a program.
New: A thread is in this state when it is created but not yet started. In code we create a object of
thread.
Runnable: After the start method is called, the thread become runnable. It’s ready to run and is
waiting for CPU time. In code when we write thread.start().
Running: The thread is in this state when it is executing. In code Run method will get executed.
Blocked/waiting: When thread is waiting for some resource or for some another thread to perform
some action.
Thread. Join(): it will ensure thread will finishes before moving to the execution of the next line.
We have studied that whenever we need to extend some class and use thread then we use runnable
but why? Because we cannot extend two or more classes as java does not support multiple
inheritance.
High Priority has value 10, medium has 5 and low has 1.
Note: Jaroori nahi he ki pehle saari high priority task execute hongi fir low priority wale. Ye ek hint
hota he jvm ke liye ki kisi bhi 2 me se high priority shoose karo. Ye isliye hota he kyuki hum multi core
cpu use kar rahe he jo ki ek time me 2 or use jada thread execute karenge.
Thread.interrupt(): The primary purpose of the interrupt() method in Java is to signal a thread that it
should stop or change its operation. It does not forcibly stop a thread; instead, it sets an interrupted
status flag on the thread, which must be checked by the thread's code and acted upon.
The Thread.stop() method is not used because it forcibly stops a thread without allowing it
to clean up. This can lead to inconsistent state, resource leaks, and deadlocks in your
program.
Thread.yield(): the yield() method is part of the Thread class and provides a hint to the thread
scheduler that the current thread is willing to yield/give its execution time for other threads.
However, it does not guarantee that the thread will stop or that another thread will immediately run
Daemon Thread: daemon thread is a background thread that provides services to other threads and
does not prevent the JVM from exiting when the application’s non-daemon (user) threads finish
execution. The JVM will terminate as soon as all user threads have completed, even if daemon
threads are still running.
A daemon thread is terminated automatically when all user threads in the program have
finished execution
Daemon threads run in the background and are typically used for tasks like monitoring,
garbage collection, or other low-priority services.
To mark a thread as a daemon, use the setDaemon(true) method before starting the thread.
It is the mechanism which ensures that two or more concurrent thread does not simultaneously
execute the same code which has shared resources to prevent from inconsistency.
Extrinsic: They are more advanced and we can control them using Lock class from
java.util.concurrent.locks package. We explicitly define when to lock and unclock. ReentrantLock
class is to initialize lock object and it must be final.
Example to show why lock is required and why synchronized cannot always be used.
So, in the above code we can see thread 1 will start executing and it will go to sleep for 3 second this
sleep operation represent some database operation, as we have used synchronized, we thread 2 is
not doing any thing and must have to wait.
o If a thread acquires a normal mutex and then tries to lock it again before unlocking
it, the thread blocks (waits indefinitely) because the lock is already held by the same
thread.
2. Reentrant Lock
o A reentrant lock allows the same thread to acquire the lock multiple times without
blocking.
o It keeps a count of how many times the thread has acquired it.
o The thread must release the lock the same number of times before it is fully
unlocked.
Unlike synchronized, ReentrantLock must be manually locked and unlocked, giving more control over
thread synchronization.
Reentrant Behavior: The same thread can lock multiple times, and it won’t block itself.
Fairness Policy: You can create a fair lock (new ReentrantLock(true)) to ensure first-
come-first-serve locking.
Interruptible Locking: Unlike synchronized, you can interrupt a waiting thread.
Try Locking Without Waiting: Use tryLock() to avoid waiting forever.
Multiple Condition Variables: Unlike synchronized, ReentrantLock allows multiple
conditions (Condition objects).
Now below code the second code does not have to wait for long time.
How to Ensure Fairness in ReentrantLock?
By default, ReentrantLock is not fair, meaning a thread might get the lock even if another thread has
been waiting longer. However, you can create a fair lock by passing true to the constructor:
What is ReadWrite Lock?
A ReadWriteLock in Java is a concurrency mechanism that allows multiple threads to read a resource
simultaneously while ensuring that only one thread can write to the resource at any given time. It
improves performance by allowing multiple readers when no writer is present.
What is Deadlock?
Deadlock is a situation in multithreading where two or more threads are blocked forever, waiting for
each other to release a resource. This typically occurs when two or more threads have circular
dependencies on a set of locks.
For Example, let suppose there are 2 thread A and B A has pen and B has copy and both required pen
and copy to execute and both are waiting for each other to release resources. They are in cyclic
dependency and hence are in deadlock.
Instead of waiting indefinitely, tryLock(timeout, TimeUnit) tries to acquire the lock within a
specified time.
If the lock cannot be acquired within that time, the thread gives up and releases any
previously acquired locks.
This prevents the situation where two threads are stuck waiting for each other.
Thread Communication
In multithreaded environment threads often need to communicate and coordinate with each other
to accomplish a task.
without proper communication mechanism thread may go into busy state and wastage of CPU
resources and Also to the Deadlock.
Java provides three key methods in the Object class for thread communication.
wait(): Causes the current thread to release the lock and wait until another thread invokes
notify() or notifyAll() on the same object.
Thread must continuously check the state of resources and locks, which wastes CPU resources. In
thread communication, threads have the ability to notify each other, so a waiting thread does not
need to continuously check.
In Java, Callable is an interface from the java.util.concurrent package that represents a task that
returns a result and may throw an exception. It is similar to Runnable, but with additional capabilities
A race condition in a multithreaded environment occurs when multiple threads access shared
resources concurrently, and the final outcome depends on the timing of their execution. This can
lead to unexpected behavior or incorrect results because the threads are competing to modify the
resource.
What is Livelock in a Multithreaded Environment?
A livelock occurs when two or more threads keep responding to each other’s actions but fail to make
progress. Unlike a deadlock (where threads are stuck waiting indefinitely), in a livelock, threads keep
changing state but never move forward to complete their tasks.
Think of two people stepping aside to avoid each other in a narrow hallway—both keep moving but
remain stuck in a loop.
A piece of code or a program is thread-safe if multiple threads can access and modify shared data
without causing race conditions, data corruption, or unpredictable behavior.
The Executor Framework in Java is a high-level API that provides a way to manage and control thread
execution efficiently. It introduces a thread pool mechanism, allowing tasks to be executed
asynchronously without manually creating and managing threads.
Creating a new thread for every task consumes a lot of system resources (CPU & memory).
Solution: The Executor Framework reuses threads, reducing overhead.
If we create too many threads, the system might run out of resources (Too many threads =
Too much context switching).
Solution: Executors provide optimized thread pools (fixed, cached, scheduled).
Using newCachedThreadPool()
Using newSingleThreadExecutor()