1.为什么需要Lock
- 为什么synchronized不够用,还需要Lock
Lock和synchronized这两个最常见的锁都可以达到线程安全的目的,但是功能上有很大不同。
Lock并不是用来代替synchronized的而是当使用synchronized不满足情况或者不合适的时候来提供高级功能的
- 为什么synchronized不够用
效率低:锁的释放情况较少,试图获得锁不能设定超时,不能中断一个正在试图获得锁的线程
不够灵活:加锁和释放的时候单一,每个锁仅有单一的条件可能是不够的
无法知道是否成功的获取锁
2.Lock锁的意义
- 与使用synchronized方法和语句相比, Lock实现提供了更广泛的锁操作。 它们允许更灵活的结构,可以具有完全不同的属性,并且可以支持多个关联的Condition对象。
- 锁是一种用于控制多个线程对共享资源的访问的工具。 通常,锁提供对共享资源的独占访问,一次只能有一个线程可以获取该锁,并且对共享资源的所有访问都需要首先获取该锁。 但是,某些锁可能允许并发访问共享资源,例如ReadWriteLock的读取锁。
- 使用synchronized方法或语句可访问与每个对象关联的隐式监视器锁,但会强制所有锁的获取和释放以块结构方式进行。当获取多个锁时,它们必须以相反的顺序释放锁。
- 虽然用于synchronized方法和语句的作用域机制使使用监视器锁的编程变得更加容易,并且有助于避免许多常见的涉及锁的编程错误,但在某些情况下,您需要以更灵活的方式使用锁。 例如,某些用于遍历并发访问的数据结构的算法需要使用“移交”或“链锁”:您获取节点A的锁,然后获取节点B的锁,然后释放A并获取C,然后释放B并获得D等。 Lock接口的实现通过允许在不同范围内获取和释放锁,并允许以任意顺序获取和释放多个锁,从而启用了此类技术。
3.锁的用法
灵活性地提高带来了额外的责任。 缺少块结构锁定需要手动地去释放锁。 在大多数情况下,应使用以下惯用法:
Lock lock = new ReentrantLock();
lock.lock();
try{
}finally {
lock.unlock();
}
当锁定和解锁发生在不同的范围内时,必须小心以确保通过try-finally或try-catch保护持有锁定时执行的所有代码,以确保在必要时释放锁定。
Lock实现通过使用非阻塞尝试获取锁( tryLock() ),尝试获取可被中断的锁( lockInterruptibly以及尝试获取锁),提供了比使用synchronized方法和语句更多的功能。可能会超时( tryLock(long, TimeUnit) )。
Lock类还可以提供与隐式监视器锁定完全不同的行为和语义,例如保证顺序,