Java Concurrent--显式锁(Lock)

类图:

Lock接口

与内置加锁机制(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

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

为什么要用ReentrantLock? 因为内置锁synchronized在一些特殊情况下存在一定的局限性,例如无法中断一个正在能带获取锁的线程。ReentrantLock能够实现轮询锁、定时锁、可中断锁、公平锁非公平锁等。但要注意:使用ReentrantLock必须在finally块中释放锁。

ReentrantLock使用方法:

1  实现普通的锁功能:
Lock lock = new ReentrantLock();
...
lock.lock();
try{
    ...
}finally{
    lock.unlock();
}
2  实现轮询锁

通过Lock接口中的tryLock()方法实现。轮询锁会尝试顺序获取需要的锁,比如下面代码中的fromAcct锁和toAcct锁。如果不能获取所有选哟的锁,它会释放所有已经获取的锁,然后重新尝试获取所有锁。

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;
}
3  实现定时锁

也是通过Lock接口中的tryLock()方法实现。定时锁会根据指定时间来提供一个时限,如果操作不能在知道哪个时间内给出结果,那么程序就会提前结束。而内置锁开始请求锁之后这个操作将无法取消,因此内置锁很难实现带有时间限制的操作。

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();
    }
}
4  实现中断锁

中断锁由Lock接口中的lockInterruptibly();方法实现,该方法能够在获取锁的同时保持对中断的相应。

public boolean SendOnSharedLine(String message) throws InterruptedException{
    lock.lockInterruptibly();
    try{
        return cancellableSendOnShareLine(message);  //该方法必须抛出InterruptedException异常
    }finally{
        lock.unlock();
    }
}
5  实现公平锁/非公平锁

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

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

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

ReadWriteLock接口

ReadWriteLock是读/写锁接口,一个资源可以被多个读操作访问,或者被一个写操作访问,但两者不能同时进行。读写锁是一种性能优化措施,在一些特定情况下能实现更高的并发性。ReadWriteLock接口如下:

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

读写锁ReentrantReadWriteLock

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

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

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();
        }
    }  
}  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值