• ReentrantLock可重入锁的理解和源码简单分析


    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author admin
     * @date 2018/1/16 12:16
     * ReentrantLock 可重入锁:
     * 1、启动两个线程,线程A获取锁,然后执行;同时线程B进来后,一直阻塞,直到线程A释放锁之后,线程B才接着执行
     */
    public class ReentrantLockTest {
        ReentrantLock lock = new ReentrantLock();
    
        public void reentrantLockRun1(String threadName) {
            System.out.println(threadName + "进入");
            lock.lock();
            System.out.println(threadName + "方法被锁");
            try {
                System.out.println(threadName + "方法执行");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
                System.out.println(threadName + "锁被释放");
            }
        }
    
        public static void main(String[] args) {
            ReentrantLockTest rltest = new ReentrantLockTest();
            Thread thread = new Thread() {
                public void run() {
                    rltest.reentrantLockRun1("线程A");
                }
            };
            thread.start();
    
            Thread thread2 = new Thread() {
                public void run() {
                    rltest.reentrantLockRun1("线程B");
                }
            };
            thread2.start();
        }
    }
    
    运行结果:
    线程A进入
    线程A方法被锁
    线程A方法执行
    线程B进入
    线程A锁被释放
    线程B方法被锁
    线程B方法执行
    线程B锁被释放
    
    import java.util.concurrent.TimeUnit;
    import java.util.concurrent.locks.ReentrantLock;
    
    /**
     * @author admin
     * @date 2018/1/16 12:16
     * ReentrantLock 可重入锁:
     * 1、启动两个线程,线程A获取锁,然后执行,不释放锁,接着线程A再调用reentrantLockRun2,不需要阻塞,接着执行,最后释放锁;说明同一个线程对ReentrantLock可重复获取
     * 2、线程B在这个过程中一直阻塞,等到线程A把所有的锁释放完之后,再获取锁,执行方法,最后释放锁
     */
    public class ReentrantLockTest2 {
        ReentrantLock lock = new ReentrantLock();
    
        public ReentrantLock reentrantLockRun1(String threadName) {
            System.out.println(threadName + "进入");
            lock.lock();
            System.out.println(threadName + "方法被锁");
            try {
                System.out.println(threadName + "方法执行");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return lock;
        }
    
        public ReentrantLock reentrantLockRun2(String threadName) {
            System.out.println(threadName + "进入");
            lock.lock();
            System.out.println(threadName + "方法被锁");
            try {
                System.out.println(threadName + "方法执行");
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return lock;
        }
    
        public static void main(String[] args) {
            ReentrantLockTest2 rltest = new ReentrantLockTest2();
            Thread thread = new Thread() {
                public void run() {
                    ReentrantLock lock1 = rltest.reentrantLockRun1("线程A");
                    ReentrantLock lock2  = rltest.reentrantLockRun2("线程A2");
                    lock1.unlock();
                    System.out.println("线程A释放锁");
                    lock2.unlock();
                    System.out.println("线程A2释放锁");
    
                }
            };
            thread.start();
    
            Thread thread2 = new Thread() {
                public void run() {
                    ReentrantLock lock = rltest.reentrantLockRun1("线程B");
                    lock.unlock();
                    System.out.println("线程B释放锁");
    
                }
            };
            thread2.start();
        }
    }
    
    运行结果:
    线程A进入
    线程A方法被锁
    线程A方法执行
    线程B进入
    线程A2进入
    线程A2方法被锁
    线程A2方法执行
    线程A释放锁
    线程A2释放锁
    线程B方法被锁
    线程B方法执行
    线程B释放锁
    

    根据源码发现:维护了这个可见性变量state ;同一个线程对可重入锁体现用state标记作累加,int nextc = c + acquires;

    private volatile int state;

    public void lock() {
    sync.lock();
    }
    public final void acquire(int arg) {
    if (!tryAcquire(arg) &&
    acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
    selfInterrupt();
    }

    // 判断是不是第一次获取锁,如果是操作state=1;否则判断是不是同一个线程如果是state+1,如果不是同一个线程直接返回false
    protected final boolean tryAcquire(int acquires) {
    final Thread current = Thread.currentThread();
    int c = getState();
    if (c == 0) {
    if (!hasQueuedPredecessors() &&
    compareAndSetState(0, acquires)) {
    setExclusiveOwnerThread(current);
    return true;
    }
    }
    else if (current == getExclusiveOwnerThread()) {
    int nextc = c + acquires;
    if (nextc < 0)
    throw new Error("Maximum lock count exceeded");
    setState(nextc);
    return true;
    }
    return false;
    }
    }
    // 大概就是用一个链表来维护等待线程
    final boolean acquireQueued(final Node node, int arg) {
    boolean failed = true;
    try {
    boolean interrupted = false;
    for (;;) {
    final Node p = node.predecessor();
    if (p == head && tryAcquire(arg)) {
    setHead(node);
    p.next = null; // help GC
    failed = false;
    return interrupted;
    }
    if (shouldParkAfterFailedAcquire(p, node) &&
    parkAndCheckInterrupt())
    interrupted = true;
    }
    } finally {
    if (failed)
    cancelAcquire(node);
    }
    }
     
  • 相关阅读:
    Lodop客户端本地角色注册号常见误区
    Spring中加载xml配置文件的六种方式
    源程序出现各种奇怪的符号P
    MyEclipse项目中的java文件的图标变成空心的问题
    servlet中的相对路径和绝对路径 及/, ./, ../的区别
    Thread 与 Runnable
    Class.forName()用法详解
    chain.doFilter(request,response)含义
    jsp简单标签开发(一)
    createStatement()的用法
  • 原文地址:https://www.cnblogs.com/xubiao/p/8296000.html
Copyright © 2020-2023  润新知