Java 5中提供了另一种实现线程同步或互斥的机制,即使用Lock。Lock比传统线程模型中的synchronized方式更加面向对象。
互斥锁–Lock(ReentrantLock)
所谓互斥锁, 指的是一次最多只能有一个线程持有的锁. 在jdk1.5之前, 我们通常使用synchronized机制控制多个线程对共享资源的访问. 而现在, Lock提供了比synchronized机制更广泛的锁定操作, Lock和synchronized机制的主要区别:
synchronized机制提供了对与每个对象相关的隐式监视器锁的访问, 并强制所有锁获取和释放均要出现在一个块结构中, 当获取了多个锁时, 它们必须以相反的顺序释放. synchronized机制对锁的释放是隐式的, 只要线程运行的代码超出了synchronized语句块范围, 锁就会被释放. 而Lock机制必须显式的调用Lock对象的unlock()方法才能释放锁, 这为获取锁和释放锁不出现在同一个块结构中, 以及以更自由的顺序释放锁提供了可能。
读写锁–ReadWriteLock(ReentrantReadWriteLock)
读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥。换句话说读取锁允许多个reader线程同时持有, 而写入锁最多只能有一个writter线程持有。
读写锁的使用场合: 读取共享数据的频率远大于修改共享数据的频率, 在上述场合下, 使用读写锁控制共享资源的访问, 可以提高并发性能。
package com.huang.test;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* 读写锁的妙用
* 简单缓存 的原理
* @author wuseyukui
*
*/
public class CacheDemo {
private ReadWriteLock lock = new ReentrantReadWriteLock();
private Map<String, Object> cache = new HashMap<String, Object>();
public static void main(String[] args) {
}
/**
* 简单,通俗
* 缺点:只有写的时候需要同步(互斥),多线程读数据的时候不需要同步,影响效率
* @param key
* @return
*/
// public synchronized Object getData(String key) {
// Object obj = cache.get(key);
// if (obj == null) {
// obj = "aaa";// 实际是去查询DB
// }
//
// return obj;
// }
/**
* 效率高, 多线程读数据时用读锁可并发,写数据时用写锁互斥。
* @param key
* @return
*/
public synchronized Object getData(String key) {
lock.readLock().lock();
Object obj = null;
try {
obj = cache.get(key);
if (obj == null) {
lock.readLock().unlock();
lock.writeLock().lock();// ①
try {
// 为了代码的严禁缜密
// 假设有三个线程到了代码①处,线程1成功获得写锁,另外两个线程被阻塞。
// 当线程1执行到②,释放了写锁,另外两个线程就有机会获取到写锁,进行DB操作,此时obj已经不为空,不需要再查询DB
if (obj == null) {
obj = "aaa";// 实际是去查询DB
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.writeLock().unlock();// ②
}
lock.readLock().lock();
}
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.readLock().unlock();
}
return obj;
}
}