Java锁迭代之Lock锁

Lock锁是一个接口,其实现类有如下

 Lock接口的模板方法有如下

 不难看出,Lock锁的特质,它在加锁时,并不是像synchronize这把同步锁,它是先尝试加锁,加锁也可以设置加锁时间,并且在阻塞时,可以直接释放锁,而且提供了lockInterruptibly(),支持响应中断

简单介绍下lock锁和tryLock的区别

/**
     * Acquires the lock.
     *
     * <p>If the lock is not available then the current thread becomes
     * disabled for thread scheduling purposes and lies dormant until the
     * lock has been acquired.
     *
     * <p><b>Implementation Considerations</b>
     *
     * <p>A {@code Lock} implementation may be able to detect erroneous use
     * of the lock, such as an invocation that would cause deadlock, and
     * may throw an (unchecked) exception in such circumstances.  The
     * circumstances and the exception type must be documented by that
     * {@code Lock} implementation.
     */
void lock();

/**
     * Acquires the lock only if it is free at the time of invocation.
     *
     * <p>Acquires the lock if it is available and returns immediately
     * with the value {@code true}.
     * If the lock is not available then this method will return
     * immediately with the value {@code false}.
     *
     * <p>A typical usage idiom for this method would be:
     *  <pre> {@code
     * Lock lock = ...;
     * if (lock.tryLock()) {
     *   try {
     *     // manipulate protected state
     *   } finally {
     *     lock.unlock();
     *   }
     * } else {
     *   // perform alternative actions
     * }}</pre>
     *
     * This usage ensures that the lock is unlocked if it was acquired, and
     * doesn't try to unlock if the lock was not acquired.
     *
     * @return {@code true} if the lock was acquired and
     *         {@code false} otherwise
     */
boolean tryLock();

这两种加锁,源码是这样说的

lock.lock(),获取锁时,如果已经锁已经被其他线程进行获取,那么就会等待,在加锁成功后,如果发生异常,是不会主动释放锁,需要我们自己释放

lock.tryLock(),可以看到是有返回值的,如果加锁成功,就会返回true,如果加锁失败,就会直接返回false,不会一直等待,但也需要我们手动释放锁

大家看到,诶,怎么还有一个Condition这样一个方法,我们又想condition又是什么?

我们点开condition这个接口有大概这些方法和实现类

 

其实condition提供给我们最基本的 实现等待/通知机制的 API,

//就是让当前获得锁的线程进入waiting状态,同时释放锁,类似于wait()

void await() throws InterruptedException;

// 通知某个处于waiting状态的线程,继续获得锁执行,类似于notify()
void signal();

//通知所有处于waiting状态的线程,开始争抢锁,谁获得锁,继续执行
void signalAll();

实现Lock锁的锁共有两个

第一个ReentrantReadWriteLock ,也就是读写锁,读读之间是不会阻塞,但在写时都阻塞

public class ReentrantReadWriteLock
        implements ReadWriteLock, java.io.Serializable {
    private static final long serialVersionUID = -6992448646407690164L;
    /** Inner class providing readlock */
    private final ReentrantReadWriteLock.ReadLock readerLock;
    /** Inner class providing writelock */
    private final ReentrantReadWriteLock.WriteLock writerLock;
    /** Performs all synchronization mechanics */
    final Sync sync;

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * default (nonfair) ordering properties.
     */
    public ReentrantReadWriteLock() {
        this(false);
    }

    /**
     * Creates a new {@code ReentrantReadWriteLock} with
     * the given fairness policy.
     *
     * @param fair {@code true} if this lock should use a fair ordering policy
     */
    public ReentrantReadWriteLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
        readerLock = new ReadLock(this);
        writerLock = new WriteLock(this);
    }

    public ReentrantReadWriteLock.WriteLock writeLock() { return writerLock; }
    public ReentrantReadWriteLock.ReadLock  readLock()  { return readerLock; }
}

写锁的获得过程

首先判断state状态位是否为0,为0代表没有线程获取锁,直接进行判断是否是公平锁,如果不是,直接获得锁,并且cas修改state的低16位为1,改为写锁状态,获取到写锁

如果state为1,表示有线程获得到锁,判断state的低16位为0,如果是,则代表此时有线程获得到读锁,那么将会判断是否公平锁,如果不是,那么将cas获取写锁,如果state的低16位不为0,则代表有线程获取到写锁,进而判断当前获得锁的线程是否为自己,如果是的话,会判断当前线程获得写锁是否超过最大次数,如果超过则抛出异常,如果不是则会判断是否是公平锁,不是的话,就会cas获取写锁

 第二个锁ReentrantLock可重入锁

使用Lock()方法获得重入锁

首先在声明锁时,如果声明锁为true,则为公平锁,如果为false就是非公平锁

先说公平锁

之后在调用锁的时候,判断state状态位是否为0,为0则代表没有其他线程获得锁,

首先会判断自己是否需要排队,如果不需要排队,才会cas修改锁,修改成功就会将AQS的state状态位修改,获取锁成功

如果state的状态位为1,代表有其他线程获得了锁,之后会判断当前锁的线程是否为当前线程,如果是的话,就会判断当前获得锁的最大获得次数,超过,抛出异常,如果没超过,就会将没获得锁的线程加入到AQS对列中去,判断当前是否为首节点,且cas获取锁的状态,不是的话,就会休眠当前线程,是的话就会直接重入,加锁成功

非公平锁

和公平锁的唯一不同,在判断state为0,是空闲状态时,不会判断自己是否需要排队,直接加锁,后续便和公平锁一样

使用tryLock()方法加锁

使用tryLock()无论是否公平都会调用nonfairTryAcquire()方法,只会判断当前线程是否可以加锁,可以加锁,便直接调用cas的compareAndSetState修改,加锁,不会判断其他

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

下图给出一个非公平锁使用lock()的加锁流程 ,帮助大家理解

最后说下,tryLock()和Lock()方法的好坏吧

tryLock方法只会判断是否可以加锁,加锁失败不会等待相比较与lock方法,不需要消耗CPU更多的性能,但仍然是需要考虑实际代码业务,针对需要,仁者见仁,智者见智吧!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

羊驼有点累

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值