Deadlock involves a mutual interdependence between two or more threads.
One simple scenario is when one thread executes some code synchronized on object1 and needs to execute code synchronized on object2 still holding the object1 lock. At the same time, a second thread executes some code synchronized to obejct2 and needs to execute code synchronized to object1. In simple terms, thread1 hold object1 lock and wait for object2 lock and thread 2 hold object 2 lock and wait for object 1 lock. Both the threads will keep on waiting for the resource held by the other without releasing the lock they hold.
Deadlock situation is usually explained using the dining philosophers problem. Consider that 5 philosophers are sitting around a round table and they need 2 chop sticks to eat. However there are only 5 chop sticks. They can all share the chop sticks such that after one person eat, he will place it back on the table and other can take. But consider the situation in which everyone get one chop stick and then wait for other. And if everyone wait without putting the chopstick back, then noon will ever get one and they will keep on waiting.
Deadlocks are dangerous and needs to be avoided using proper synchronization.
Deadlocks doesn't always occur and occur only with some bad timings, which is called a race condition. And because of this, it may go undetected in initial testing.
Deadlocks in Java are non-recoverable unlike database systems. In database systems, whenever a deadlock is detected, the system will try to abort one of the transaction so that the other can proceed with the resources. The aborted transaction can then retry again later.
According to the book Java Concurrency in Practice, there are different types of deadlocks such as
Lock ordering deadlocks
Lock ordering deadlock is caused when using different order of acquiring locks when locking on multiple objects.
Open call deadlocks
Happens when you make calls to methods which can be inherited and the inherited method does not take care of proper synchronization.
When waiting for multiple resources in a cycle following the necessary conditions for a deadlock.
Deadlock is the most important liveness hazards in Java. Other liveness hazards include starvation, poor responsiveness and livelock.
Often, starvation is confused with deadlock. They are not same.
For deadlock to happen there should be some necessary conditions to happen.
According to computer science, a deadlock can occur if all of the following conditions hold simultaneously in a system:
Mutual Exclusion: At least one resource must be held in a non-shareable mode. Only one process can use the resource at any given instant of time.
Hold and Wait or Resource Holding: A process is currently holding at least one resource and requesting additional resources which are being held by other processes.
No Preemption: The operating system must not de-allocate resources once they have been allocated; they must be released by the holding process voluntarily.
Circular Wait: A process must be waiting for a resource which is being held by another process, which in turn is waiting for the first process to release the resource.
These four conditions are known as the Coffman conditions.
Java Concurrency in Practice by Brian Goetz, David Holmes, DOug Lea, Tim Peierls, Joshua Bloch and Joseph Bowbeer.