Autumn Road strokes 8: JAVA lock system and a queue synchronizer abstract AQS

Figure entire system

Pessimistic locking, optimistic locking

It is a broad concept; reflects a different angle view of a thread synchronization.

Pessimistic locking

Believe in their own time use of data must be another thread to modify data, it will first be locked at the time of acquiring data, to ensure that data is not modified by another thread.
Implementation: keyword synchronized, the interface implementation class Lock
applicable scene: more than write operation, the first lock ensures that the correct data write operation.

Optimistic locking

I think they will not have time to use the data in other threads to modify the data, so it will not add locks, just in time before the update data to judge there is no other thread update this data.
Achieved: CAS algorithm , e.g. AtomicInteger class atomic increment is achieved by spin CAS
Application scenario: read more, locking features can not read performance boost.

Pessimistic locking, optimistic locking execution flow

CAS algorithm

Full name: Compare And Swap
, also known as lock-free algorithms: based on hardware primitives synchronous implementation, without the use of locks (no thread is blocked), to achieve variables between multiple threads.
jdk implemented: java.util.concurrent package - Atom (of AtomicInteger) is achieved by the CAS optimistic locking.

The principle

Three operands algorithm design
needs to read and write memory value V
compares a value (version) A
to write the new value B

Process is as follows:
If the value of V memory location to match the expected original value A, then the processor will be automatically updated to the new value of the position value B;
otherwise, do not change the location, just tell me the value of this position now, just tell I value this position now to (specific //).
Specific Pictured:

基于CAS实现的原子类关键方法

AtomicInteger.getAndDecrementUnsafe.getAndAddInt
源码:稍后补

CAS存在问题

ABA问题

因为CAS需要在操作值的时候检查下值有没有发生变化,如果没有发生变化则更新,
但是如果一个值原来是A,变成了B,又变成了A,那么使用CAS进行检查时会发现它的值没有发生变化,但是实际上却变化过。

ABA问题的解决思路就是使用版本号。
在变量前面追加上版本号,每次变量更新的时候把版本号加一,那么A-B-A 就会变成1A-2B-3A。

循环开销时间长

只能保证一个共享变量的原子操作

从Java1.5开始JDK提供了AtomicReference类来保证引用对象之间的原子性,
可以把多个变量放在一个对象里来进行CAS操作。

自旋锁

它是为实现保护共享资源而提出一种锁机制。其实,自旋锁与互斥锁比较类似,它们都是为了解决对某项资源的互斥使用。无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。

但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁,"自旋"一词就是因此而得名。

线程的上下文切换

一个重要的问题:线程切换为什么会耗资源耗时间?

  1. 线程切换的时候,CPU需要将此线程的所有执行状态保存起来,如线程编号,执行到的位置等,然后再去执行其它线程。
  2. CPU运行状态分为用户态和内核态。线程切换状态会使CPU运行状态从用户态转换到内核态。
    这里面有一篇:专门讲用户态和内存态的[https://www.cnblogs.com/maxigang/p/9041080.html]。

AQS队列同步器原理


ReentrantLock中的一个内部类

其具体是基于CLH队列实现。

具体原理

AQS 是构建锁或者其他同步组件的基础框架(如 ReentrantLock、ReentrantReadWriteLock、Semaphore 等), 包含了实现同步器的细节(获取同步状态、FIFO 同步队列)。AQS 的主要使用方式是继承,子类通过继承同步器,并实现它的抽象方法来管理同步状态。

  1. 维护一个同步状态 state。当 state > 0时,表示已经获取了锁;当state = 0 时,表示释放了锁。
  2. AQS 通过内置的 FIFO 同步队列来完成资源获取线程的排队工作:如果当前线程获取同步状态失败(锁)时,AQS 则会将当前线程以及等待状态等信息构造成一个节点(Node)并将其加入同步队列,同时会阻塞当前线程当同步状态释放时,则会把节点中的线程唤醒,使其再次尝试获取同步状态。

AQS 内部维护的是** CLH 双向同步队列**

具体原理图

公平锁,非公平锁

在公平的锁中,如果有另一个线程持有锁或者有其他线程在等待队列中等待这个所,那么新发出的请求的线程将被放入到队列中。
而非公平锁上,只有当锁被某个线程持有时,新发出请求的线程才会被放入队列中(此时和公平锁是一样的)。
所以,它们的差别在于非公平锁会有更多的机会去抢占锁。

总之就是:先来的先排队,先来的先获取资源,而非公平锁则无法提供这个保障。
其实对于非公平锁,只要线程进入了等待队列,队列里面依然是FIFO的原则,跟公平锁的顺序是一样的。

可重入锁

可重入锁作用:避免死锁.
场景:

public class Demo1 {
    public synchronized void functionA(){
        System.out.println("iAmFunctionA");
        functionB();
    }
    public synchronized void functionB(){
        System.out.println("iAmFunctionB");
    }
}

functionA()和functionB()都是同步方法,当线程进入funcitonA()会获得该类的对象锁,这个锁"new Demo1()",在functionA()对方法functionB()做了调用,但是functionB()也是同步的,因此该线程需要再次获得该对象锁(new Demo1())。其他线程是无法获该对象锁的。
可重入锁的作用就是为了避免死锁,java中synchronized和ReentrantLock都是可重入锁

可重入就是说某个线程已经获得某个锁,可以再次获取锁而不会出现死锁。
其实,就是改变上图的state的状态++。

Guess you like

Origin www.cnblogs.com/whyaza/p/12336777.html