On the lock of fair / unfair lock / reentrant lock / recursive lock / spin locks understand? Please handwriting a spin lock

Fair and unfair lock

What is

Fair locks: it refers to the number of threads in order to acquire the lock application lock, similar to line up Dafan, first come first served.

Unfair lock: is the order of multiple threads to obtain the lock is not in order to apply the lock, it is possible to apply the thread after thread priority to acquire the lock than prior application, in the case of high concurrency, it may cause anti priority transfer or hunger

The difference between the two

Lock fair / unfair Lock

And creation of ReentrantLock contract may specify boolean type constructor to fair fair lock or locks, lock default non-fair

/**
     * Creates an instance of {@code ReentrantLock}.
     * This is equivalent to using {@code ReentrantLock(false)}.
     */
    public ReentrantLock() {
        sync = new NonfairSync();
    }

About the difference between the two:
fair locks:. Threads acquire a fair lock in the order in which they requested it
fair locks, is very fair, in a concurrent environment, each thread will first see this lock when acquiring the lock waiting queue maintenance , if empty, or the current thread is waiting on a queue, it occupies a lock, otherwise it will be added to the waiting queue, the future will be taken from the FIFO queue in accordance with the rules to their own

Unfair lock: a nonfair lock permits barging: threads requesting a lock can jump ahead of the queue of waiting threads if the lock happens to be available when it is requested.
Nonfair lock relatively rude, come directly try to have a lock, if you try fails, then a similar fair locks that way.

Digression

Java ReentrantLock terms,
specified by the constructor whether the lock is locked fair, non-default fair locks. Unfair advantage that the lock is larger than the throughput fairness lock.

For Synchronized, it is also an unfair lock

Reentrant lock (also known as recursive lock)

What is

Reentrant lock (also called recursive lock)

Refers to the outer function after the same thread acquires the lock, the inner recursive function can still acquire the lock code, while acquiring the lock in the same way as the outer layer of a thread, it will automatically get into the inner lock method

That is, any thread can enter a code block it already has a lock synchronized with the.

ReentrantLock / Synchronized is a typical lock reentrant

Reentrant lock greatest effect is to avoid deadlocks

ReentrantLockDemo code demonstrates

package com.brian.interview.study.thread;

import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Copyright (c) 2020 ZJU All Rights Reserved
 * <p>
 * Project: JavaSomeDemo
 * Package: com.brian.interview.study.thread
 * Version: 1.0
 * <p>
 * Created by Brian on 2020/2/12 11:01
 */

class Phone implements Runnable{
    public synchronized void sendSMS() throws Exception {
        System.out.println(Thread.currentThread().getName() + "\t invoked sendSMS()");
        sendEmail();
    }

    public synchronized void sendEmail() throws Exception {
        System.out.println(Thread.currentThread().getName() + "\t #####invoked sendEmail()");
    }

    // ==========================================================
    Lock lock = new ReentrantLock();
    @Override
    public void run() {
        get();
    }

    public void get() {
        lock.lock();
        lock.lock();   // 只要两两配对几把锁都可以
        try {
            // 线程可以进入任何一个它已经拥有的锁
            //
            // 所同步着的代码块
            System.out.println(Thread.currentThread().getName() + "\t invoked get()");
            set();
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
            lock.unlock();
        }
    }

    public void set() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t #####invoked set()");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            lock.unlock();
        }
    }
}

/**
 * 可重入锁(也叫做递归锁)
 *
 * 指的是同一线程外层函数获得锁之后, 内层递归函数仍然能获取该锁的代码,
 * 在同一个线程在外层方法获取锁的时候, 在进入内层方法会自动获取锁
 *
 * 也即是说, 线程可以进入任何一个它已经拥有的锁所同步着的代码块。
 *
 * case one synchronized 就是一个典型的可重入锁
 * t1	 invoked sendSMS()          t1线程在外层方法获取锁的时候
 * t1	 #####invoked sendEmail()   t1进入内层方法会自动获取锁
 *
 * t2	 invoked sendSMS()          t2线程在外层方法获取锁的时候
 * t2	 #####invoked sendEmail()   t2进入内层方法会自动获取锁
 *
 *
 * case two ReentrantLock 就是一个典型的可重入锁
 * t3	 invoked get()
 * t3	 #####invoked set()
 * t4	 invoked get()
 * t4	 #####invoked set()
 */
