栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JAVA并发编程实战-显式锁

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JAVA并发编程实战-显式锁

目录
  • 思维导图
  • 1 Lock和ReentrantLock
    • 1.1 可轮询的和可定时的锁
    • 1.2 可中断的锁获取操作
  • 2 对性能的考量
  • 3 公平性
  • 4 在内部锁synchronized和ReentrantLock直接进行选择
  • 5 读写锁
  • 总结
  • 参考文献

思维导图

1 Lock和ReentrantLock

Lock定义了抽象的锁操作,如下图

Lock提供了无条件的、可中断的、可轮询的和定时的获取锁操作。
Lock的实现与内部锁具有相同的内存可见性语义。

ReentrantLock实现了Lock接口,提供了与synchronized相同的互斥和内存可见性保证。
内部锁存在的条件下,创建这种Lock机制原因,主要是内部锁具有的一些局限:

  • 不能中断正在等待获取锁的线程,并且请求失败必须无限等待。
  • 不具备高级特性,比如定时、可轮询等。

下列demo-1是Lock接口的规范形式:

    private final Lock lock = new ReentrantLock();
//lock接口规范形式
    public void addPlace(String place) {
        lock.lock();
        try {
            //应用操作
            //捕获异常
        } catch (Exception e) {
            //处理异常
        } finally {
            //finally释放锁
            lock.unlock();
        }
    }
}

Lock锁必须在finally块中显示释放。

1.1 可轮询的和可定时的锁

Lock定义的tryLock的两种形式可以实现可轮询和可定时的锁操作。这些操作可以避免死锁的发生。

  • tryLock():立即返回,true表示获取成功,false表示锁不可用。
  • tryLock(long time, TimeUnit unit):如当前线程没有被中断则获取锁,成功则返回true,否则当前线程将被休眠,直到发生下列事件:
  •  1. 锁被当前线程获取。
  •  2. 该线程被其它线程中断。
  •  3. 指定的等待时间已过。

如demo-2演示了如何通过可轮询的方式,在指定时间进行一个操作:

 public boolean transferMoney(Account from, Account to, BigDecimal amount, long timeout, TimeUnit timeUnit) throws InterruptedException {
        long stopTime = System.nanoTime() + timeUnit.toNanos(timeout);
        while (true) {
            if (from.lock.tryLock()) {
                try {
                    if (to.lock.tryLock()) {
                        try {
                            from.setBalance(from.getBalance().subtract(amount));
                            to.setBalance(to.getBalance().add(amount));
                            return true;
                        } finally {
                            to.lock.unlock();
                        }
                    }
                } finally {
                    from.lock.unlock();
                }
            }
            if (System.nanoTime() > stopTime) {
                return false;
            }
            Thread.sleep(100);
        }
    }

上述代码通过传入一个等待时间,用于限定轮询获取锁的超时时间,如果超过这个时间还未获取两个锁,则代码直接失败返回false。

1.2 可中断的锁获取操作

可中断的锁可以在取消活动中使用。
一个可中断的规范形式如demo-3:

public void sendMessage(String message) throws InterruptedException {
        lock.lockInterruptibly();
        try {
            doSendMessage(message);
        } finally {
            lock.unlock();
        }
    }

当等待获取锁过程,被中断,方法将抛出中断异常,交给调用方处理。

2 对性能的考量

ReentrantLock在Java5.0提供的竞争性要优于内部锁,但是Java6.0对内部锁进行了改善,使得两者性能差距非常小了。

性能是一个不断变化的目标。

3 公平性

ReentrantLock提供了两种公平性选择。默认是非公平性锁:

两者区别在于非公平锁允许在首次获取时进行抢占,失败后仍然进行排队:

4 在内部锁synchronized和ReentrantLock直接进行选择

如果内部锁满足需求,不使用ReentrantLock,只有当你需要如下高级性能时使用:

  • 可定时、可轮询和可中断的操作。
  • 公平队列(内部锁非公平),或者非块结构的锁。

未来的性能改进倾向于synchronized,因为它是基于JVM的,能够进行优化。

5 读写锁

读写锁:一个资源可以被多个读者访问,但是只能被一个写者访问。

ReadWriteLock接口定义如下:

public interface ReadWriteLock {
    
    Lock readLock();

    
    Lock writeLock();
}

只暴露了读锁和写锁。

如下等待demo-4是使用读写锁封装Map的示例:

public class ReadWriteMap {
    private final Map map;
    private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    private final Lock w = readWriteLock.writeLock();
    private final Lock r = readWriteLock.readLock();

    public ReadWriteMap(Map map) {
        this.map = map;
    }

    public V put(K k, V v) {
        //写锁控制写入-排它
        w.lock();
        try {
            return map.put(k, v);
        } finally {
            w.unlock();
        }
    }

    public V get(K k) {
        //读锁控制读取-共享
        r.lock();
        try {
            return map.get(k);
        } finally {
            r.unlock();
        }
    }
}

读写操作前获取对应读写锁即可。

总结

显式锁提供了内部锁的不具备的一些高级特性,但是不能完全替代内部锁。只有当你需要内部锁没有提供的特性时才使用显式锁。

参考文献

[1]. 《JAVA并发编程实战》.

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/872323.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号