Concurrency Issues
Unfortunately, with the numerous advantages programmers gain in the use of concurrency, it also causes potential problems that must be addressed. Here we will take a look at the most common ones. To recap and continue from our last article, concurrency allows multiple Threads to evaluate more than one set of statements at once.
These threads all share the same processor. This means that the processor is essentially “switching” back and forth between the different threads. The time that the processor gives to each thread and when it will switch is essentially random though. This can cause issues where threads are relying on the same variables or methods. You can imagine an instance where Thread A changes a variable when Thread B is in the middle of evaluating statements with that variable.
This forces a programmer to make Thread A wait until Thread B is done with the variable. This brings about the problem of starvation. Starvation is essentially when other threads are permanently denied resources by another thread. If Thread X cannot continue until Thread Y is finished with a certain resource (a variable or method), but Thread Y never releases that resource, X is said to be starved. In this scenario, Thread X can never complete its task because it will always be denied the necessary resources.
This is similar in effect to deadlock. Deadlock happens when two or more threads are all waiting on the other one for a resource. To use an analogy, think of two people making a trade. One person says “I’ll give you this trophy as soon as you give me five dollars”, whilst the other person says “I’ll give you five dollars as soon as you give me the trophy.” These two people are deadlocked, because they will both always be waiting for the other party.
A famous example of deadlock is the Dining Philosopher’s problem. The classic example involves five philosophers sitting at a table, but two is simply and works equally well. Let’s say two philosophers are sitting across from each other at a table, but there is only one set of chopsticks. When these two “philosopher threads” run, the code dictates that they each try to pick up the chopsticks. So, both philosophers grab a chopstick. This can cause a scenario where they each grab one chopstick! A philosopher cannot eat with one chopstick, and they are always waiting for the other one to be done with the other chopstick. Once the program gets to this point, it will go no further, since each thread is dependent on the other for the next step.
A way someone might think to fix this would be to add a timeout. If you have had a chopstick for 2 minutes, but don’t get the other, you put back down the chopstick. What would happen in this case though, is that they would first each grab the chopstick on their right, then put them down, then both grab the one on their left, then put them down, then the right, and so on. This is known as a livelock. The program is not “frozen”, but still no progress can ever be made.
The simplest solution to the problem is to assign an order to the chopsticks (or Objects, in the programming parallel). If we say that there is a chopstick 1, and a chopstick 2, we can dictate that the philosophers MUST pick up 1 before they can try to get 2. In this way, if a philosopher tries to grab 1 and it is already taken, he will not grab the other and cause a deadlock.