Synchronized和ReentrantLock

ReentrantLock常常对比着synchronized来分析,我们先对比着来看然后再一点一点分析。
(1)synchronized是独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。ReentrantLock也是独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。
(2)synchronized可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;ReentrantLock也可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。
(3)synchronized不可响应中断,一个线程获取不到锁就一直等着;ReentrantLock可以相应中断。
ReentrantLock好像比synchronized关键字没好太多,我们再去看看synchronized所没有的,一个最主要的就是ReentrantLock还可以实现公平锁机制。什么叫公平锁呢?也就是在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。

加锁的方式

ReentrantLock加锁是修改锁的状态值,state属性,当加锁时,加一即可,因为可重入,当当前线程第二次加锁时,就会直接加一。

final void lock() {
        if (compareAndSetState(0, 1))//通过cas修改state的值
            setExclusiveOwnerThread(Thread.currentThread());
        else
            acquire(1);
    }

Synchronized加锁是每个对象有一个监视器锁(monitor)。当monitor被占用时就会处于锁定状态,线程执行monitorenter指令时尝试获取monitor的所有权,过程如下:
1、如果monitor的进入数为0,则该线程进入monitor,然后将进入数设置为1,该线程即为monitor的所有者。
2、如果线程已经占有该monitor,只是重新进入,则进入monitor的进入数加1.
3.如果其他线程已经占用了monitor,则该线程进入阻塞状态,直到monitor的进入数为0,再重新尝试获取monitor的所有权。

synchron就是抢占monitor,抢到就是抢到了锁。进入数加一,锁重入加一就行,释放一次减一,等进入数为0的时候锁就为无人占有。
当有线程再次申请这个锁的时候,不会直接去申请,而是会查看ACC_SYNCHRONIZED访问标识,查看是否被设置。而这个标记是存在对象的对象头中的Mark Word中,下面介绍一下Mark Word。

java对象的请求头:64位计算机 为8*12=96bit 前64位为Mark Word存储hashcode, GC分代信息,和锁的状态信息。
当对象刚创建时,前56位存储hashcode,后面4个存GC分带信息,三位存锁的状态。
无锁:001.偏向锁:101 偏向锁记录线程ID
当锁膨胀为轻量锁、重量锁时,前62位都为线程号后两位为状态号
00:轻量级 10重量级 11表示GC

ReentrantLock详解

构造方法
一个无参构造,默认为非false,非公平锁。
当传入new ReentrantLock(true)时会创建一个公平锁。

/**
 * Creates an instance of {@code ReentrantLock}.
 * This is equivalent to using {@code ReentrantLock(false)}.
 */
public ReentrantLock() {
    sync = new NonfairSync();
}

/**
 * Creates an instance of {@code ReentrantLock} with the
 * given fairness policy.
 *
 * @param fair {@code true} if this lock should use a fair ordering policy
 */
public ReentrantLock(boolean fair) {
    sync = fair ? new FairSync() : new NonfairSync();
    }

在创建ReentrantLock时,可以传入true或false来确定是要创建公平锁还是非公平锁,默认是非公平锁(false)。

第一次加锁就是上面写的那样,当有第二个线程申请锁时,申请失败会加入到一个阻塞队列中。

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

/*通过tryAcquire尝试获取独占锁,如果成功返回true,失败返回false
如果tryAcquire失败,则会通过addWaiter方法将当前线程封装成Node添加到AQS队列尾部
acquireQueued,将Node作为参数,通过自旋去尝试获取锁*/


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

解锁,调aqs的release方法

public void unlock() {
    sync.release(1);
}


//重写的AQS的方法
public final boolean release(int arg) {
    if (tryRelease(arg)) {
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h);
        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;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值