JDK1.5开始引入显式锁,Lock
与使用synchronized
方法和语句相比,实现提供了更广泛的锁定操作。它们允许更灵活的结构,可以具有完全不同的属性。
1.接口说明
Modifier and Type | Method and Description |
---|---|
void |
lock() Acquires the lock.(获取锁) |
void |
lockInterruptibly() Acquires the lock unless the current thread is interrupted.(获取锁,除非当前线程被中断。) |
Condition |
newCondition() Returns a new Condition instance that is bound to this Lock instance.(返回一个Condition实例,java条件变量) |
boolean |
tryLock() Acquires the lock only if it is free at the time of invocation.(尝试获取锁) |
boolean |
tryLock(long time, TimeUnit unit) Acquires the lock if it is free within the given waiting time and the current thread has not been interrupted.(尝试获取锁,超市中断) |
void |
unlock() Releases the lock.(释放锁) |
2.基本使用
public static void main(String[] args) {
Lock lock = new ReentrantLock();
//申请锁
lock.lock();
try {
//todo 业务逻辑
}finally {
// unlock放到finally块中,避免锁泄露
lock.unlock();
}
}
- lock/unlock是成对使用的,unlock要放到finally中,避免锁泄露
- ReentrantLock是可重入锁,new ReentrantLock(boolean fair)当fair为true表示公平锁,因为公平锁使用开销比非公平锁打,所以默认为非公平锁策略。
3.Lock和synchronized对比
-
synchronized比较死板,在申请锁时只能等待,不能中断,不能跨越方法块,Lock相对灵活,有tryLock()方法,这样就有了等待超时,而且可以在一个方法中申请,另一个方法中释放,比较灵活。
-
synchronized不容易有锁泄露,在使用Lock的时候稍微不注意就会造成锁泄露。切记lock/unlock要成对出现,unlock要放到finally中。
-
synchronized具有可重入性,但是非公平,Lock同样具有重入性,但是也有公平锁。
-
ReentrantLock还有很多方法
- isLocked()方法用于检测锁是否被某个线程持有。
- getQueueLength()方法检测等待线程的数量。
- hasQueuedThreads()是否有线程等待
- hasQueuedThread(Thread thread)当前线程是否在等待
- isFair()是否是公平锁,返回true则为公平锁
- 等等详细到->官网地址
4.读写锁
4.1定义
锁的排他性使得多个线程无法以安全的方式在同一时间对共享变量的进行读取(仅仅读取),这个不利于提高系统的并发性。
读写锁是一种改型的排它锁。
-
它允许多个线程同时读取(只读)共享变量,但是只允许一个线程对线程的共享变量进行更新(包括读取更新)。
-
任何线程读取共享变量的时候,其他线程无法更新变量。
-
一个线程更新变量的时候,其他线程无法访问变量。
-
读锁是共享的,可以有多个线程一起持有;写锁是排他的,一个线程获取写锁之后,其他线程无法再次获取写锁和读锁。
4.2代码实现
public static final ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
public static final Lock read = lock.readLock();
public static final Lock write = lock.writeLock();
public static void main(String[] args) {
// 读锁
read.lock();
try{
//todo
}catch (Exception e){
}finally {
// 在finally中释放,避免锁泄露
read.unlock();
}
// 写锁
write.lock();
try{
//todo
}catch (Exception e){
}finally {
// 在finally中释放,避免锁泄露
write.unlock();
}
}
读写锁和ReentrantLock逻辑类似,毕竟都是继承了Lock接口,
在读取操作较多,更新操作较少的场景用读写锁能更好的优化系统的性能。
4.3读写锁降级
ReentrantReadWriteLock支持锁降级,也就是在获取写锁的情况下可以获取读锁。
注意:ReentrantReadWriteLock 不支持锁升级,读锁想要获取写锁,必须先释放读锁,再申请写锁。