ReentrantLock的使用

public class LockTest {
    
    private static final Lock lock = new ReentrantLock();
    
    public static void main(String[] args) throws Exception {
        Thread thread = new Thread() {
            @Override
            public void run() {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + " AAAAAAAAAA: " + lock.toString());
                method("a");
                lock.unlock();
            }
        };
        Thread thread2 = new Thread() {
            @Override
            public void run() {
                System.out.println(Thread.currentThread().getName() + " BBBBBBBBBB: " + lock.toString());
                method("b");
            }
        };
        thread.start();
        thread2.start();
    }


    public static void method(String a) {
        lock.lock();
        System.out.println(a);
        System.out.println(Thread.currentThread().getName() + " : " + lock.toString());
        for (int i = 0; i < 10; i++) {
            System.out.println(Thread.currentThread().getName() + " : " + i);
        }
        lock.unlock();
    }
}

ReentrantLock是可重入锁,上面的代码是使用的示例,在看源码之前需要先了解下ReentrantLock。

ReentrantLock和Synchronized都是可重入锁,可重入是指某线程已经获得某个锁,可以再次获取锁而不会出现死锁,不会因为之前已经获取过还没释放而阻塞。

ReentrantLock 与 synchronized 的区别

前面提到 ReentrantLock 提供了比 synchronized 更加灵活和强大的锁机制,那么它的灵活和强大之处在哪里呢?他们之间又有什么相异之处呢?

首先他们肯定具有相同的功能和内存语义。

  1. synchronized 相比,ReentrantLock提供了更多,更加全面的功能,具备更强的扩展性。例如:时间锁等候,可中断锁等候,锁投票。
  2. ReentrantLock 还提供了条件 Condition ,对线程的等待、唤醒操作更加详细和灵活,所以在多个条件变量和高度竞争锁的地方,ReentrantLock 更加适合(以后会阐述Condition)。
  3. ReentrantLock 提供了可轮询的锁请求。它会尝试着去获取锁,如果成功则继续,否则可以等到下次运行时处理,而 synchronized 则一旦进入锁请求要么成功要么阻塞,所以相比 synchronized 而言,ReentrantLock会不容易产生死锁些。
  4. ReentrantLock 支持更加灵活的同步代码块,但是使用 synchronized 时,只能在同一个 synchronized 块结构中获取和释放。注意,ReentrantLock 的锁释放一定要在 finally 中处理,否则可能会产生严重的后果。
  5. ReentrantLock 支持中断处理,且性能较 synchronized 会好些。

ReentrantLock的方法

• void lock() 获取锁,调用该方法当前线程将会获取锁,当锁获取后,该方法将返回。
• void lockInterruptibly() throws InterruptedException 可中断获取锁,与 lock()方法不同之处在于该方
法会响应中断,即在锁的获取过程中可以中断当前线程
• boolean tryLock() 尝试非阻塞的获取锁,调用该方法立即返回,true 表示获取到锁
• boolean tryLock(long time,TimeUnit unit) throws InterruptedException 超时获取锁,以下情况会返回:
时间内获取到了锁,时间内被中断,时间到了没有获取到锁。
• void unlock() 释放锁

ReentrantLock()底层是公平锁(FairSync)和非公平锁(NonfairSync)实现的,默认是非公平锁

new ReentrantLock(); // 默认非公平锁。也可以填入true指定为公平锁
-------->
public ReentrantLock() {
        sync = new NonfairSync();
    }

公平锁与非公平锁的区别在于,公平锁的锁获取是有顺序的。但是公平锁的效率往往没有非公平锁的效率高,在许多线程访问的情况下,公平锁表现出较低的吞吐量。

NonfairSync分析

static final class NonfairSync extends Sync {
    private static final long serialVersionUID = 7316153563782823691L;

    /**
     * Performs lock.  Try immediate barge, backing up to normal
     * acquire on failure.
     */
  // 获得锁
        final void lock() {
            if (compareAndSetState(0, 1)) // 比较并设置状态成功,状态0表示锁没有被占用
                // 把当前线程设置独占了锁
                setExclusiveOwnerThread(Thread.currentThread());
            else // 锁已经被占用,或者set失败
                // 以独占模式获取对象,忽略中断
                acquire(1); 
        }
  // 非公平的方式,获得同步状态。
    protected final boolean tryAcquire(int acquires) {
        return nonfairTryAcquire(acquires);
    }
}

NonfairSync继承Sync,Sync又继承了AbstractQueuedSynchronizer(AQS),所以实际上Lock锁底层是借助AQS同步队列实现的!

FairSync 是 ReentrantLock 的内部静态类,实现 Sync 抽象类,公平锁实现类。

FairSync

FairSync 是 ReentrantLock 的内部静态类,实现 Sync 抽象类,公平锁实现类。

#lock() 实现方法,代码如下:

final void lock() {
    acquire(1);
}

Sync分析

既然NonfairSync继承Sync,那就分析下Sync吧

abstract static class Sync extends AbstractQueuedSynchronizer {
        // 序列号
        private static final long serialVersionUID = -5179523762034025860L;       
    
    // 获取锁
    abstract void lock();
    
    // 非公平方式获取
    final boolean nonfairTryAcquire(int acquires) {
        // 当前线程
        final Thread current = Thread.currentThread();
        // 获取状态
        int c = getState();
        if (c == 0) { // 表示没有线程正在竞争该锁
            if (compareAndSetState(0, acquires)) { // 比较并设置状态成功,状态0表示锁没有被占用
                // 设置当前线程独占
                setExclusiveOwnerThread(current); 
                return true; // 成功
            }
        }
        else if (current == getExclusiveOwnerThread()) { // 当前线程拥有该锁
            int nextc = c + acquires; // 增加重入次数
            if (nextc < 0) // overflow
                throw new Error("Maximum lock count exceeded");
            // 设置状态
            setState(nextc); 
            // 成功
            return true; 
        }
        // 失败
        return false;
    }
    
    // 试图在共享模式下获取对象状态,此方法应该查询是否允许它在共享模式下获取对象状态,如果允许,则获取它
    protected final boolean tryRelease(int releases) {
        int c = getState() - releases;
        if (Thread.currentThread() != getExclusiveOwnerThread()) // 当前线程不为独占线程
            throw new IllegalMonitorStateException(); // 抛出异常
        // 释放标识
        boolean free = false; 
        if (c == 0) {
            free = true;
            // 已经释放,清空独占
            setExclusiveOwnerThread(null); 
        }
        // 设置标识
        setState(c); 
        return free; 
    }
    
    // 判断资源是否被当前线程占有
    protected final boolean isHeldExclusively() {
        // While we must in general read state before owner,
        // we don't need to do so to check if current thread is owner
        return getExclusiveOwnerThread() == Thread.currentThread();
    }

    // 新生一个条件
    final ConditionObject newCondition() {
        return new ConditionObject();
    }

    // Methods relayed from outer class
    // 返回资源的占用线程
    final Thread getOwner() {        
        return getState() == 0 ? null : getExclusiveOwnerThread();
    }
    // 返回状态
    final int getHoldCount() {            
        return isHeldExclusively() ? getState() : 0;
    }

    // 资源是否被占用
    final boolean isLocked() {        
        return getState() != 0;
    }

    /**
     * Reconstitutes the instance from a stream (that is, deserializes it).
     */
    // 自定义反序列化逻辑
    private void readObject(java.io.ObjectInputStream s)
        throws java.io.IOException, ClassNotFoundException {
        s.defaultReadObject();
        setState(0); // reset to unlocked state
    }
}
Last modification:December 2nd, 2019 at 07:22 pm