java包含两种锁机制:synchronized和java.util.concurrent.Lock
Lock接口实现类:ReenTrantLock
锁的种类:
1.可重入锁
2.可中断锁
3.公平锁
4.读写锁
java中的锁是基于对象的,比如线程A拥有了对象A的锁,线程B等待线程A释放对象A的锁
对于高并发的情况下,优先使用Lock
synchronized是java内置的关键字,Lock是一个接口
synchronized会自动释放锁比如:当前拥有锁的线程执行完后会自动释放锁,或者发生异常时,JVM会去释放锁,
而Lock不会,使用Lock建议在finally中调用unLock()方法去手动释放锁
synchornized不能让等待锁的线程响应中断,而Lock可以
synchornized不能判断是否获取到了锁,而Lock可以
可重入锁:两者都是可重入锁, 以synchronized为例,如果一个类包含两个方法,都含有synchronized关键字,此时
如果A方法中调用了B方法,如果A方法获取了该类的锁,在执行到调用B方法时,可以直接执行而不用再去申请锁
可中断锁:Lock是可中断锁,synchronized不是,其它线程在等待锁的时候可以中断当前等待就是可中断
公平锁:如果有10个线程在等待锁A释放,此时如果锁A释放了,那么这是个线程谁等待的时间久,那么将会获得锁,
synchronized是非公平锁,Lock默认也是非公平锁,但是Lock可以在创建的时候设置,比如:
ReenTrantLock r = new ReenTrantLock(true);
读写锁:读写锁讲一个资源的访问分成了两个锁,读锁和写锁
ReadWriteLock就是一个读写锁,是个接口,子类有:ReenTrantReadWriteLock,可以通过readLock()获取读锁,通过
writeLock()获取写锁
读锁和写锁的互斥关系:
持有读锁时,不能持有写锁
持有写锁时,不能持有读锁
可多线程共同持有读锁,只有单一线程可持有写锁
持有写锁的前提是,没有一个线程持有读锁
持有读锁的前提是,没有一个线程持有写锁
锁升级:读锁--写锁,此处需要先释放读锁
锁降级:写锁--读锁,此处需要先获取读锁,然后再释放写锁(防止其它线程获取写锁后,修改数据,造成脏数据)
文档demo对读写锁的时候描述很清晰:
class CachedData {
Object data;
volatile boolean cacheValid;
final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
void processCachedData() {
rwl.readLock().lock();
if (!cacheValid) {
// Must release read lock before acquiring write lock
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
// Recheck state because another thread might have
// acquired write lock and changed state before we did.
if (!cacheValid) {
data = ...
cacheValid = true;
}
// Downgrade by acquiring read lock before releasing write lock
rwl.readLock().lock();
} finally {
rwl.writeLock().unlock(); // Unlock write, still hold read
}
}
try {
use(data);
} finally {
rwl.readLock().unlock();
}
}
}