Java thread safety - reentrant lock

Author: pheasant

As a tool for concurrently sharing data and ensuring consistency, locks have multiple implementations on the JAVA platform (such as synchronized and ReentrantLock, etc.). These locks that have been written and provided provide convenience for our development, but the specific properties and types of locks are rarely mentioned. This series of articles will analyze the common lock names and characteristics under JAVA, and answer your questions.

4. Reentrant lock:

This article is about reentrant locks in a broad sense, not just ReentrantLock under JAVA.

Reentrant locks, also known as recursive locks, refer to the fact that after the outer function of the same thread acquires the lock, the inner recursive function still has the code to acquire the lock, but it is not affected.
ReentrantLock and synchronized are reentrant locks in the JAVA environment

The following is an example of use

public class Test implements Runnable{

    public synchronized void get(){
        System.out.println(Thread.currentThread().getId());
        set();
    }

    public synchronized void set(){
        System.out.println(Thread.currentThread().getId());
    }

    @Override
    public void run() {
        get();
    }
    public static void main(String[] args) {
        Test ss=new Test();
        new Thread(ss).start();
        new Thread(ss).start();
        new Thread(ss).start();
    }
}
public class Test implements Runnable {
    ReentrantLock lock = new ReentrantLock();

    public void get() {
        lock.lock();
        System.out.println(Thread.currentThread().getId());
        set();
        lock.unlock();
    }

    public void set() {
        lock.lock();
        System.out.println(Thread.currentThread().getId());
        lock.unlock();
    }

    @Override
    public void run() {
        get();
    }

    public static void main(String[] args) {
        Test ss = new Test();
        new Thread(ss).start();
        new Thread(ss).start();
        new Thread(ss).start();
    }
}

The final result of both examples is correct, that is, the same thread id is output twice in a row.

The result is as follows:

Threadid: 8
Threadid: 8
Threadid: 10
Threadid: 10
Threadid: 9
Threadid: 9

The biggest role of reentrant locks is to avoid deadlocks.
Let's take spin locks as an example,

public class SpinLock {
    private AtomicReference<Thread> owner =new AtomicReference<>();
    public void lock(){
        Thread current = Thread.currentThread();
        while(!owner.compareAndSet(null, current)){
        }
    }
    public void unlock (){
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }
}

For spin locks,
1. If the same thread calls lock() twice, it will cause the second call to the lock position to spin, resulting in a deadlock,
indicating that the lock is not reentrant. (In the lock function, it should be verified whether the thread is the thread that has obtained the lock)
2. If the problem 1 has been solved, when unlock() is called for the first time, the lock has been released. The lock should not actually be released.
(using count times for statistics)
After modification, it is as follows:

public class SpinLock1 {
    private AtomicReference<Thread> owner =new AtomicReference<>();
    private int count =0;
    public void lock(){
        Thread current = Thread.currentThread();
        if(current==owner.get()) {
            count++;
            return ;
        }

        while(!owner.compareAndSet(null, current)){

        }
    }
    public void unlock (){
        Thread current = Thread.currentThread();
        if(current==owner.get()){
            if(count!=0){
                count--;
            }else{
                owner.compareAndSet(current, null);
            }

        }

    }
}

The spinlock is a reentrant lock.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325810198&siteId=291194637
Recommended