Java并发编程(10)-显式锁和读写锁的使用

版权声明: https://blog.csdn.net/pbrlovejava/article/details/83786433


更多关于Java并发编程的文章请点击这里:Java并发编程实践(0)-目录页


在Java5.0之前,用于调节共享对象访问的机制只有synchronized和volatile。Java5.0之后提供了新的选择:ReentrantLock,即显式锁。显式锁与之前提过的synchronized的同步互斥 机制不太一样,ReentrantLock并不作为内部锁机制的替代,而是当内部锁机制有局限时可供选择的高级特性。
本文总结自《Java并发编程实践》 第十三章 显式锁 。

一、显式锁

1.1、什么是显式锁

在Java5.0之前,用于调节共享对象访问的机制只有synchronized和volatile。Java5.0之后提供了新的选择:ReentrantLock,即显式锁。显式锁与之前提过的synchronized的同步互斥 机制不太一样,ReentrantLock并不作为内部锁机制的替代,而是当内部锁机制有局限时可供选择的高级特性:

  • 1、显式锁可供开发者人工调控,不易出现同步锁的死锁问题
  • 2、显式锁是可轮询的、定时的以及可中断的锁获取操作、非快结构的加锁(对应于synchronized的加锁和释放都是在同一块代码块中)

1.2、Lock和ReentrantLock

  • Lock接口
    Lock 实现提供了比使用 synchronized 方法和语句可获得的更广泛的锁定操作:
public interface Lock {
    void lock();
    void lockInterruptibly() throws InterruptedException;//可中断锁(在获取锁的时候可以响应中断)
    boolean tryLock();                    //轮询锁
    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;//定时锁
    void unlock();  //释放锁
    Condition newCondition();
}
  • ReentrantLock类
    ReentrantLock 是Lock的一个实现类,将由最近成功获得锁,并且还没有释放该锁的线程所拥有。此类的构造方法接受一个可选的公平 参数。当设置为 true 时,在多个线程的争用下,这些锁倾向于将访问权授予等待时间最长的线程。否则此锁将无法保证任何特定访问顺序。

1.3、如何使用显示锁

首先思考一下,为什么要使用显式锁呢?了解过synchronized同步锁的朋友应该明白,当A线程持有该锁,并且迟迟不能完成任务时,该锁将一直被A持有且不能释放,那么B线程就需要等待A释放锁,这会形成一个问题,即线程饥饿死锁。如以下代码:

public synchronized void function(){
            //doing something...
    }

但是,如果使用显式锁的话,我们可以人为地调控何时去获得锁,何时去释放锁,从而避免了死锁的发生,这就是使用显式锁的原因。

 Lock lock = new ReentrantLock();

        lock.lock();//由执行到该处的线程获得锁

        try{
            //doing something
            //捕获异常进行处理
        }finally {
            lock.unlock();//释放锁
        }

二、读写锁

2.1、为什么使用读写锁

ReentrantLock是一种标准的互斥锁,最多只有一个线程能拥有它;这样的实现是线程安全的,但是对读和写两种情况都进行了同步限制,那么在频繁读取时,会对性能造成不必要的浪费。所以,读写锁的出现缓解了这样的问题读写锁(ReadWriteLock)的加锁策略允许多个同时存在的读者,但是只允许一个写者。

2.2、ReadWriteLock接口和ReentrantReadWriteLock实现类

特点允许多个线程同时读,允许一个线程写。

public interface ReadWriteLock {
    Lock readLock();
    Lock writeLock();
}
//ReentrantReadWriteLock是默认的非公平读写锁实现
ReentrantLock lock= new ReentrantReadWriteLock();
Lock readLock = lock.readLock();//获得读锁
Lock writeLock = lock.writeLock();//获得写锁

2.3、使用读写锁

public class ReadWriteMap<K,V> {
    //读写锁
    private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
    //read lock
    private Lock readLock = readWriteLock.readLock();
    //write lock
    private Lock writeLock = readWriteLock.writeLock();
    //map
    private final Map<K,V> map ;
    //含参构造
    public ReadWriteMap(Map map){
        this.map = map;
    }

    //get方法,使用读锁
    public V get(K key){
        readLock.lock();//其他线程只读不写
        return map.get(key);
    }

    //put方法,使用写锁
    public void put(K key,V value){
        writeLock.lock();//其他线程都不能写
        map.put(key,value);
    }

}

猜你喜欢

转载自blog.csdn.net/pbrlovejava/article/details/83786433
今日推荐