Race Conditions gibt es sobald der Zugriff auf gemeinsame Ressourcen (Variablen oder ähnliches) nicht genügend synchronisiert wurde. Anders ausgedrückt gibt es eine Race Condition, wenn die Ausführung nicht immer zu einem gültigen Zustand kommt. Man könnte auch sagen, dass aufgrund der Race-Condition je nach Thread-Scheduling andere Ergebnisse bei gleichem Code generiert werden.

Deadlocks sind Situationen, in denen sich Threads gegenseitig blockieren. Das kann beispielsweise passieren, wenn Locks in unterschiedlichen Reihenfolgen akquiriert wurden und dabei je ein Thread das Lock vom anderen Thread benötigt, bevor er fortfahren kann.

Starvation ist eine Fortschrittsbehinderung der Threads, beispielsweise wegen der Fairness-Problematik. Ein Thread kann “verhungern”, wenn er nie aufgeweckt wird oder weil jemand immer das Lock schneller akquiriert hat.

Data Races gibt es bei unsynchronisiertem Zugriff auf den gleichen Speicher. Es braucht immer mindestens ein write für einen Data Race. Einer Race-Condition liegt oft ein Data-Race zu Grunde. Durch das synchronized Schlüsselwort gibt es kein Data-Race auf der Variable balance. Dennoch kann es ungültige Zustände geben. So genannte Lost Updates sind unten möglich, weil das Lesen und das Schreiben nicht atomar sind.

class BankAccount {
   int balance = 0;
   synchronized int getBalance() { return balance; }
   synchronized void setBalance(int x) { balance = x; }
}

account.setBalance(account.getBalance() + 100);