Abstract: The visibility problem is also caused by the CPU cache, and the visibility problem caused by the cache is one of the “black hand” that causes many weird concurrent programming problems.

This article is shared from Huawei cloud community “[High Concurrency] article decrypting the first dark hand behind the weird concurrency problem — visibility problem”, author: Glacier.

Concurrent programming has always been a headache, because it is not easy to locate problems in multi-threaded environments. Unlike normal business code, it is possible to locate problems with a simple debug. In concurrent programming, problems are often very strange, and for the most part, they don’t recur every time. So how can we better solve the concurrency problem? This requires us to understand what is behind these problems!

1. Visibility

The official definition of visibility is that changes made by one thread to a shared variable are immediately visible to another thread.

To put it bluntly, two threads share a variable, and whenever one thread makes changes to the variable, the other thread can see the changes made by the previous thread. A shared variable is a variable whose value can be accessed and modified by multiple threads.

For example, thread A and thread B directly modify the shared variable in main memory. Whether thread A modiates the shared variable or thread B modiates the shared variable, the value of the variable read by another thread from main memory must be the modified value. This is the visibility of the thread.

2. Visibility issues

Visibility problems can be interpreted as: a shared variable modified by one thread is not immediately visible by another thread. This is caused by the CPU adding a cache.

Now that you understand what visibility is, the visibility problem becomes easier to understand. Since visibility means that when one thread makes a change to a shared variable, another thread can see the change immediately, if not immediately, this can cause visibility problems.

2.1 Single-core cpus do not have visibility problems

One more thing to note about understanding visibility issues is that there are no visibility issues on single-core cpus. Why is that?

Because on a single-core CPU, no matter how many threads are created, only one thread can acquire CPU resources to perform tasks at any one time, even if the single-core CPU has added caching. These threads are all running on the same CPU, operating on the same CPU cache. As long as one thread changes the value of the shared variable, the other thread must be able to access the changed value of the variable.

2.2 Multi-core cpus Have Visibility Problems

Single-core cpus do not have visibility problems because only one thread is executing at a time, and each thread is executing on the same CPU cache. But on multi-core cpus, visibility issues arise.

This is because on multi-core cpus, each CPU’s core has its own cache. When multiple different threads are running on different CPU cores, these threads operate on different CPU caches. Writes by one thread to its bound CPU’s cache are not necessarily visible to another thread, which creates visibility problems for threads.

For example, in the figure above, since the CPU is multi-core, thread A operates on the cache on CPU-01 and thread B operates on the cache on CPU-02. In this case, changes made by thread A to variable V are invisible to thread B and vice versa.

2.3 Visibility issues in Java

When writing concurrent programs in the Java language, when a thread uses variables, it copies data from the main memory to the thread’s private memory, also known as the working memory. When each thread reads or writes data, it manipulates the data in its own working memory.

At this point, the Java model of threads reading and writing shared variables is similar to that of a multicore CPU, because when Java concurrent programs are running on a multicore CPU, the thread’s private memory, or working memory, is equivalent to the cache of each CPU core in a multicore CPU.

From the above figure, we can also see that thread A’s changes to A shared variable may not be immediately visible to thread B, which can cause visibility problems.

3. Code examples

We use a Java program to verify multithreaded visibility problems by defining a member variable count of type long and a method called addCount that increments the value of count by one. Meanwhile, in the execute method, start two threads respectively, call the addCount method 1000 times for each thread, and return the value of count after the two threads finish executing, as shown in the code below.

package io.mykit.concurrent.lab01; Public class ThreadTest {private long count = 0; private void addCount(){ count ++; } public long execute() throws InterruptedException { Thread threadA = new Thread(() -> { for(int i = 0; i < 1000; i++){ addCount(); }}); Thread threadB = new Thread(() -> { for(int i = 0; i < 1000; i++){ addCount(); }}); Threada.start (); threadB.start(); Threada.join (); threadB.join(); return count; } public static void main(String[] args) throws InterruptedException { ThreadTest threadTest = new ThreadTest(); long count = threadTest.execute(); System.out.println(count); }}Copy the code

Let’s run the program and the result is shown below.

You can see that the result of this program is 1509, not 2000 as we expected. Why is that? Let’s examine the program.

First, the variable count belongs to A member variable of the ThreadTest class, which is A shared variable for threads A and B. Let’s say that thread A and thread B are executing at the same time, and they both read count=0 into their working memory, and each thread writes the value of count to memory after the first count++ operation. At this point, the value of count in memory is 1, not 2. During the entire calculation process, thread A and thread B are calculated based on the count value in their working memory. This results in the final count value being less than 2000.

Bottom line: Visibility problems are caused by CPU caching.

4, summarize

Visibility is a change made by one thread to a shared variable that another thread can see immediately. If it is not seen immediately, visibility problems can occur. There are no visibility issues on single-core cpus, visibility issues are mainly with concurrent programs running on multi-core cpus. At the end of the day, visibility problems are caused by the CPU’s cache, and cache-induced visibility problems are one of the reasons behind many weird concurrent programming problems. Only when we deeply understand the visibility problems caused by caching, and pay attention to avoid visibility problems in practical work, can we write high concurrency programs better.

Click to follow, the first time to learn about Huawei cloud fresh technology ~