文章目录
1.1 与 synchronized 对比
Lock 锁实现提供了比使用同步方法和语句可以获得的更广泛的锁操作。它们允许更灵活的结构,可能具有非常不同的属性,并且可能支持多个关联的条件对象。Lock 提供了比 synchronized 更多的功能。
区别:
- Lock 非 Java 语言内置的是一个类, synchronized 是 Java 语言的关键字,incident是内置特性。
- synchronized 不需要手动释放锁,当其修饰的部分执行完后,线程自动释放锁。Lock 必须手动释放锁,若不主动释放可能出现死锁现象。
1.2 lock 介绍
public interface Lock {
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition()
}
1.2.1 lock和unlock与一般格式
lock() 和 unlock() 分别为上锁和解锁
一般使用格式:
Lock lock = ...;
lock.lock();
try{
//处理的任务
}catch(Exception ex){
}finally{
lock.unlock();
}
1.2.2 newCondition
相对于 synchronized 与 wait()/notify() 这两个方法一起使用可以实现等待/通知模式,Lock 锁的 newCondition() 方法返回 Condition 对象,Condition 类也可以实现等待/通知模式。
用 notify() 通知时, JVM 会随机唤醒某个等待的线程,使用 Condition 类可以进行选择性的通知。
Condition常用的两个方法:
- await():使当前线程等待,并释放锁,其他线程调用 signal() 时,线程重新获得锁并继续执行。
- signal():唤醒一个等待的线程。
注意:调用 Condition 的两个方法前,也需要线程持有相关的 Lock 锁,调用 await() 后线程会释放这个锁,在 signal() 调用后会从当前 Condition 对象的等待队列中唤醒一个线程,唤醒的线程尝试获得锁,一旦获得锁成功就继续执行。
1.3 ReentrantLock 类
可重入锁,是唯一实现 Lock 接口的类,并且 ReentrantLock 提供了更多的方法。
可重入锁:若线程A获取某一方法的锁后,线程B也要获取该方法的锁,假如该锁为不可重入锁,则B会进入一直等待的状态,出现自我死锁。(因此 synchronized 也是可重入锁)
ReentrantLock 类具有完全互斥排他的效果,同一时间只有一个线程在执行 ReentrantLock.lock() 方法后面的任务,这样做虽然保证了同时写实例变量的线程安全性,但效率低下。(这也是和 ReentrantReadWriteLock 类相比最主要的缺点)
因此jdk亦提供了 ReentrantReadWriteLock 类,使用它时可以在进行读操作时不需要同步执行,提升运行速度,加快运行效率。
1.4 ReadWriteLock 接口
public interface ReadWriteLock {
/**
* Returns the lock used for reading.
*/
Lock readLock();
/**
* Returns the lock used for writing.
*/
Lock writeLock();
}
一个用来获取读锁(共享锁),一个用来获取写锁(排他锁)。
即,将文件的读写操作分开,分成两个锁来分配给线程,从而使得多个线程可以同时进行读操作。
读操作:读取实际变量的值。写操作:向实例变量写入值。
**ReentrantReadWriteLock **实现了 ReadWriteLock 接口。其中有很多方法,但最重要的是 readLock() 和 writeLock() 两种方法。
读锁之间不互斥,写锁和读锁或写锁都有互斥效果。
1.5 小结
- Lock 是 synchronized 的进阶,完全可以取代后者,并且有更丰富的功能。
- Lock 是接口, synchronized 是关键字,后者是内置的语言实现。
- synchronized 在发生以长时,会自动释放线程占有的锁,因此不会导致死锁现象的发生;而 Lock 发生异常时,如果没有主动通过 unlock() 去释放锁,则很可能造成死锁现象,因此使用 lock 时需要在 finally 块中释放锁。
- Lock 可以让等待锁的线程响应中断,而 synchronized 不行,使用它的线程会一直等待下去,不能够响应中断。
- 通过 lock 可以知道是否成果获取锁,而 synchronized 不可以。
- Lock 实现读写操作分离,可以提高多个线程的读操作效率。
- 读读异步,读写互斥,写读互斥,写写互斥。
- 当资源竞争十分激烈时(大量线程同时竞争),lock 的性能远远优于 synchronized。