public class ReentrantLockDemo {
    public static void main(String[] args) {
        Phone phone = new Phone();

        new Thread(() -> {
            try {
                phone.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                phone.sendSMS();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }, "t2").start();

        Thread t3 = new Thread(phone, "t3");
        Thread t4 = new Thread(phone, "t4");

        t3.start();
        t4.start();
    }
}

Spinlock (spinlock)

Refers to a thread attempts to acquire the lock will not be blocked immediately, but by way of circulation to try to acquire the lock , this benefit is to reduce the consumption of thread context switching, the drawback is that the cycle will consume CPU
Here Insert Picture Description

package com.brian.interview.study.thread;

/**
 * Copyright (c) 2020 ZJU All Rights Reserved
 * <p>
 * Project: JavaSomeDemo
 * Package: com.brian.interview.study.thread
 * Version: 1.0
 * <p>
 * Created by Brian on 2020/2/12 11:47
 */

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

/**
 * 题目:实现一个自旋锁
 * 自旋锁好处:循环比较获取直到成功为止, 没有类似 wait 的阻塞。
 *
 * 通过CAS操作完成自旋锁, A线程先进来调用 myLock 方法自己持有锁5秒钟, B随后进来后发现
 * 当前有线程持有锁, 不是 null, 所以只能通过自旋等待, 直到A释放锁后B随后抢到。
 */
public class SpinLockDemo {

    // 原子引用线程
    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void myLock(){
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t come in (⊙o⊙)");
        while (!atomicReference.compareAndSet(null, thread)){

        }
    }

    public void myUnlock(){
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName()+"\t invoked myUnlock()");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            spinLockDemo.myLock();

            // 暂停一会儿线程
            try {
                TimeUnit.SECONDS.sleep(5);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }

            spinLockDemo.myUnlock();
        }, "AA").start();

        // 暂停一会儿线程
        try {
            TimeUnit.SECONDS.sleep(1);
        }catch (InterruptedException e) {
            e.printStackTrace();
        }

        new Thread(() -> {
            spinLockDemo.myLock();

            try {
                TimeUnit.SECONDS.sleep(1);
            }catch (InterruptedException e) {
                e.printStackTrace();
            }

            spinLockDemo.myUnlock();
        }, "BB").start();
    }
}

Exclusive lock (write lock) / share lock (write lock) / mutex

Exclusive lock: that the lock can only be held by a thread. For ReentrantLock and Synchronized are exclusive lock

Shared lock: that the lock can be held by multiple threads.
For ReentrantReadWriteLock its read lock is a shared lock, write lock is an exclusive lock it.
Shared lock to read lock ensures very efficient concurrent read, write, write, read, write processes are mutually exclusive.

package com.brian.interview.study.thread;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * Copyright (c) 2020 ZJU All Rights Reserved
 * <p>
 * Project: JavaSomeDemo
 * Package: com.brian.interview.study.thread
 * Version: 1.0
 * <p>
 * Created by Brian on 2020/2/12 13:44
 */

class MyCache {  // 资源类
    private volatile Map<String, Object> map = new HashMap<>();
    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    public void put(String key, Object value) {

        rwLock.writeLock().lock();
        try{
            System.out.println(Thread.currentThread().getName() + "\t 正在写入:" + key);
            // 暂停一会儿线程
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            map.put(key, value);
            System.out.println(Thread.currentThread().getName() + "\t 写入完成");
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rwLock.writeLock().unlock();
        }
    }

    public void get(String key) {
        rwLock.readLock().lock();
        try {
            System.out.println(Thread.currentThread().getName() + "\t 正在读取");
            // 暂停一会儿线程
            try {
                TimeUnit.MILLISECONDS.sleep(300);
            }catch (InterruptedException e){
                e.printStackTrace();
            }
            Object result = map.get(key);
            System.out.println(Thread.currentThread().getName() + "\t 读取完成:" + result);
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            rwLock.readLock().unlock();
        }
    }

    public void clearMap(){
        map.clear();
    }
}

/**
 * 多个线程同时读一个资源类没有任何问题, 所以为了满足并发量, 读取共享资源应该可以同时进行。
 * 但是
 * 如果有一个线程想去写共享资源来, 就不应该再有其它线程可以对该资源进行读或写
 * 小总结:
 *     读-读能共存
 *     读-写不能共存
 *     写-写不能共存
 *
 *     写操作:原子+独占, 整个过程必须是一个完整的统一体, 中间不许被分割, 不许被打断
 */
public class ReadWriteLockDemo {
    public static void main(String[] args) {
        MyCache myCache = new MyCache();

        for (int i = 1; i <= 5; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.put(tempInt+"", tempInt+"");
            }, String.valueOf(i)).start();
        }

        for (int i = 1; i <= 5; i++) {
            final int tempInt = i;
            new Thread(() -> {
                myCache.get(tempInt+"");
            }, String.valueOf(i)).start();
        }
    }
}

Published 77 original articles · won praise 79 · views 5707

Guess you like

Origin blog.csdn.net/qq_35340189/article/details/104570079