美文网首页
理解J.U.C中的ReentrantLock

理解J.U.C中的ReentrantLock

作者: 吉他手_c156 | 来源:发表于2020-06-01 23:13 被阅读0次

    JUC(java.util.concurrent工具包的简称)

    Lock(synchronized)

    ReentrantLock 重入锁
    // 重入锁
        private static Lock lock = new ReentrantLock();
        private static int count = 0;
        public static void inc(){
            lock.lock();  // 获得锁
            try {
                Thread.sleep(1);
                count++;
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();  // 锁
            }
        }
    

    Lock 它不会像 synchronized 自动释放锁,而是要手动释放锁,否则别的线程是无法抢占到锁的,所以需要在 finally 中去做一个释放锁的操作

    ReentrantReadWriteLock 重入读写锁

    读和读不互斥,读和写互斥,写和写互斥,在读多写少的情况下,读数据是不需要互斥的,因为读本身不会影响数据的改变,引入读写锁是为了解决原本互斥锁带来的性能问题。

    public class ReentrantReadWriteLockDemo {
    
        static Map<String,Object> cacheMap = new HashMap<String, Object>();
        static ReadWriteLock rwl = new ReentrantReadWriteLock();
        static Lock read = rwl.readLock(); // 读锁
        static Lock write = rwl.writeLock(); // 写锁
    
        public static Object get(String key){
            read.lock(); // 读锁
            try {
                // TODO  业务......
            }finally {
                read.unlock(); // 释放锁
            }
            return cacheMap.get(key);
        }
    
        public static void set(String key ,Object value){
            write.lock();  // 写锁
            try{
                //  TODO   业务.....
            }finally {
                write.unlock(); // 释放锁
            }
        }
    }
    
    重入锁的特性

    看代码

        // 重入锁
        private static Lock lock = new ReentrantLock();
        private static int count = 0;
        public static void inc(){
            lock.lock();  // 获得锁
            try {
                Thread.sleep(1);
                count++;
    
                decr();   // 调用减减操作
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                lock.unlock();  // 锁
            }
        }
        
        public static void decr(){
            lock.lock();  // 获得锁
            try{
                count --;
            }finally {
                lock.unlock();  // 释放锁
            }
        }
    

    正如上面代码操作,假设线程 A 进入 inc() 方法抢占到了锁,这是方法走到 decr() 方法中,又去抢占锁,而这时需要 inc() 方法释放锁,decr() 方法才能继续抢占锁,但是这里线程 A 在 inc() 方法中无法释放掉锁,这样你等你我等你,就会出现死锁情况!而重入锁的重入特性就避免了这一情况,在 decr() 方法中不需要在此抢占锁,而只是增加重入的次数。


    image.png

    思考锁的实现(设计思维)

    互斥,就是说多个线程访问某个共享资源的时候,只有一个线程可以持有资源,其他线程无法持有

    • 锁的互斥特性 - 共享资源
    • 没有抢占到锁的线程 - 释放 CPU 资源 ,等待/唤醒
    • 等待的线程如何存储 - 数据结构去存储一些等待中的线程 FIFO(等待队列)
    • 公平非公平 (能否插队)
    • 重入的特性 - (识别是否是同一个线程?threadID)

    技术方案

    • volatile(保证可见性,原子性) state=0(无锁),1代表持有锁,>1代表重入
    • wait/notify | condition 需要唤醒指定的线程,LockSupport.park() -> unpark(thread)
    • 存储【双向链表】,需要行前或者向后查询,需要前指针后指针双向查找
    • 公平/非公平,可以逻辑实现
    • 重入特性,在某一个地方存储当前获得锁的线程 ID,判断下次抢占锁的线程是否为同一个

    相关文章

      网友评论

          本文标题:理解J.U.C中的ReentrantLock

          本文链接:https://www.haomeiwen.com/subject/clsdzhtx.html