likes
comments
collection
share

懒虫学习:ReentrantLock浅析

作者站长头像
站长
· 阅读数 15

ReentrantLock

扩展对象 实现Lock, Serializable 具有与使用同步方法和语句访问的隐式监视锁相同的基本行为和语义的可重入互斥锁,但具有扩展的功能。 ReentrantLock属于上一次成功锁定但尚未解锁的线程。当锁不属于其他线程时,调用锁的线程将返回并成功获取锁。如果当前线程已经拥有该锁,该方法将立即返回。这可以使用isHeldByCurrentThread()和getHoldCount()方法进行检查。

该类的构造函数接受一个可选的公平性参数。当设置为true时,在争用情况下,锁倾向于授予对等待时间最长的线程的访问权。否则,此锁不能保证任何特定的访问顺序。使用由多个线程访问的公平锁的程序可能会显示较低的总体吞吐量(即速度较慢;(通常比使用默认设置的慢得多),但是在获得锁和保证没有饥饿的时间上的差异较小。但是请注意,锁的公平性并不能保证线程调度的公平性。因此,使用公平锁的多个线程中的一个可以连续多次获得公平锁,而其他活动线程没有进展,也没有当前持有该锁。还要注意,不定时的tryLock()方法不遵守公平性设置。如果锁可用,即使其他线程正在等待,它也会成功。

建议在调用lock之后立即使用try块,最典型的是在before/after构造中,例如:以上翻译结果来自有道神经网络翻译(YNMT)· 通用场景

class X {
   private final ReentrantLock lock = new ReentrantLock();
   // ...

   public void m() {
     lock.lock();  // block until condition holds
     try {
       // ... method body
     } finally {
       lock.unlock()
     }
   }
 }

除了实现Lock接口之外,该类还定义了许多公共和受保护的方法,用于检查锁的状态。其中一些方法仅对仪器和监视有用。

该类的序列化与内置锁的行为方式相同:反序列化的锁处于解锁状态,无论其序列化时的状态如何。

该锁最多支持同一个线程的2147483647个递归锁。尝试超过此限制将导致锁定方法抛出错误。

tryLock()

仅在调用时未被其他线程持有时获取锁。 如果锁未被其他线程持有,则获取该锁,并立即返回值true,将锁持有计数设置为1。即使将此锁设置为使用公平的排序策略,如果锁可用,调用tryLock()将立即获取该锁,无论当前是否有其他线程正在等待该锁。这种“闯入”行为在某些情况下是有用的,即使它破坏了公平。如果您希望遵守此锁的公平性设置,那么使用tryLock(0, TimeUnit.SECONDS),这几乎是等效的(它也检测中断)。

如果当前线程已经持有此锁,则持有计数增加1,该方法返回true。

如果锁由另一个线程持有,则此方法将立即返回值false。

规定: tryLock在接口锁定 返回: 如果锁是空闲的并且被当前线程获取,或者锁已经被当前线程持有,则为True;否则为假

Condition()

返回用于此锁实例的条件实例。 当与内置监控器锁一起使用时,返回的Condition实例支持与Object监控器方法(wait、notify和notifyAll)相同的用法。

如果在调用任何条件等待或信令方法时未持有此锁,则抛出IllegalMonitorStateException。 当调用条件等待方法时,锁被释放,在它们返回之前,锁被重新获取,锁持有计数恢复到调用方法时的状态。 如果线程在等待时被中断,那么等待将终止,InterruptedException将被抛出,线程的中断状态将被清除。 等待线程按FIFO顺序发出信号。 从等待方法返回的线程的锁重获取顺序与最初获取锁的线程相同,默认情况下没有指定,但对于公平锁,优先考虑等待时间最长的线程。 规定: 在接口锁定中使用newCondition 返回: 条件对象

acquire(int arg)

以独占模式获取,忽略中断。实现通过调用至少一次{@link #tryAcquire}成功归来。否则,线程可能会排队反复阻塞和解除阻塞,调用ryAcquire直到成功。

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

tryAcquire(int arg)

尝试以独占模式获取。该方法应该查询对象的状态是否允许以独占模式获取它,如果允许则获取它。

此方法总是由执行acquire的线程调用。如果此方法报告失败,则acquire方法可能会将线程排队(如果它尚未排队),直到其他线程释放它为止。这可以用来实现方法{@link Lock#tryLock()}。

默认抛出 UnsupportedOperationException。

数获取参数。这个值总是1传递给获取方法,或者是在进入时保存的值到一个条件等待。否则将不解释该值可以表示任何你喜欢的东西。 如果成功,@返回{@code true}。一旦成功,这个对象就有已被收购。返回成功表明锁已经占有。

抛出IllegalMonitorStateException如果获取将放置此同步器处于非法状态。这个例外必须是以一致的方式抛出,以便同步工作

如果不支持独占模式,则抛出UnsupportedOperationException

protected boolean tryAcquire(int arg) {
        throw new UnsupportedOperationException();
}

tryAcquireShared(int arg)

在共享模式下尝试获取。该方法应该查询对象的状态是否允许以共享模式获取它,如果允许则获取它。

此方法总是由执行acquire的线程调用。如果此方法报告失败,则acquire方法可能会将线程(如果尚未排队)放入队列,直到其他线程发出释放信号为止。

protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }

release(int arg)

以独占模式释放。如果{@link #tryRelease}返回true,通过解除阻塞一个或多个线程实现。这个方法可以用来实现方法{@link Lock#unlock}。

public final boolean release(int arg) {
        if (tryRelease(arg)) {
            Node h = head;
            if (h != null && h.waitStatus != 0)
                unparkSuccessor(h);
            return true;
        }
        return false;
    }

tryRelease(int arg)

尝试将状态设置为在独占模式下反映释放。 此方法总是由执行释放的线程调用。

 protected boolean tryRelease(int arg) {
        throw new UnsupportedOperationException();
    }

tryReleaseShared(int arg)

尝试将状态设置为反映共享模式下的释放。 此方法总是由执行释放的线程调用。 默认实现抛出{@link UnsupportedOperationException}。

protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }

代码示例:

package com.juc.reentrantlock;
import java.util.concurrent.locks.ReentrantLock;
public class MService implements Runnable {
	static ReentrantLock lock = new ReentrantLock(true);//公平//不填是非公平的
	private static volatile int num = 10;

	public  void m1() {
			num--;
	}
	@Override
	public void run() {
		try {
			lock.lock();
			m1();
			System.out.println(Thread.currentThread().getName() + " num " + num);
		} finally {
			lock.unlock();
		}
	}
}

源码浅析:

1.ReentrantLock默认创建的就是非公平锁。那么看看NonfairSync是怎么实现的

 public ReentrantLock() {
        sync = new NonfairSync();
   }

2.NonfairSync代码

NonfairSync 使用静态的final 内部类继承的

Sync是ReentrantLock类内部的抽象类 继承了AbstractQueuedSynchronizer,这个类学会了在关注

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))
                setExclusiveOwnerThread(Thread.currentThread());
            else
                acquire(1);
        }

        protected final boolean tryAcquire(int acquires) {
            return nonfairTryAcquire(acquires);
        }
    }

