In Java, 'thread' means two different things:
An instance of class java.lang.Thread.
A thread of an execution.
An instance of Thread is just an object. Like any other object in Java, it has variables and methods and lives and dies on the heap. But a thread of execution is an individual process (a "lightweight" process) that has its own call stack.
In Java, there is one thread per call stack or to think of it in reverse, one call stack per thread. Even if you don't create any new threads in your program, threads back there are running.
Threads share the address space of the process that created it whereas processes have their own address.
Threads have direct access to the data segment of its process while processes have their own copy of the data segment of the parent process.
Threads can directly communicate with other threads of its process while on the other hand processes must use interprocess communication to communicate with sibling processes.
Threads have almost no overhead while processes have considerable overhead.
New threads are easily created whereas new processes require duplication of the parent process.
Threads can exercise considerable control over threads of the same process whereas processes can only exercise control over child processes.
Changes to the main thread (cancellation, priority change, etc.) may affect the behavior of the other threads of the process while changes to the parent process do not affect child processes.
Threads support concurrent operations. For example:
Multiple requests by a client on a server can be handled as an individual client thread. Threads often result in simpler programs.
In sequential programming, updating multiple displays normally requires a big while-loop that performs small parts of each display update. Threads provide a high degree of control.
Imagine launching a complex computation that occasionally takes longer than is satisfactory. Threaded applications exploit parallelism.
A computer with multiple CPUs can literally execute multiple threads on different functional units without having to simulate multi-tasking ("time sharing").
There are two ways to create a new thread. Extend the 'Thread' class and override the 'run()' method in your class. Create an instance of the subclass and invoke the 'start()' method on it, which will create a new thread of execution.
public class NewThread_JBK extends Thread
{
public void run()
{
// the code that has to be executed
// in a separate new thread goes here
}
public static void main(String [] args)
{
NewThread_JBK c = new NewThread_JBK();
c.start();
}
}
This will implement the 'Runnable' interface. The class will have to implement the 'run()' method in the 'Runnable' interface. Create an instance of this class. Pass the reference of this instance to the 'Thread' constructor and a new thread of execution will be created.
public class NewThread_JBK implements Runnable
{
public void run()
{
// the code that has to be executed
// in a separate new thread goes here
}
public static void main(String [] args)
{
NewThread_JBK c = new NewThread_JBK();
Thread t = new Thread(c);
t.start();
}
}
The different states of threads are as follows:
New : When a thread is instantiated it is in 'New' state until the 'start()' method is called on the 'thread' instance. In this state the thread is not considered to be alive.
Runnable : The thread enters into this state after the 'start()' method is called in the thread instance. The thread may enter into the 'Runnable' state from 'Running' state. In this state the thread is considered to be alive.
Running : When the thread scheduler picks up the thread from the 'Runnable' thread's pool, the thread starts running and the thread is said to be in 'Running' state.
Waiting/Blocked/Sleeping : In these states the thread is said to be alive but not runnable. The thread switches to this state because of various reasons like when 'wait()' method is called or 'sleep()' method has been called on the running thread or thread might be waiting for some input/output resource so blocked.
Dead : When the thread finishes its execution i.e. the 'run()' method execution completes, it is said to be in 'dead' state. A 'dead' state cannot be started again. If a 'start()' method is invoked on a dead thread a runtime exception will occur.
'Synchronized' keyword can be applied to 'static/non-static' methods or a block of code. Only one thread at a time can access synchronized methods and if there are multiple threads trying to access the same method then other threads have to wait for the execution of method by one thread. 'Synchronized' keyword provides a lock on the object and thus prevents race condition. E.g.
public void synchronized method()
{
}
public void synchronized staticmethod(){}
public void myMethod(){
synchronized (this){
//synchronized keyword on block of code
}
}
When a 'synchronized non-static' method is called a 'lock' is obtained on the object. When a 'synchronized static' method is called a 'lock' is obtained on the class and not on the object.
The lock on the object and the lock on the class don't interfere with each other. It means, if a thread accesses a synchronized non-static method, then the other thread can access the synchronized static method at the same time but can't access the synchronized non-static method.
In general each thread has its own copy of variable, such that one thread is not concerned with the value of same variable in the other thread. But sometimes this may not be the case.
Consider a scenario in which the count variable is holding the number of times a method is called for a given class irrespective of any thread calling. In this case irrespective of thread access the count has to be increased so the count variable is declared as 'volatile'.
The copy of 'volatile' variable is stored in the main memory, so every time a thread accesses the variable even for reading purpose the local copy is updated each time from the main memory. The 'volatile' variable also have performance issues.
'yield()' allows the current thread to release its lock from the object and scheduler gives the lock of the object to the other thread with same priority.
'sleep()' allows the thread to go to sleep state for x milliseconds. When a thread goes into sleep state it doesn't release the lock.
'wait()' is a method of 'Object' class whereas 'sleep()' is a method of Object class.
sleep() allows the thread to go to sleep state for x milliseconds. When a thread goes into sleep state it doesn't release the lock. 'wait()' allows thread to release the lock and goes to suspended state. The thread is only active when a 'notify()' or 'notifAll()' method is called for the same object.
'notify()' wakes up the first thread that called 'wait()' on the same object.
'notifyAll()' wakes up all the threads that called 'wait()' on the same object. The highest priority thread will run first.
If a thread has been instantiated but not started it is said to be in 'new' state. Unless and until a 'start()' method is invoked on the instance of the thread, it will not be said to be alive.
If you do not call a 'start()' method on the newly created thread, instance thread is not considered to be alive.
If the 'start()' method is not invoked and the 'run()' method is directly called on the 'Thread' instance, the code inside the 'run()' method will not run in a separate new thread but it will start running in the existing thread.
A new thread of execution with a new call stack starts when 'start()' method is called. The state of thread changes from 'new' to 'runnable'. When the thread gets chance to execute its target, 'run()' method starts to run.
When a code is running in a thread and it creates a new thread object, the priority of the new thread is set equal to the priority of the thread which has created it.
When jvm starts up the thread executing main method is started.
'Daemon' threads are service provider threads which run in the background. These are not used to run the application code generally.
When all user threads(non-daemon threads) complete their execution the jvm exit the application whatever may be the state of the daemon threads.
Jvm does not wait for the daemon threads to complete their execution if all user threads have completed their execution. To create Daemon thread set the daemon value of Thread using 'setDaemon(boolean value)' method.
By default all the threads created by user are user threads. To check whether a thread is a Daemon thread or a user thread use 'isDaemon()' method.
Example of the Daemon thread is the 'Garbage' Collector run by jvm to reclaim the unused memory by the application. The 'Garbage' collector code runs in a Daemon thread which terminates as all the user threads are done with their execution.
No. Only methods can be synchronized and not the variables or classes.
Each object has only one lock.
Yes, a class can have both synchronized and non-synchronized methods.
Yes. If a class has a synchronized and non-synchronized methods, multiple threads can access the non-synchronized methods.
Yes when a thread goes to sleep, invoked by 'Thread.sleep(xx)' it holds a lock.
Yes. A thread can hold multiple locks at the same time. Once a thread acquires a lock and enters into the synchronized method / block, it may call another synchronized method and acquire a lock on another object.
Yes. Once a thread acquires a lock in some object, it may call any other synchronized method of that same object using the lock that it already holds. The locks are Reentrant.
Yes. Static methods are class methods and have only one copy of static data for the class. Only one lock for the entire class is required. Every class in java is represented by java.lang.Class instance. The lock on this instance is used to synchronize the static methods.
No. The static synchronized methods of the same class always block each other as only one lock per class exists. So no two static synchronized methods can execute at the same time.
No, the thread executing the static synchronized method holds a lock on the class and the thread executing the non-static synchronized method holds the lock on the object on which the method has been called. These two locks are different and these threads do not block each other.
No. A thread can be started only once in its lifetime. If you try starting a thread which has been already started once an 'IllegalThreadStateException' is thrown, which is a runtime exception. A thread in runnable state or a dead thread cannot be restarted.
When a locked object tries to access a locked object which is trying to access the first locked object, the threads will wait for each other to release the lock on a particular object due to which deadlock occurs.
If a class is made to extend the thread class to have a multithreaded application then this subclass of Thread cannot extend any other class and the required application will have to be added to this class as it cannot be inherited from any other class. If a class is made to implement Runnable interface, then the class can extend other class or implement other interface.