[Java 并发编程] 18. Lock


前言

Lock 是除synchronized代码块的另外一种线程同步机制,Lock可以具有比synchronized代码块更复杂的作用。Lock可以使用synchronized关键字,所以它并不能让我们摆脱synchronized关键字。

自JDK 5开始,java.util.concurrent.locks包提供了一些Lock的实现类,比如ReentrantLock、ReadWriteLock、Condition,因此你可能不需要自己自定义Lock,但是你可能需要知道如何使用它们并了解其中的原理。这里我们不详细讲解,更多的细节,后面我将总结JUC工具包的相关知识点。


一、简单的Lock

先看看synchronized的同步:

public class Counter {

    private int count = 0;
    
    public int inc() {
        synchronized(this) {
            return ++count;
        }
    }
}

注意inc()方法里面的synchronized(this)代码块,这个代码块确保了同一时间只有一个线程能够执行 ++count 指令。下面我们看看使用自定义的Lock代替上面这段代码。

public class Counter{
    
    private Lock lock  = new Lock();
    private int count = 0;

    public int inc() throws InterruptedException {
        lock.lock();
        int newCount = ++count;
        lock.unLock();
        return newCount;
    }
}

Lock的实现如下:

public class Lock {

    private boolean hasLock = false;

    private synchronized void lock() throws InterruptedException {
        while (hasLock) {
            wait();
        }
        hasLock = true;
    }

    private synchronized void unLock() {
        hasLock = false;
        notify();
    }
}

请注意lock()方法中的while (hasLock)循环,我们称它为“自旋锁”,自旋锁解决了线程通信的虚假唤醒的问题。


二、锁重入

当某个线程进入同步代码块时,首先会尝试获取同步代码块的对象锁,一个线程可以拥有多个对象锁,由于不同同步代码块可能具有相同的对象锁,当线程拥有某个对象锁时,进入这个对象锁的同步代码块不需要再次获取锁,我们称它为可重入锁。

synchronized 具有重入锁的机制。示例:

public class Reentrant {
    
    public synchronized void methodA() {
        methodB();
    }

    public synchronized void methodB() {
        
    }

}

请注意普通方法A和普通方法B都使用了synchronized关键字声明,它的监视器对象是Reentrant的一个实例对象,methodA()中调用了methodB(),当某个线程进入进入methodA(),获取到对应的锁,进入methodB()不需要再次获取锁,可以直接进入methodB()。

JUC中还提供了一些重入锁,比如ReentrantLock、ReentrantReadWriteLock等等,这里我们不细讲,后面请看JUC博客专题。

现在我们自定义一个可重入锁。如下所示:

/**
 * 自定义可重入锁
 *
 * @author : sungm
 * @date : 2020-09-03 10:48
 */
public class CustomReentrantLock {

    private boolean hasLocked = false;
    private Thread lockedBy = null;

    public synchronized void lock() throws InterruptedException {
        while (hasLocked && lockedBy != Thread.currentThread()) {
            wait();
        }

        hasLocked = true;
        lockedBy = Thread.currentThread();
    }

    public synchronized void unLock() {
        hasLocked = false;
        lockedBy = null;
    }
}

如上所示,while 循环中不仅要判断hasLocked是否被锁住还要判断当前线程是否是锁被拥有的线程,这就是可重入的锁。


三、公平锁

在上一章 《饥饿与公平》 中我们讲了不公平锁和公平锁,synchronized是一种不公平的锁。


四、在finally代码块中调用unLock()方法

当关键代码(介于lock和unLuck之间的代码)可能抛出异常,请在finally代码块中调用unLock()方法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值