How do thread safety issues Arise?

2022-06-05 0 By

Thread safety refers to a method or a piece of code that can be correctly executed in multiple threads without data inconsistency or data pollution. We call such a program thread-safe, whereas it is non-thread-safe.For example, if there is only one bank employee to handle business, this situation is called single thread execution in the program, and single thread execution is not a problem, that is, thread safety.But then one day a lot of people come along and they’re doing business at the same time, and that’s called multithreading.If everyone is scrambling to do business at once, it’s likely to lead to errors, which are called non-thread-safe.We call this thread-safe if everyone lines up in an orderly fashion and there are no mistakes made.Problem demonstration Let’s show you an example of a program that is not thread safe.Let’s create a variable number equal to 0, and then let thread 1 perform a million number++ operations, and let thread 2 perform a million number– operations, and wait for thread 1 and thread 2 to complete,The correct result should still be 0, but the result of an untampered multi-threaded execution is inconsistent with the expected correct result, as shown in the following code:Public class ThreadSafeTest {private static int number = 0;// Loop COUNT (100W) private static final int COUNT = 1_000_000;Public static void main(String[] args) throws InterruptedException {// Thread 1:Thread t1 = new Thread(() -> {for (int I = 0;i < COUNT; i++) { number++; }});t1.start(); Threadt2 = new Thread(() -> {for (int I = 0;i < COUNT; i++) { number--; }});t2.start(); Join (); // Wait for thread 1 and thread 2 to execute, print the final result of number t1.join();t2.join(); System.out.println("number final result: "+ number);}} The execution result of the above program is as follows: from the above execution result, we can see that the final result of the number variable is not 0, which is inconsistent with the correct result we expect, which is the thread safety problem in multithreading.The causes of thread safety problems are as follows: Multithreaded preemptive execution.Multiple threads modify the same variable simultaneously.Nonatomic operation.Memory visibility.Instruction reorder.Let's take a look at the specific meanings of each of these five factors.The number one cause of thread safety problems is multithreaded preemptive execution. Imagine that if it is a single thread execution, or multithreaded execution is orderly, then there will be no chaos. If there is no chaos, there will be no non-thread safety problems.Thread 1 modifysnumber1, thread 2 modifysnumber2, thread 2 modifysnumber2.Public class ThreadSafe {private static int number = 0;// Loop COUNT (100W) private static final int COUNT = 1_000_000;Private static int number1 = 0; private static int number1 = 0;Private static int number2 = 0; private static int number2 = 0;Public static void main(String[] args) throws InterruptedException {// Thread 1:Thread t1 = new Thread(() -> {for (int I = 0;i < COUNT; i++) { number1++; }});t1.start(); Threadt2 = new Thread(() -> {for (int I = 0;i < COUNT; i++) { number2--; }});t2.start(); Join (); // Wait for thread 1 and thread 2 to execute, print the final result of number t1.join();t2.join(); number = number1 + number2; System.out.println("number=number1+number2 ");}} The execution result of the above program is shown as follows: from the above results, it can be seen that as long as multithreading does not modify the same variable at the same time, there will be no thread safety problems.3. Nonatomic Operations Atomic operations are operations that can no longer be separated.For example, the human breathing in or exhaling is done in a single moment. You can't take half of the breath, stop to play with the phone, and then take half of the breath. This operation is atomic operation.The non-atomic operation is I'm going to sleep now, but before I go to sleep, I have to go to bed, pull the quilt, lie down, fall asleep and so on. That's the non-atomic operation.Non-atomic operations can be separated and interrupted, for example, before going to bed and finding the time is still there, watching a TV show, checking your phone, playing a game, even eating a barbecue, etc., so there is a lot of uncertainty in non-atomic operations, and this uncertainty can lead to thread safety issues.Operations like I ++ and I -- these are non-atomic, requiring the value of the original variable to be queried before +1 or -1. This is not done in one go, thus causing thread-safety problems.For example, the following operations are performed: Procedure Thread 1 Thread 2T1 reads number=1 and prepares to perform the number-1 operation. However, the time slice is used up before the operation is performed.T2 reads number=1 and performs number+1 to change number to 2.Select * from T3 where number=1; select * from T3 where number=1Number = 1, thread 1 does -1, thread 2 increments 1, number = 1, thread 2 increments 1, number = 1, thread 1 increments 1, number = 1, thread 2 increments 1, number = 1, thread 1 increments 1, number = 1, thread 2 increments 1, number = 1, thread 1 increments 1, number = 0, thread 2 increments 1, number = 1, thread 1 increments 1, number = 1, thread 2 increments 1Memory is divided into two types in Java programming: working memory and main memory. Working memory is realized by CPU registers, while main memory refers to the memory in a computer. As we know, the operation speed of CPU registers is much faster than that of memory, and their performance differences are shown in the following figure:So what does this have to do with thread safety?This is because in the Java language, in order to improve the speed of execution of the program, so the operating variables, the variables will be a copy from main memory into the working memory, and the main memory is Shared by all threads, working memory each thread is private, it will lead to a thread has made public variables change in the main memory, and another thread I don't know.Still using variables in your own working memory leads to problems, which leads to thread-safety issues.5. Instruction reordering Instruction reordering refers to the combination and optimization of the following operations in order to improve the execution speed of the Java program.For example, If Zhang SAN wants to return books to the library, and his roommate asks Him to help him borrow books, the execution thinking of the program is that Zhang SAN first returns his own books to the library, and then goes to the library to help his roommate borrow books back.After the instruction reordering, the two executions were combined. Zhang SAN took his book to the library to return the book first, and then helped his roommate to borrow the book. The whole process was completed, which is the advantage of the instruction reordering under normal circumstances.But instruction reordering also has "side effect", and "side effect" is happening in the multithreaded execution, or zhang SAN borrow books and help roommates for books, for example, if zhang SAN is one thing to do is another thing no problem (that is, there is no question of single thread), but if it is a multithreaded execution, is more than two pieces for personal mixed with do,Such as zhang met over their students in the library, and then put the task assigned to more than one person to perform together, some borrowed some books, some borrowed also has a few books, then borrow a few books, someone borrowed is again a few books, perform very confusion without a clear goal, at the end of the tragedy happened, this is the instruction reordering thread safety problem.Summary Thread safety refers to a method or a piece of code can be correctly executed in multithreading without data inconsistency or data contamination, otherwise it is a thread safety problem.In simple terms, non-thread safety refers to the problem that in multithreading, the execution result of a program is inconsistent with the correct result expected.There are five factors that cause thread-safety problems: preemptive execution by multiple threads, simultaneous modification of the same variable by multiple threads, non-atomic operations, memory visibility and instruction reordering.