【Java并发基础】加锁机制解决原子性问题

原子性指一个或多个操作在CPU执行的过程不被中断的特性。前面提到原子性问题产生的源头是线程切换,而线程切换依赖于CPU中断。于是得出,禁用CPU中断就可以禁止线程切换从而解决原子性问题。但是这种情况只适用于单核,多核时不适用。

以在 32 位 CPU 上执行 long 型变量的写操作为例来说明。
long 型变量是 64 位,在 32 位 CPU 上执行写操作会被拆分成两次写操作(写高 32 位和写低 32 位,如下图所示,图来自【参考1】)。

【Java并发基础】加锁机制解决原子性问题

在单核 CPU 场景下,同一时刻只有一个线程执行,禁止 CPU 中断,意味着操作系统不会重新调度线程,即禁止了线程切换,获得 CPU 使用权的线程就可以不间断地执行。所以两次写操作一定是:要么都被执行,要么都没有被执行,具有原子性。
但是在多核场景下,同一时刻,可能有两个线程同时在执行,一个线程执行在 CPU-1 上,一个线程执行在 CPU-2 上。此时禁止 CPU 中断,只能保证 CPU 上的线程连续执行,并不能保证同一时刻只有一个线程执行。如果这两个线程同时向内存写 long 型变量高 32 位的话,那么就会造成我们写入的变量和我们读出来的是不一致的。

所以解决原子性问题的重要条件还是为:同一时刻只能有一个线程对共享变量进行操作,即互斥。如果我们能够保证对共享变量的修改是互斥的,那么,无论是单核 CPU 还是多核 CPU,就都能保证原子性。

下面将介绍实现互斥访问的方案,加锁机制。

锁模型

我们把一段需要互斥执行的代码称为临界区
线程在进入临界区之前,首先尝试加锁 lock(),如果成功,则进入临界区,此时我们称这个线程持有锁;
否则就等待或阻塞,直到持有锁的线程释放锁。持有锁的线程执行完临界区的代码后,执行解锁 unlock()。

锁和锁要保护的资源是要对应的。这个指的是两点:①我们要保护一个资源首先要创建一把锁;②锁要锁对资源,即锁A应该用来保护资源A,而不能用它来锁资源B。

所以,最后的锁模型如下:(图来自【参考1】)

【Java并发基础】加锁机制解决原子性问题

Java提供的锁技术: synchronized

锁是一种通用的技术方案,Java 语言提供的 synchronized关键字,就是锁的一种实现。

synchronized 关键字可以用来修饰方法,也可以用来修饰代码块,它的使用示例如下:

class X { // 修饰非静态方法 synchronized void foo() { // 临界区 } // 修饰静态方法 synchronized static void bar() { // 临界区 } // 修饰代码块 Object obj = new Object(); void baz() { synchronized(obj) { // 临界区 } } }

与上面的锁模型比较,可以发现synchronized修饰的方法和代码块都没有显式地有加锁和释放锁操作。但是这并不代表没有这两个操作,这两个操作Java编译器会帮我们自动实现。Java 编译器会在 synchronized 修饰的方法或代码块前后自动加上加锁 lock() 和解锁 unlock(),这样的好处在于代码更简洁,并且Java程序员也不必担心会忘记释放锁了。

然后我们再观察可以发现:只有修饰代码块的时候,锁定了一个 obj 对象。那么修饰方法的时候锁了什么呢?
这是Java的一个隐式规则:

当修饰静态方法时,锁的是当前类的 Class 对象,在上面的例子中就是 X.class;

当修饰非静态方法时,锁定的是当前实例对象 this

对于上面的例子,synchronized 修饰静态方法相当于:

class X { // 修饰静态方法 synchronized(X.class) static void bar() { // 临界区 } }

修饰非静态方法,相当于:

class X { // 修饰非静态方法 synchronized(this) void foo() { // 临界区 } } 内置锁

每个Java对象都可以用作一个实现同步的锁,这些锁被称为内置锁(Intrinsic Lock)或者监视器锁(Monitor Lock)。被synchronized关键字修饰的方法或者代码块,称为同步代码块(Synchronized Block)。线程在进入同步代码块之前会自动获取锁,并且在退出同步代码块时自动释放锁,这在前面也提到过。

Java的内置锁相当于一种互斥体(或互斥锁),这也就是说,最多只有一个线程能够持有这个锁。由于每次只能有一个线程执行内置锁保护的代码块,因此,由这个锁保护的同步代码块会以原子的方式执行

内置锁是可重入的

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zwwgjx.html