Lock 与 Syncronized 和 Volatiled 的区别?
- synchronized与volatile是java中的一个关键字,也就是说是Java语言内置的特性。
- Lock是java.util.concurrent.locks并发包下面的一个接口
- Lock需主动释放锁unlock(),否则会引起线程阻塞
Lock的优点
- 性能并不是选择syncronized或者lock的原因,jdk6中syncronized的性能已经与lock相差不大。
- 如果要选择lock的话,会基于lock拥有的几个优点(内置锁所不具备):
1.如果希望当获取锁时,有一个等待时间,不会无限期等待下去。
2.希望当获取不到锁时,能够响应中断
3.当读多,写少的应用时,希望提高性能
4.获取不到锁时,立即返回false。获取到锁时返回true。
Lock的接口方法
lock接口定义以下方法:
public interface Lock {
//表明加锁时,当前拥有这个锁的线程可被中断。
void lockInterruptibly() throws InterruptedException;
//用于尝试获取锁,能获取返回true,否则返回false。
boolean tryLock();
//与tryLock类似,尝试一定的时间后,是否能够获取锁返回相应的true或false。
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
//释放当前线程锁
void unlock();
Condition newCondition();
}
java.util.concurrent.locks包下常用的类(JDK1.5)
- ReentrantLock(可重入锁)
- ReadWriteLock(读写锁)
- ReentrantReadWriteLock (可重入读写锁)
ReentrantLock(可重入锁)
同一个线程可以重复加锁,可以对同一个锁加多次,每次释放的时候回释放一次,直到该线程加锁次数为0,这个线程才释放锁。
如果锁具备可重入性,则称作为可重入锁。像synchronized和ReentrantLock都是可重入锁,可重入性在我看来实际上表明了锁的分配机制:基于线程的分配,而不是基于方法调用的分配。举个简单的例子,当一个线程执行到某个synchronized方法时,比如说method1,而在method1中会调用另外一个synchronized方法method2,此时线程不必重新去申请锁,而是可以直接执行方法method2。
class MyClass {
public synchronized void method1() {
method2();
}
public synchronized void method2() {
}
}
上述代码中的两个方法method1和method2都用synchronized修饰了,假如某一时刻,线程A执行到了method1,此时线程A获取了这个对象的锁,而由于method2也是synchronized方法,假如synchronized不具备可重入性,此时线程A需要重新申请锁。但是这就会造成一个问题,因为线程A已经持有了该对象的锁,而又在申请获取该对象的锁,这样就会线程A一直等待永远不会获取到的锁。
而由于synchronized和Lock都具备可重入性,所以不会发生上述现象。
- ReentrantLock实现公平锁
new ReentrantLock(true); // 公平锁
new ReentrantLock() 或 new ReentrantLock(false);//非公平锁
ReentrantReadWriteLock (可重入读写锁)
什么叫可重入:
就是同一个线程可以重复加锁,可以对同一个锁加多次,每次释放的时候回释放一次,直到该线程加锁次数为0,这个线程才释放锁。什么叫读写锁:
也就是读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能只有一个线程拥有,而且获取写锁的时候,其他线程都已经释放了读锁,而且在该线程获取写锁之后,其他线程不能再获取读锁。特点:
1.可重入:就是同一个线程可以重复加锁,可以对同一个锁加多次,每次释放的时候回释放一 次,直到该线程加锁次数为0,这个线程才释放锁。比如同一个线程中可以对同一个锁加多次写入锁。写线程获取写入锁后可以再次获取读取锁,但是读线程获取读取锁后却不能获取写入锁。
2.读写锁: 也就是读锁可以共享,多个线程可以同时拥有读锁,但是写锁却只能只有一个线程拥有,而且获取写锁的时候,其他线程都已经释放了读锁,而且在该线程获取写锁之后,其他线程不能再获取读锁。
3.锁降级:写线程获取写入锁后可以获取读取锁,然后释放写入锁,这样就从写入锁变成了读取锁,从而实现锁降级的特性。
4.锁升级:读取锁是不能直接升级为写入锁的。因为获取一个写入锁需要释放所有读取锁,所以如果有两个读取锁试图获取写入锁而都不释放读取锁时就会发生死锁。
5.重入数:读取锁和写入锁的数量最大分别只能是65535(包括重入数)
6.锁获取中断:读取锁和写入锁都支持获取锁期间被中断。这个和独占锁一致。源码代码示例
*class RWDictionary {
* private final Map<String, Data> m = new TreeMap<String, Data>();
* private final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
* private final Lock r = rwl.readLock();
* private final Lock w = rwl.writeLock();
*
* public Data get(String key) {
* r.lock();
* try { return m.get(key); }
* finally { r.unlock(); }
* }
* public String[] allKeys() {
* r.lock();
* try { return m.keySet().toArray(); }
* finally { r.unlock(); }
* }
* public Data put(String key, Data value) {
* w.lock();
* try { return m.put(key, value); }
* finally { w.unlock(); }
* }
* public void clear() {
* w.lock();
* try { m.clear(); }
* finally { w.unlock(); }
* }
* }