【JUC基础】04. Lock锁

这篇具有很好参考价值的文章主要介绍了【JUC基础】04. Lock锁。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1、前言

java.util.concurrent.locks为锁定和等待条件提供一个框架的接口和类,说白了就是锁所在的包。

【JUC基础】04. Lock锁

2、什么是Lock

Lock是一种锁机制,比同步块(synchronized block)更加灵活,同时也更加复杂的线程同步机制。在JDK1.5就已存在Lock接口了。

【JUC基础】04. Lock锁

其中有三个实现类:

【JUC基础】04. Lock锁

  1. ReentrantLock:可重入锁
  2. ReentrantReadWriteLock.ReadLock:读锁
  3. ReentrantReadWriteLock.WriteLock:写锁

3、Lock的API

Lock接口只提供了6个方法:

【JUC基础】04. Lock锁

其中lock()和unlock()是配对的。lock()是加锁,而unlock()是解锁。lockInterruptibly()与lock()类似,区别在于lock()如果无法获取到锁,线程一直被阻塞,直到锁释放。而lockInterruptibly()允许线程被中断,并抛出java.lang.InterruptedException。

tryLock()只有在调用时才可以获得锁。如果可用,则获取锁定,并返回true,反之返回false。相应的还有重载方法tryLock(long time, TimeUnit unit)表示在给定的等待时间内空闲,则可以获取锁。如果到了超时时间,还没获取到就放弃获取。

4、ReentrantLock的基本使用

Lock是一个接口,官方文档其实也给了如何使用的说明:

  1. 声明Lock对象。Lock lock = new XXXLock();
  2. 方法执行加锁lock.lock();
  3. 在方法块执行完毕后,需要释放锁:lock.unlock();

【JUC基础】04. Lock锁 

由于LocK是一个接口,需要使用具体的实现。典型的实现如ReentrantLock。示例代码如下:

public class ReentrantLockDemo {

    public static void main(String[] args) {
        Phones phones = new Phones();

        new Thread(() -> {
            for(int i = 0; i< 50; i++) {
                phones.sale();
            }
        }, "销售员小王").start();

        new Thread(() -> {
            for(int i = 0; i< 50; i++) {
                phones.sale();
            }
        }, "销售员小红").start();
    }

}

class Phones {
    // 库存10部手机
    private int total = 50;

    // 1、 声明锁的实例
    ReentrantLock lock = new ReentrantLock();

    public void sale(){
        try {
            // 2、加锁,代码规约检测会提示你加载try的第一行
            lock.lock();
            if(total > 0){
                System.out.println(Thread.currentThread().getName() + "卖出了一部手机,当前库存剩余:" + (--total));
            }
        } finally {
            // 3、释放锁
            lock.unlock();
        }
    }
}

执行结果:

【JUC基础】04. Lock锁

ReentrantLock是个可重入锁,其中可以执行公平锁和非公平锁。如new ReentrantLock(true),默认是非公平锁(false)。

4.1、公平锁和非公平锁

  • 公平锁:每个线程获取锁的顺序按照先后顺序获取。关键字眼:先到先得。
    • 优点:能保证所有的线程都得到资源,不会产生线程饥饿现象。
    • 缺点:吞吐量低,除了第一个线程以外,其余的都处于排队阻塞的状态,cpu需要每次唤醒线程,开销较大。
  • 非公平锁:多个线程同时尝试获取,哪个线程优先获取到锁取决于系统分配策略。关键字眼:无需排队。
    • 优点:吞吐量高,cpu无需唤醒所有的线程,开销低。
    • 缺点:会产生线程饥饿现象,可能后到的线程先获取到锁,而前面的线程永远都获取不到

5、读写锁ReadWriteLock

ReadWriteLock是一个读写锁接口。什么是读写锁呢?看下官方文档说明:

【JUC基础】04. Lock锁 

ReadWriteLock分为一个读锁和一个写锁。读锁可以被多个线程持有,而写锁只能被一个线程持有。典型的实现类有ReentrantReadWriteLock。

读锁:就是我们常说的共享锁。

写锁:就是常说的独占锁。

示例代码:

public class ReadWriteLock {

    public static void main(String[] args) {
        MyMap map = new MyMap();

        // 写操作
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> map.put(String.valueOf(finalI), String.valueOf(finalI))).start();
        }

        // 读操作
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> map.get(String.valueOf(finalI))).start();
        }
    }

}

// 模拟公共资源类
class MyMap extends HashMap<String, String> {

    @Override
    public String get(Object key) {
        System.out.println(Thread.currentThread().getName() + "获取key:" + key);
        return super.get(key);
    }

    @Override
    public String put(String key, String value) {
        System.out.println(Thread.currentThread().getName() + "写入key:" + key);
        String put = super.put(key, value);
        System.out.println(Thread.currentThread().getName() + "写入key:" + key + "完成");
        return put;
    }
}

如果不加入任何的锁限制,我们直到结果肯定是很随机的。在写入操作时会被其他读线程插队。

【JUC基础】04. Lock锁

加入读写锁后:

public class ReadWriteLock {

    public static void main(String[] args) {
        MyMap map = new MyMap();

        // 写操作
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> map.put(String.valueOf(finalI), String.valueOf(finalI))).start();
        }

        // 读操作
        for (int i = 0; i < 5; i++) {
            int finalI = i;
            new Thread(() -> map.get(String.valueOf(finalI))).start();
        }
    }

}

// 模拟公共资源类
class MyMap extends HashMap<String, String> {

    private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock();

    @Override
    public String get(Object key) {
        try{
            rwLock.readLock().lock();

            System.out.println(Thread.currentThread().getName() + "获取key:" + key);
            return super.get(key);

        } finally {
            rwLock.readLock().unlock();
        }
    }

    @Override
    public String put(String key, String value) {
        try {
            rwLock.writeLock().lock();

            System.out.println(Thread.currentThread().getName() + "写入key:" + key);
            String put = super.put(key, value);
            System.out.println(Thread.currentThread().getName() + "写入key:" + key + "完成");
            return put;
        } finally {
            rwLock.writeLock().unlock();
        }
    }
}

【JUC基础】04. Lock锁 

我们可以看到在写入的时候,并不会有读的线程插队操作。

6、小结

关于Lock锁大概就讲这些,主要讲了ReentrantLock和ReadWriteLock的基本使用,也是通常比较常用的。其中Locks中还有一个接口Condition,这个等后面讲生产者和消费者的时候在细说。文章来源地址https://www.toymoban.com/news/detail-441082.html

到了这里,关于【JUC基础】04. Lock锁的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包