In the entire process from the beginning of a thread to the end of the access, if an access object is modified by another thread, then a thread safety problem will occur for the current thread; if During the entire access process, no object is modified by other threads, which means it is thread-safe.
The first is a multi-threaded environment, that is, there are multiple Operator, single-threaded environment does not have thread safety issues. In a single-threaded environment, any operation, including modification operations, is issued by the operator himself. The operator not only has a clear purpose when issuing the operation, but is also aware of the impact of the operation.
Multiple operators (threads) must operate the same object. Only when multiple operators operate an object at the same time can the impact of the behavior be immediately passed to other operations. By.
The operations of multiple operators (threads) on the same object must include modification operations. There is no thread safety issue in joint reading because the object is not modified. Unchanged, cannot have an impact.
#It can be seen from the above that the root cause of thread safety problems is that shared data may be modified concurrently, that is, when one thread reads, another thread is allowed to Revise.
According to the conditions under which thread safety problems arise, the idea for solving thread safety problems is to eliminate the occurrence of thread safety problems. Problem environment:
Eliminate shared data: member variables and static variables are shared by multiple threads, convert these global variables into local variables, and store the local variables on the stack. If there is no sharing between threads, there will be no environment for thread safety issues to arise. Eliminate the shortcomings of shared data: If you need an object to collect information from each thread, or transfer information between threads, this purpose cannot be achieved if you eliminate the shared object.
Use the thread synchronization mechanism: lock the read and write operations at the same time so that only one thread can access the shared data at the same time. If the write operation is only locked, and only one thread can perform the write operation at the same time, and the read operation is not restricted, allowing multiple threads to read concurrently, then non-repeatable reads may occur, such as a read with a relatively long duration. Threads read the data at the same index position of the array over a long period of time. During the two readings, a thread modified the data at the index, causing the data read by the thread from the same index to be inconsistent. Whether to lock both reading and writing, or only writing, depends on the specific needs. The disadvantage of the synchronization mechanism is that it reduces the throughput of the program.
Create a copy: Use ThreadLocal to create a copy of the variable for each thread. Each thread operates independently without affecting each other. This method is essentially an implementation of eliminating the idea of shared data.
#4. Visibility A copy of the variable. When one thread updates this shared variable, another thread may or may not see it. This is visible. Sexual issues, take the following code as an example:
public class NoVisibility {
private static boolean ready;
private static int number;
public static class ReadThread extends Thread {
public void run() {
while(!ready )
Thread. yield();
System. out.println(number);
}
}
public static void main(String [] args) {
new ReadThread().start();
number = 42;
ready = true ;
}
}
The above code may output 0 or nothing. Why can't anything be output? Because we set ready to true in the main thread, but the ready value we set may not be read in ReadThread, so Thread.yield() will continue to be executed in ReadThread. Why might it be 0? If ReadThread can read our value, it may first read that the ready value is true, and before reading and updating the number value, ReadThread will output the retained number value, which is 0.
Note that all the above are assumptions. We cannot predict how ReadThread and the main thread will interact in the absence of synchronization. The above two situations are just two possibilities. So how to avoid this problem? Quite simply, whenever there is data to be shared between multiple threads, use proper synchronization.
4.1, Locking and Visibility
内置锁可以用于确保某个线程以一种可预测的方式查看另一个线程的执行结果,当线程A进入某同步代码块时,线程B随后进入由同一个锁保护的同步代码块,此时,线程B执行由锁保护的同步代码块时,可以看到线程A之前在同一同步代码块中的所有操作结果,如果没有同步,那么就无法实现上述保证。
加锁的含义不仅仅局限于互斥行为,还包括内存可见性。为了确保所有线程都能看到共享变量的最新值,所有执行读操作或者写操作的线程都必须在同一个锁上同步。
4.2、volatile变量
volatile是一种比synchronized关键字轻量级的同步机制,volatile关键字可以确保变量的更新操作通知到其他线程。
下面是volatile的典型用法:
volatile boolean asleep; ... while(!asleep) doSomeThing();
加锁机制既可以确保可见性,又可以确保原子性,而volatile变量只能确保可见性。
5、总结
编写线程安全的代码,其核心在于要对状态访问操作进行管理。编写线程安全的代码时,有两个关注点,一个是原子性问题,一个是可见性问题,要尽量避免竞态条件错误。
The above is the detailed content of Java's explanation of the principles of multi-threaded security issues. For more information, please follow other related articles on the PHP Chinese website!