ReentrantLock和ReentrantReadWriteLock使用介绍

ReentrantLock实现自Lock,而ReentrantReadWrietLock实现自ReadWriteLock,两者之间没有关系。
ReentrantLock和ReentrantReadWriteLock都可以得到Condition(ReadLock除外)

ReentrantLock reentrantLock = new ReentrantLock();
Condition lockCondition = reentrantLock.newCondition();

ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
Condition readCondition = readWriteLock.readLock().newCondition();
/** writeCondition同理 */

ReentrantLock和ReentrantReadWriteLock在使用的时候都要注意手动释放锁。
而不像synchronized在执行完代码后自动释放内置锁。

ReentrantLock锁的使用就调用lock()和unlock()。
这里重点讲ReentrantReadWriteLock中读写锁的使用。
一句话:写锁被获取后依旧可以获取读锁,读锁被获取后不能再获取写锁,写锁之间互斥,读锁之间不互斥

读锁存在的意义:当读锁被获取时,即保证了写锁不会被获取到,因此保证了共享资源不会出现脏读。

ReentrantLock一些使用介绍:
防止重复执行:

  • 在锁已经被获取的情况下,为了不让重复代码重复执行,可以使用tryLock()来查看锁是否已经被获取,
    如果已经被获取,那么直接跳过代码不再执行。 为了防止由于资源处理不当长时间占用等情况,建议使用tryLock(long
    timeout,TimeUnit unit)方法,超时则立即返回。

可中断锁:

  • synchronized与Lock在默认情况下是不会响应中断(interrupt)操作,会继续执行完。ReentrantLock提供了lockInterruptibly()可中断锁来解决此问题,即在锁获取过程中不处理中断状态,而是直接抛出中断异常,由上层调用者处理中断。这种情况主要用于取消某些操作对资源的占用。

ReentrantReadWriteLock特性介绍:

  • 公平性:默认不公平。
    synchronized是公平锁,而ReentrantLock和ReentrantReadWriteLock默认是不公平锁(可以自己设置)。
    公平情况下,操作会排一个队优先为等待时间最长的那个写操作分配写入锁,来保证执行顺序,这样会消耗更多的时间来排队。(而ReadLock之间不互斥,所以没有公平性和不公平性。)
    不公平情况下,是无序状态允许插队,jvm会自动计算如何处理更快速来调度插队。 因此非公平锁的吞吐量要高于公平锁。
  • 重入性:读写锁允许读线程和写线程按照请求锁的顺序重新获取读取锁或者写入锁。只有写线程释放了锁,读线程才可以获取重入锁,写线程获取写入锁后可以再次获取读取锁,但是读线程获取读取锁后却不能获取写入锁。
  • 锁降级:写线程获取写入锁后可以获取读取锁,然后释放写入锁,这样就从写入锁变成了读取锁,从而实现锁降级特性,经典cache案例使用了锁降级
  • 锁升级:读取锁是不能直接升级为写入锁的。因此获取一个写入锁需要先释放所有的读取锁,如果在读锁中试图获取写入锁,就会发生死锁
    锁获取中断:读取锁和写入锁都支持Interrupt,语义与ReentrantLock一致。
  • 条件变量:写入锁提供了Condition的支持,但是读取锁却不允许获取条件变量,否则会得到一个UnsupportedOperationExcetpion异常
  • 重入锁:读取锁和写入锁的数量最大分别只能是65535

ReentrantReadWriteLock运用实例:

ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    Map cache = new HashMap();
    public Object get(String key){
        Object value  = null;
        try {
            rwl.readLock().lock();
            value = cache.get(key);
            if(null==value){
                rwl.readLock().unlock();//释放都锁,获取写锁
                try {
                    rwl.writeLock().lock();
                    //获取写锁后再次判断对象是否为null,方式下一个等待的写线程进入后直接获取数据去
                    if(null==value){
                        System.out.println(Thread.currentThread().getName());
                        value="aaaaa";//实际操作代码从数据库中查询得到的对象
                        cache.put(key, value);
                    }
                    //自身锁降级为都锁
                    rwl.readLock().lock();
                } catch (Exception e) {
                    // TODO: handle exception
                }finally{
                    rwl.writeLock().unlock();//释放写锁
                }
            }
        } catch (Exception e) {

        }finally{
            rwl.readLock().unlock();
        }
        return value;
    }

你可能感兴趣的:(多线程)