66 What Are the Drawbacks of Cas

66 What Are the Drawbacks of CAS #

This lesson primarily discusses the drawbacks of CAS.

Previously, we discussed that CAS has many advantages, such as avoiding the use of mutex locks and improving program efficiency. However, CAS also has some significant drawbacks. Therefore, when using CAS, we should consider both its advantages and disadvantages and make a reasonable technical selection.

Now let’s take a look at the main drawbacks of CAS.

The ABA Problem #

Firstly, the biggest drawback of CAS is the ABA problem.

The criterion for deciding whether CAS performs a swap is whether the current value and the expected value are consistent. In most cases, this criterion is not a problem.

However, in some business scenarios, we want to know precisely whether the value has changed since we last saw it. For example, if the value changes from A to B and then back to A, we not only consider that it has changed but also consider that it has changed twice.

In this scenario, when we use CAS, we cannot see these two changes because only checking if “the current value and the expected value are consistent” is not enough. CAS does not check whether the value has changed but rather compares the current value with the expected value to see if they are the same. So if the value of a variable changes from the old value A to the new value B and then back to the old value A, since the initial value A and the current value A are equal, CAS will consider that the variable’s value has not changed during this period. Therefore, CAS cannot detect whether the value has been modified during this period; it can only determine if the current value and the initial value are the same.

Let’s consider an example: Suppose the first thread obtains an initial value of 100, performs calculations, and during the calculation, a second thread changes the initial value to 200, followed by a third thread changing it back to 100. When the first thread finishes the calculation and executes CAS, it compares the current value to the initial value of 100 and finds that they are indeed equal. Therefore, thread one assumes that no thread has changed the value during this period and naturally changes this 100 to the newly calculated value. However, in reality, another thread has already modified this value during this process, which results in an ABA problem.

If an ABA problem occurs, thread one will have no way to know whether another thread has modified the value during the calculation process. Since the first thread finds that the current value and the expected value are equal, it will assume that no thread has modified the variable’s value during this period. Therefore, it will continue with its logic based on the assumption that the value has not been modified during this period, such as printing a log saying, “This modification went smoothly.” However, it should have triggered other logic when it discovers that another thread has modified the value during this period, and the log should have been “This modification process is interfered with.”

So, how can we solve this problem? By adding a version number.

By adding a version number outside the variable value itself, the change path of this value changes from A→B→A to 1A→2B→3A. In this way, we can determine if the value has changed by comparing the version numbers, which is more reliable than directly comparing the two values. So, this kind of thinking can solve the ABA problem. The AtomicStampedReference class is provided in the atomic package to specifically address the ABA problem. The solution uses a version number, and AtomicStampedReference maintains a data structure similar to a tuple, where the int represents a counter or version number. It allows for atomic updates to both the object and the int version number, thus solving the ABA problem. Rather than judging whether it has been modified based on the value changing, we now judge whether it has been modified based on the version number changing. Even if the values are the same, their version numbers will be different.

This concludes the introduction to the first drawback of CAS - the ABA problem.

Long Spin Time #

The second drawback of CAS is long spin time.

Since a single CAS does not always succeed, CAS is often implemented with a loop, sometimes even an infinite loop, continuously retrying until the thread competition is not intense enough to succeed in modification.

However, if our application scenario itself is highly concurrent, it is possible for CAS to never succeed, resulting in ever-increasing loop times. Moreover, during this period, CPU resources are constantly consumed, which will have a big impact on performance. Therefore, this requires us to choose whether to use CAS based on the actual situation. In highly concurrent scenarios, the efficiency of CAS is usually not high.

Limited Flexibility of Scope Control #

The third drawback of CAS is the limited flexibility of thread-safe scope control.

Usually when we perform CAS, it is targeted at a single variable, not multiple shared variables. This variable may be of type Integer, Long, Object, etc., but we cannot perform CAS operations on multiple shared variables at the same time, as these variables are independent. Simply combining atomic operations does not guarantee atomicity. Therefore, it is difficult to simultaneously perform CAS operations on multiple objects and ensure thread safety.

There is a solution, which is to use a new class to integrate the group of shared variables mentioned earlier. The multiple member variables in this new class are the multiple shared variables mentioned earlier, and then use the AtomicReference in the atomic package to perform CAS operations on the entire new object, thus ensuring thread safety.

In comparison, if we use other thread-safe techniques, adjusting the scope of thread safety can be very easy. For example, when using the synchronized keyword, if we want to lock more code, we can simply put more code inside the synchronized block.

Summary #

In this lesson, we discussed the three drawbacks of CAS, namely the ABA problem, long spin time, and limited flexibility of thread-safe scope control. After understanding its drawbacks, we can make targeted choices when selecting technologies.