compareAndSetState这个是CAS操作能保证在无锁情况下更新状态,实现了共享数据的安全

setExclusiveOwnerThread这个是将这个锁设置为当前线程持有

private transient Thread exclusiveOwnerThread;
protected final void setExclusiveOwnerThread(Thread thread) {
        exclusiveOwnerThread = thread;
 }

acquire(1)这个方法是获取一个锁

nonfairTryAcquire这个是非公平锁的实现

final boolean nonfairTryAcquire(int acquires) {
            final Thread current = Thread.currentThread();
            int c = getState();
            if (c == 0) {
                if (compareAndSetState(0, acquires)) {
                    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;
        }

公平锁的实现

static final class FairSync extends Sync {
        private static final long serialVersionUID = -3000897897090466540L;

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

        /**
         * Fair version of tryAcquire.  Don't grant access unless
         * recursive call or no waiters or is first.
         */
        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;
        }
    }

公平和非公平锁的实现上 多了一个!hasQueuedPredecessors() &&的判断

总结:

1.ReentrantLock是支持可重入的

2.支持公平和非公平锁

3.基于AQS,和CAS实现了公平锁 和非公平锁的获取和释放

4.acquire类是请求获取锁 release是释放锁

5.acquire对AQS中的state进行增加操作

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

6.release对AQS中的state进行减少操作

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