java锁类型详解(ReentrantLock/ReentrantReadWriteLock)

目录

    • 一.公平锁
    • 二.非公平锁
    • 三.可重入锁
    • 四.自旋锁
    • 五.读写锁

总结一下java各种锁。

一.公平锁

公平锁:是指多个线程按照申请锁的顺序来获取锁;每个线程在获取锁时,先查看此锁维护的等待队列,如果为空或当前线程是队列中的第一个,就获取锁,否则就会加如到等待队列中。类似食堂打饭,先来后到。

二.非公平锁

非公平锁:是指多个线程获取锁的顺序并不是按照申请锁的顺序,有可能后申请锁的线程先获取锁;线程上来就尝试占有锁,如果尝试失败,才会采用类似公平锁的那种方式。在高并发的场景,可能造成优先级反转或饥饿现象(某个线程一直获取不到锁)。

三.可重入锁

可重入锁(递归锁):指的是同一个线程,外层函数获得锁之后,内层递归函数仍然能获取该锁的代码;同一个线程,在外层函数获得锁之后,在进入内层方法时,会自动获取锁;
也就是说,线程可以进入任何一个,它已经拥有的锁(lock),所同步着的代码块
1.synchronized
synchronized是一个典型的可重入锁;

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

说明:一个线程在调用sendSMS方法时,此时已经获取到锁,当在sendSMS中继续调用sendEmail时,会直接获取锁;虽然此方法已经加了synchronized,但是线程在sendSMS已经获取到锁,并且synchronized时可重入的;
2.ReentrantLock
ReentrantLock也是一个典型的可重入锁;默认是非公平锁。

    //声明一个可重入锁
    Lock lock = new ReentrantLock();
    
    public void get() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getId() + "\t invoked set()");
            //调用下一个加锁的方法
            set();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }

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

说明:一个线程在进入get方法后(说明已获取lock),可直接继续调用set方法;虽然set方法是被lock锁着的,但是在调用get时,已经获取到锁,并且set方法也是用同一把lock锁着的。
ReentrantLock的详细原理分析,可参考链接:ReentrantLock原理&源码解析

四.自旋锁

自旋是指,尝试获取锁的线程,不会立即阻塞,而是采用循环的方式去尝试获取锁,
优点:减少线程上下文切换的消耗
缺点:循环会消耗cpu。
原理:利用Unsafe类的cas原理实现。java中提供了一些原子类,比如AtomicReference,可以用它手动实现自旋锁。

手写一个自旋锁:

public class SpinLockDemo {
    /**
     * 原子引用线程
     */
    private AtomicReference<Thread> atomicReference = new AtomicReference<>();
    public void myLock() {
        Thread thread = Thread.currentThread();
        System.out.println(Thread.currentThread().getName() + "\t come in");
        while (!atomicReference.compareAndSet(null, thread)) {

        }
    }

    public void myUnlock() {
        Thread thread = Thread.currentThread();
        atomicReference.compareAndSet(thread, null);
        System.out.println(Thread.currentThread().getName() + "\t invocked myUnlock");
        System.out.println();
    }
    
    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();
    }
}

五.读写锁

可实现读写分离,提高程序的并发性能。主要是因为多个线程可以同时读取。

  • 读-读:能共存
  • 读-写:不能共存
  • 写-写:不能共存

读写锁其实可细分为其他几种名字的锁:
独占锁(写锁):一次只能被一个线程所持有,ReentrantLock和Synchronized都是独占锁;
共享锁(读锁):指该锁可以被多个线程所持有;ReentrantReadWriteLock的读锁是共享锁,写锁是独占锁;
互斥锁:不能同时进行,比如写-写,读-写。

ReentrantReadWriteLock的使用demo:

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();
        }
    }
}

你可能感兴趣的:(java锁类型详解(ReentrantLock/ReentrantReadWriteLock))