显式锁

Lock和ReentrantLock:

与内置加锁机制(synchronized)不同的是,Lock提供到了一种无条件的、可轮询的、定时的以及课中断的锁获取操作,所有加锁和解锁的方式都是显式的。Lock接口方法声明如下:

public interface Lock{
    void lock();
    void lockInterruptibly() throws InterruptedException;
    boolean tryLock();
    boolean tryLock(long timeout, TimeUnit unit) throws InterruptedException;
    void unlock();
    Condition newCondition();
}

ReentrantLock实现了Lock接口,并提供了与synchronized相同的互斥性和内存可见性。在获取ReentrantLock时有着与进入同步代码块相同的内存语义;在释放ReentrantLock时,有着与退出同步代码块相同的内存语义。

使用ReentrantLock必须在finally块中释放锁。ReentrantLock使用方法如下:

Lock lock = new ReentrantLock();
...
lock.lock();
try{
    ...
}finally{
    lock.unlock();
}

轮询锁、定时锁和中断锁

定时锁和轮询锁的获取方式是由tryLock()方法实现的,与无条件的锁的获取方式相比,它具有更完善的错误恢复机制。

在内置锁中,死锁是一个严重的问题,恢复程序的唯一方法是重新启动程序,而防止死锁的唯一方法就是构造程序时避免出现不一致的锁顺序。可定时和可轮询的锁提供了另一种选择:避免死锁的发生。

如果不能获取所有需要的锁,那么可以使用定时锁或轮询锁,它会释放已经获取的锁,并重新尝试获取所有锁。

轮询锁:

public boolean Polling(Account fromAcct, Account toAcct){
    while(true){
        if(fromAcct.lock.tryLock()){
            try{
                if(toAcct.lock.tryLock()){
                    try{
                        //业务逻辑
                    }finally{
                         toAcct.lock.unlock();
                    }
                }
            }finally{
                 fromAcct.lock.unlock();
            }
        }
    }
    return false;
}

定时锁:

public boolean trySendOnSharedLine(String message, long timeout, TimeUnit unit) throws InterruptedException{
    long nanosToLock = unit.toNanos(timeout) - estimatedNanosToSend(message);
    if(!lock.tryLock(nanosToLock, NANOSECONDS))
        return false;
    try{
        return sendOnShareLine(message);
    }finally{
        lock.unlock();
    }
}

中断锁:

public boolean SendOnSharedLine(String message) throws InterruptedException{
    lock.lockInterruptibly();
    try{
        return cancellableSendOnShareLine(message);
    }finally{
        lock.unlock();
    }
}

公平性

ReentrantLock的构造函数提供了两种公平性选择:创建一个非公平锁(默认)和创建一个公平锁。公平锁按照线程发出请求的顺序获得锁;非公平锁允许“插队”:如果一个线程发出请求的同时正好该锁的状态变为可用,则该线程直接获得锁而不需要排队。

在大多数情况下,非公平锁的性能大于公平锁。一个原因是:在恢复一个被挂起的线程和该线程真正执行之间存在很大的延迟。

当持有锁的时间相对较长,或者请求锁的平均时间间隔较长,那么应该使用公平锁。默认的内置锁(synchronized)并不提供公平性保证。

内置锁和显式锁:

当需要一些高级功能,内置锁无法实现时,才应该使用ReentrantLock, 这些功能包括:可定时的、可轮询的与可中断的锁获取操作,公平队列,以及非块结构的锁。否则就应该使用内置锁(synchronized)。

ReentrantLock的非块结构特性意味着获取锁的操作不能和特定的栈帧关联起来,而内置锁可以。

读写锁:

ReentrantLock是一种强硬的互斥加锁规则:每次只允许一个线程获得锁。这种规则虽然避免的“读-写”冲突和“写-写”冲突,但也避免的“读-读”冲突。但实际上数据结构上的操作大多是读操作,如何可以放宽加锁规则,允许多个执行读操作的线程同时访问数据结构,则可以使用“读写锁”:一个资源可以同时被多个读操作访问,或则被一个写操作访问,但两者不可同时进行。

ReadWriteLock和ReenReadWriteLock:

ReadWriteLock接口:

public interface ReadWriteLock{
    Lock readLock();
    Lock writeLock();
}

ReenReadWriteLock是ReadWriteLock的实现类。ReenReadWriteLock为两种锁都提供了可重入加锁语义。ReenReadWriteLock在构造时也可以选择是非公平锁(默认)或者是公平锁。ReenReadWriteLock的写入锁只能有一个拥有者,并且只能由获得该锁的线程来释放。

public class ReadWriteMap<K,V>{
    private final Map<K,V> map;
    private final ReadWriteLock lock = new ReenReadWriteLock();
    private final Lock r = lock.readLock();
    private final Lock r = lock.writeLock();

    public ReadWriteMap(Map<K,V> map){
        this.map = map;
    }

    public V put(K key,V value){
        w.lock();
        try{
            return map.put(key,value);
        }finally{
            w.unlock();
        }
    }

    public V get(Object key){
        r.lock();
        try{
            return map.get(key);
        }finally{
            r.unlock();
        }
    }  
}  

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Java虚拟机--类加载的时机

    SuperHeroes
  • 查找----基于无序链表

    SuperHeroes
  • 加权有向图----无环情况下的最短路径算法

    SuperHeroes
  • 干货 | 天天拍车运维总监李强:天天拍车运维架构演进及实践

    嘉宾演讲视频 Guest Video ? 温馨提示 本视频时长1小时08分01秒,建议在wifi下观看 4月8日,DBAplus社群在上海举办了本年度第二场线下...

    IT大咖说
  • 在达沃斯,跑得比谁都快的DT君

    前两天, 2017夏季达沃斯世界经济论坛在大连开幕。围绕着“第四次工业革命”的主题,大会讨论了人工智能、共享经济、智慧城市等以大数据为核心众多DT君关心的话题。

    DT数据侠
  • 三场沙龙直播,解析“新基建”如何助力实体经济

    欢迎关注公众帐号“鹅厂网事”,我们给你提供最新的行业动态信息、腾讯网络最接地气的干货分享。 注1:凡注明来自“鹅厂网事”的文字和图片等作品,版权均属于“深圳市...

    鹅厂网事
  • ZooKeeper实现读写锁

    在上一篇文章,我们已经实现了分布式锁。今天更进一步,在分布式锁的基础之上,实现读写锁。

    程序猿讲故事
  • 各类工具网站搜索素材微信公众号排版在线设计工具

    自从看到同学的微信公众号以后,我就疯狂的迷恋上了微信公众号的排版,我觉得这不仅仅是展示自我的一个途径,更多的是,我可以任意排版,我喜欢排版,就像我喜欢养植物一样...

    贺贺V5
  • 记录一次接口评测的优化

    我们在测试过程中往往使用不同的方式评估产品的质量,这些方法种类繁多,从简单的缺陷计数到严格的统计建模不一而足。当我们的功能涉及到过量或者无法穷尽的数据时,我们需...

    用户5521279
  • 谈避免ERP过度二次开发的策略

    作为一个ERP项目,合适的开发工作量是很重要的。如果开发工作量过大,再加上没有相应的奖惩措施,那么就会无法调动开发团队的积极性,也就无法进行持续有效的二次开发,...

    SAP斯凯普斯 Qasim

扫码关注云+社区

领取腾讯云代金券