Thread-safe and thread-unsafe analysis and examples in Java

Introduction

As the first article of multithreaded programming, this article will start with a simple example, and take you to truly understand what thread unsafety is and why it occurs at the code level. The article will provide a complete example of thread unsafety. I hope you can follow the article, run this program by yourself, and experience the thread safety issues that must be considered in multi-threaded programming.

1. What is thread safety

The definition of Brian Goetz, author of "Java Concurrency In Practice": "When multiple threads access an object, if the scheduling and alternating execution of these threads in the runtime environment are not considered, there is no need for additional synchronization or on the caller. Any other coordination operation, calling the behavior of this object can get the correct result, then this object is thread-safe.

Why not make all operations thread-safe?

There is a cost to implementing thread safety. For example, thread-safe programs will run relatively slowly, and the complexity of development will also increase, which increases labor costs.

2. Start with the classic thread-unsafe example

Classic case : Two threads read and write a global variable together count, and each thread executes 10,000 times count++. countWill the final result be 20,000? Guess the running result in your mind?

Code implementation of the classic case:

package com.study.synchronize.object;

/**
 * 线程不安全案例:两个线程同时累加同一个变量,结果值会小于实际值
 */
public class ConcurrentProblem implements Runnable {

	private static ConcurrentProblem concurrentProblem = new ConcurrentProblem();
	private static int count;


	public static void main(String[] args) {
		Thread thread1 = new Thread(concurrentProblem);
		Thread thread2 = new Thread(concurrentProblem);
		thread1.start();
		thread2.start();
		try {
			// 等待两个线程都运行结束后,再打印结果
			thread1.join();
			thread2.join();
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		//期待结果是20000,但是结果会小于这个值
		System.out.println(count);
	}

	/**
	 * 多线程问题原因:count++这行代码要分三步执行;1:读取;2:修改;3:写入。
	 * 在这三步中,任何一步都可能被其他线程打断,导致值还没来得及写入,就被其他线程读取或写入,这就是多线程并行操作统一变量导致的问题。
	 */
	@Override
	public void run() {
		for (int k = 0; k < 10000; k++) {
			count++;
		}
	}

}

Results of multiple runs: count the final value will be less than or equal to 20000.

3. Analysis of the problem: Why is the multi-threaded accumulation less than the expected value?

1. Understand how the JVM executescount++

When the program performs count++this operation, the JVM will complete it in three steps (non-atomic):

  1. A thread reads from memory count.
  2. A thread modifies the countvalue.
  3. A thread will countrewrite memory.

This operation process should be well understood, you can simply compare it to 把大象装进冰箱里的三个步骤. This situation does not occur when the above code is executed in a single thread 小于2万. Why does multi-threading occur inconsistent with expectations? In order to fully understand this, you need to first understand what is a thread?

Like a thread 病毒, it cannot survive in the world independently, and needs to be parasitized in 宿主细胞it. Threads also cannot live independently in the system and 线程need to be attached to 进程existence. What is a process?

进程是代码在数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位

线程是进程的一个执行路径,一个进程至少有一个线程,多个线程则会共享进程中的资源。

2. Understand the source of the problem:

After we have an understanding of threads, let's think about count++the three steps. Since threads will share resources in the process, in these three steps, any step may be interrupted by other threads, resulting in the countvalue not having time to write. It is read or written by other threads.

3. The process of brain supplement restoration error:

insert image description here

  • If countthe value is 1, after thread 1 reads the countvalue, it will be countmodified to 2. At this time, the result has not been written to the memory, and the count value in the memory is still 1.
  • Another thread 2, countafter reading the value of 1, also modified it to 2, and successfully wrote it into the memory. At this time, the countvalue in the memory became 2.
  • Then thread 1 also writes countthe result 2 into memory, and countthe result in memory is still 2 (should be 3).

In the above scenario, the two threads each execute once count++, but the count value is only increased by 1, which is the problem.

Summarize

Multithreading can perform some tasks in parallel to improve processing efficiency, but it also brings new problems, that is, thread safety issues. When operating the same resource between multiple threads, unexpected situations also occur. , the reason is that these operations may not be atomic operations. In short, we look at the program to perform one operation, but it may need to be executed in multiple steps in the JVM, and multiple threads may disrupt the execution order of the JVM , followed by unpredictable problems.

So in Java, how to deal with this kind of problem? With the upgrade of the version, Java provides many solutions, such as: classes in the Concurrent package. But our next article will explain one of the simplest and most convenient solutions. The above case code can easily avoid the problem of thread safety by adding only one word, which is the synchronizedkeyword.

If you like this article, please bookmark and like it, and please continue to read other articles in this column. This column will combine various scene codes to thoroughly explain the concurrency problems and synchronizedvarious usage scenarios in java.

Guess you like

Origin http://10.200.1.11:23101/article/api/json?id=324098292&siteId=291194637