This article brings you relevant knowledge about java, which mainly introduces issues related to the three elements of concurrent programming, including atomicity, visibility, orderliness and their generation Let’s take a look at the reasons, definitions, etc. below, I hope it will be helpful to everyone.
## Recommended study: "
java video tutorial"
1 Atomicity
1.1 Definition of atomicity
Atomicity refers to one or more operations that are either all executed and are not interrupted by other operations during the execution process. Or just don’t execute them all.
1.2 Causes of atomicity problems
Thread switching is the cause of atomicity problems. Thread switching is to improve CPU utilization.
Taking count as an example, at least three CPU instructions are required:
Instruction 1: First, the variable count needs to be loaded from memory to the CPU register; - Instructions 2: After that, perform 1 operation in the register;
- Instruction 3: Finally, write the result to the memory (the cache mechanism may cause the CPU cache instead of the memory to be written).
-
We assume count=0. If thread A switches threads after instruction 1 is executed, and thread A and thread B execute according to the sequence in the figure below, then we will find that both threads are executed. Count =1 operation, but the result is not the 2 we expected, but 1.
1.3 Atomic operations
In a multi-threaded environment, Java only guarantees that variables and assignment operations of basic data types are atomic (
Note: In a 32-bit JDK environment, reading 64-bit data is not an atomic operation*, such as long, double)
1.4 How to solve the atomicity problem
If we can ensure that modifications to shared variables are mutually exclusive, then atomicity can be guaranteed whether it is a single-core CPU or a multi-core CPU. Locking can solve atomicity problems, such as using synchronized and lock.
2 Visibility
2.1 Visibility definition
Visibility refers to when multiple threads operate a shared variable, where After one thread modifies a variable, other threads can immediately see the result of the modification.
2.2 Causes of visibility problems
The data consistency between the CPU cache and the memory is the cause of the visibility problem. The CPU cache is to improve the efficiency of the CPU.
2.3 Visibility problem solution
The cause of the visibility problem is the CPU cache, then we can disable the CPU cache.
The volatile field can disable CPU caching and solve visibility problems. - Both synchronized and locks can guarantee visibility.
-
2.4 What are visibility rules
The visibility rule is the Happens-Before rule.
Happens-Before rules:
Simply put: - The result of the previous operation is visible to subsequent operations.
Happens-Before restricts the optimization behavior of the compiler. Although the compiler is allowed to optimize, it requires the compiler to comply with the Happens-Before rules after optimization. -
2.5 Happens-Before rules
In a thread, follow the program In order, the previous operation happens-before any subsequent operation.
class Example {
public void test() {
int x = 42; ①
int y = 20; ②
}
}
Copy after login
① Happens-Before ②.
A write operation to a volatile variable happens-before a subsequent read operation to the volatile variable.
If A Happens-Before B, and B Happens-Before C, then A Happens-Before C.
class Example {
int x = 0;
volatile int y = 0;
public void writer() {
x = 42; ①
y = 1; ②
}
public void reader() {
if (y == 1) { ③
// 这里x会是多少呢?
}
}
}
Copy after login
① Happens-Before ②, satisfying rule 1-sequential rule. - ② Happens-Before ③, satisfying rule 2-volatile variable rule.
- ① Happens-Before ③, satisfying rule 3-transitive rule. If y == 1, then x = 42;
-
- Rules for locks in the monitor
Unlocking a lock Happens-Before for subsequent locking of this lock.
管程是一种通用的同步原语,在 Java 中指的就是 synchronized,synchronized 是 Java 里对管程的实现。
synchronized (this) { //此处自动加锁
// x是共享变量,初始值=10
if (this.x < 12) {
this.x = 12;
}
} //此处自动解锁
Copy after login
假设 x 的初始值是 10,线程 A 执行完代码块后 x 的值会变成 12(执行完自动释放锁);
线程 B 进入代码块时,能够看到线程 A 对 x 的写操作,也就是线程 B 能够看到 x==12。
它是指主线程 A 启动子线程 B 后,子线程 B 能够看到主线程在启动子线程 B 前的操作。
它是指主线程 A 等待子线程 B 完成(主线程 A 通过调用子线程 B 的 join() 方法实现),当子线程 B 完成后(主线程 A 中 join() 方法返回),主线程能够看到子线程的操作。当然所谓的“看到”,指的是对共享变量的操作。
3 有序性
3.1 有序性的定义
有序性,即程序的执行顺序按照代码的先后顺序来执行。
3.2 有序性问题原因
编译器为了优化性能,有时候会改变程序中语句的先后顺序。
例如:“a=6;b=7;”编译器优化后可能变成“b=7;a=6;”,在这个例子中,编译器调整了语句的顺序,但是不影响程序的最终结果。
以双重检查代码为例:
public class Singleton {
static Singleton instance;
static Singleton getInstance(){
if (instance == null) { ①
synchronized(Singleton.class) {
if (instance == null)
instance = new Singleton(); ②
}
}
return instance;
}
}
Copy after login
上面的代码有问题,问题在 ② 操作上:经过优化后的执行路径是这样的:
- 分配一块内存 M;
- 将 M 的地址赋值给 instance 变量;
- 最后在内存 M 上初始化 Singleton 对象。
优化后会导致什么问题呢?我们假设线程 A 先执行 getInstance() 方法,当执行完 ① 时恰好发生了线程切换,切换到了线程 B 上;如果此时线程 B 也执行 getInstance() 方法,那么线程 B 在执行第一个判断时会发现 instance != null ,所以直接返回 instance,而此时的 instance 是没有初始化过的,如果我们这个时候访问 instance 的成员变量就可能触发空指针异常。
如何解决双重检查问题?变量用 volatile 来修饰,禁止指令重排序。
public class Singleton {
static volatile Singleton instance;
static Singleton getInstance(){
if (instance == null) { ①
synchronized(Singleton.class) {
if (instance == null)
instance = new Singleton(); ②
}
}
return instance;
}
}
Copy after login
推荐学习:《java视频教程》
The above is the detailed content of Learn more about the three elements of concurrent programming in Java. For more information, please follow other related articles on the PHP Chinese website!