在JDK1.5以后,在并发包(java.util.concurrent)里面添加包locks,并提供了Lock接口,用于与synchronized类似的锁功能,不同的是Lock需要手动开启锁和释放锁。
为什么要用Lock锁?
- 尝试非阻塞的获取锁
- 获取锁的过程可以被中断
- 超时获取锁
Lock锁的实现类图

Lock锁的常用API
lock():加锁
lockInterruptibly():可中断
tryLock():尝试非阻塞的获取锁
unlock():释放锁
public class AttemptLocking {
private ReentrantLock lock = new ReentrantLock();
public void untimed() {
boolean captured = lock.tryLock();
try {
System.out.println("tryLock(): " + captured);
} finally {
if(captured)
lock.unlock();
}
}
public void timed() {
boolean captured = false;
try {
captured = lock.tryLock(2, TimeUnit.SECONDS);
} catch(InterruptedException e) {
throw new RuntimeException(e);
}
try {
System.out.println("tryLock(2, TimeUnit.SECONDS): " +
captured);
} finally {
if(captured)
lock.unlock();
}
}
public static void main(String[] args) {
final AttemptLocking al = new AttemptLocking();
al.untimed();
al.timed();
new Thread() {
{ setDaemon(true); }
public void run() {
al.lock.lock();
System.out.println("acquired");
}
}.start();
Thread.yield();
al.untimed();
al.timed();
}
}
在untimed方法里如果tryLock()无法拿到锁则离开去做别的事情;在timed方法里设置了等待时间为2秒,如果等待2秒还没拿到锁则离开去做别的事情。
Lock锁使用模板
Lock lock = new ReentrantLock();
lock.lock();
try {
// do my work
} finally {
lock.unlock();
}
注:这里lock()方法不能写到try里,因为如果自定义的lock发生异常,会导致空放解锁。return要放到try里面,以保证unlock()不会过早发生,从而将数据暴露给了第二个任务。
Lock锁的可重入性
可重入性是指当前线程可以重复进入被自己锁的代码块。如果没有可重入性,当遇到递归操作时就会发生死锁现象。
公平锁和非公平锁
- 公平锁:在多个线程等待锁的时候,当锁被释放时,保证最先请求的线程先得到锁(也就是等待时间最长的线程),保证了每个线程都可以得到锁。
- 非公平锁:与公平锁不一样的是,非公平锁不敢保证每个线程都可以拿到锁,但是可以提高线程交替的效率,从而获得高执行率。
读写锁ReentrantReadWriteLock
ReentrantReadWriteLock可以在多个线程同时进行时,允许一个写线程(不允许其他读线程和写线程)或者多个读线程的操作,支持读多写少的业务场景,提高程序性能。
读写锁模板
public class RwLockTemplate {
private ReentrantReadWriteLock reentrantReadWriteLock = new ReentrantReadWriteLock();
private Lock read = reentrantReadWriteLock.readLock();
private Lock write = reentrantReadWriteLock.writeLock();
public void set(Object params) {
this.write.lock();
try {
// do my work
} finally {
this.write.unlock();
}
}
public Object get() {
this.read.lock();
try {
// do my work
return null;
} finally {
this.read.unlock();
}
}
}
Condition接口
既然已经有了Lock锁,那么他也会有一套等待通知机制,他就是Condition接口,对应synchronized的wait()、notify()、notifyAll()方法。
condition模板
public class ConditionTemplete {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
public void waitc() throws InterruptedException {
lock.lock();
try {
condition.await();
} finally {
lock.unlock();
}
}
public void notityc() throws InterruptedException {
lock.lock();
try {
condition.signal();
//condition.signalAll();
} finally {
lock.unlock();
}
}
}
这里await()方法就是等待,signal()/signalAll()方法就是通知,condition可以有多个。
注:这里的signal()/signalAll()方法和synchronized的notify()/notifyAll()方法有所不同,signal()通知这个lock的所有等待,signalAll()通知程序中所有lock的所有等待。
使用Lock和Condition实现阻塞队列
public class BlockingQueueLC<T> {
private List<T> queue = new LinkedList<>();
private int limit;
private Lock lock = new ReentrantLock();
private Condition needNotEmpty = lock.newCondition();
private Condition needNotFull = lock.newCondition();
public BlockingQueueLC(int limit) {
this.limit = limit;
}
public void push(T el) throws InterruptedException {
this.lock.lock();
try {
while (this.limit == this.queue.size()) {
this.needNotFull.await();
}
this.queue.add(el);
this.needNotEmpty.signal();
} finally {
this.lock.unlock();
}
}
public T pull() throws InterruptedException {
this.lock.lock();
try {
while (0 == this.queue.size()) {
this.needNotEmpty.await();
}
this.needNotFull.signal();
return this.queue.get(0);
} finally {
lock.unlock();
}
}
}
来源:https://www.cnblogs.com/huanStephen/p/8213911.html