Java并发编程(五)Java中的锁

一、Lock接口

  1. 锁是用来控制多个线程访问共享资源的方式。一般来说,一个锁能防止多个线程同时访问共享资源(有些锁允许多个线程并发访问共享资源,比如读写锁)。Lock接口提供了与synchronized关键字类似的同步功能,只是在使用时需要显示地获取和释放锁。虽然它缺少了隐式获取释放锁的便捷性,但是缺拥有了获取和释放锁的可操作性、可中断性的获取锁及超时获取锁等多种synchronized关键字所不具备的同步特性。

  2. Lock的使用方式

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

    在finally块释放锁,保证在获取到锁之后,最终能够被释放。

    不要将获取锁的过程写在try块中,因为如果在获取锁时发生了异常,异常抛出的同时,也会导致锁无故释放。

  3. Lock接口提供的synchronized关键字不具备的特性
    在这里插入图片描述

  4. Lock接口常用API
    在这里插入图片描述

二、队列同步器

  1. 队列同步器AbstractQueuedSynchronizer,是用来构建锁或者其他同步组建的基础框架,它使用了一个int成员变量表示同步状态,通过内置的FIFO队列来完成资源获取线程的排队工作。
  2. 同步器的主要使用方式是继承,子类通过继承同步器并实现它的抽象方法来管理同步状态,在抽象方法的实现过程通过使用同步器提供的三个方法(getState()、setState(int newState)和compareAndSetState(int expect,int update))来进行对同步状态的更改操作,因为它们能保证状态的改变是安全的。
  3. 同步器是实现锁(也可以是任意同步组件)的关键,在锁的实现中聚合同步器,利用同步器实现锁的语义。可以这样理解二者之间的关系:锁是面向使用者的,它定义了使用者与锁交互的接口(比如可以允许两个线程并行访问),隐藏了实现细节;同步器面向的是锁的实现者,它简化了锁的实现方式,屏蔽了同步状态管理、线程的排队、等待与唤醒等底层操作。锁和同步器很好地隔离了使用者和实现者所需关注的领域。

三、重入锁

  1. 重入锁ReentrantLock,支持重进入的锁,表示该锁能够支持一个线程对资源的重复加锁。除此之外,该锁还支持获取锁时的公平和非公平选择。

    Synchronizer关键字隐式的支持重进入,比如一个synchronized修饰的递归方法,在方法执行时,执行线程在获取了锁之后仍能连续多次地获得该锁。

    ReentrantLock虽然没能像synchronized关键字一样支持隐式的重进入,但是在调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。

    公平性问题:如果在绝对时间上,先对锁进行获取的请求一定先被满足,那么这个锁是公平的,反之,是不公平的。ReentrantLock提供了一个构造函数能控制锁是否公平。

  2. 实现重进入

    重进入是指任意线程在获取到锁之后能够再次获取到改锁而不会被阻塞。满足:1、线程再次获取锁。2、锁的最终释放。锁对于获取进行计数自增,锁被释放时。计数自减,等于0时成功释放。

    ReentrantLock是通过组合自定义同步器来实现锁的获取与释放.

  3. 公平与非公平获取锁的区别

    公平性与否是针对获取锁而言的,如果一个锁是公平的,那么锁的获取顺序就应该符合请求的绝对时间顺序,也就是FIFO。

    对于非公平锁,只要CAS设置同步状态成功,则表示当前线程获取了锁,而公平锁则不同,判断条件多了hasQueuedPredecessors()方法,即加入了同步队列中当前节点是否有前驱节点的判断,如果该方法返回true,则表示有线程比当前线程更早地请求获取锁,因此需要等待前驱线程获取并释放锁之后才能继续获取锁。

    公平性锁保证了锁的获取按照FIFO原则,而代价是进行大量的线程切换。非公平性锁虽然可能造成线程“饥饿”,但极少的线程切换,保证了其更大的吞吐量。

四、读写锁

  1. 读写锁在同一时刻可以允许多个读线程访问,但是在写线程访问时,所有的读线程和其他写线程均被阻塞。读写锁维护了一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排它锁有了很大的提升。

  2. 特性
    在这里插入图片描述

  3. ReadWriteLock定义了获取读锁和写锁的两个方法,即readLock()和writeLock()两个方法。

    缓存实例:

    package thread;
    
     
    
    import java.util.HashMap;
    
    import java.util.Map;
    
    import java.util.concurrent.locks.Lock;
    
    import java.util.concurrent.locks.ReentrantReadWriteLock;
    
     
    
    public class Cache {
    
    static Map<String, Object> map = new HashMap<String, Object>();
    
    static ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    
    static Lock r = rwl.readLock();
    
    static Lock w = rwl.writeLock();
    
     
    
    // 获取一个key对应的value
    
    public static final Object get(String key) {
    
        r.lock();
    
        try {
    
            return map.get(key);
    
        } finally {
    
            r.unlock();
    
            }
    
    }
    
     
    
    // 设置key对应的value,并返回旧的value
    
    public static final Object put(String key, Object value) {
    
        w.lock();
    
        try {
    
            return map.put(key, value);
    
        } finally {
    
            w.unlock();
    
            }
    
    }
    
     
    
    // 清空所有的内容
    
    public static final void clear() {
    
    	w.lock();
    
        try {
    
            map.clear();
    
        } finally {
    
            w.unlock();
    
        }
    
    }
        
    }
    
  4. LockSupport工具类

    主要用于阻塞或者唤醒一个线程,它定义了一组的公共静态方法,这些方法提供了最基本的线程阻塞和唤醒功能。
    在这里插入图片描述

  5. Condition接口

    提供了类似Object的监视器方法(wait()、wait(long timeout)、notify()以及notifyAll())。

    Object的监视器方法与Condition接口的对比。
    在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值