A lock occurs when multiple processes try to access the same resource at the same time.
One process loses out and must wait for the other to finish.
A deadlock occurs when the waiting process is still holding on to another resource that the first needs before it can finish.
So, an example:
Resource A and resource B are used by process X and process Y
- X starts to use A.
- X and Y try to start using B
- Y 'wins' and gets B first
- now Y needs to use A
- A is locked by X, which is waiting for Y
The best way to avoid deadlocks is to avoid having processes cross over in this way. Reduce the need to lock anything as much as you can.
In databases avoid making lots of changes to different tables in a single transaction, avoid triggers and switch to optimistic/dirty/nolock reads as much as possible.
Starvation is a resource management problem where a process does not get the resources it needs for a long time because the resources are being allocated to other processes.
[UPDATE]: Starvation generally occurs in a Priority based scheduling System. Where High Priority (Lower Number = Higher Priority) requests get processed first. Thus a request with least priority may never be processed.
differ from each other
A deadlock occurs when two (or more) threads have created a situation where they are all blocking each other. Imagine that threads T1 and T2 need to acquire both resources A and B in order to do their work. If T1 acquires resource A, then T2 acquires resource B, T1 could then be waiting for resource B while T2 was waiting for resource A. In this case, both threads will wait indefinitely for the resource held by the other thread. These threads are said to be deadlocked.
Starvation occurs when a scheduler process (i.e. the operating system) refuses to give a particular thread any quantity of a particular resource (generally CPU). If there are too many high-priority threads, a lower priority thread may be starved. This can have negative impacts, though, particularly when the lower-priority thread has a lock on some resource.
Race conditions occur when two threads interact in a negatve (buggy) way depending on the exact order that their different instructions are executed. If one thread sets a global variable, for example, then a second thread reads and modifies that global variable, and the first thread reads the variable, the first thread may experience a bug because the variable has changed unexpectedly.