因为虚拟机的优化技术,使用synchronized锁带来的性能开销越来越低,也不再是之前所谓的重量级锁。但是其本身的加锁机制限制了在灵活性和细粒度方面的拓展,例如在阻塞等待获取锁时不能响应中断(当然可以添加额外的代码实现响应中断),并且synchronized维持的是一种排他锁,在加锁的策略中没有考虑任务的类型。
在JDK1.5之后引入locks包,作为对加锁策略一个补充,synchronized使用的锁是依赖于对象上,lock则是创建出一个锁来使用。
Lock在加锁方面的同步语义与synchronized相似,提供如下函数:
1、lock,类似与synchronized的阻塞等待获取锁,不响应中断
2、lockInterruptibly,相比较与lock,在阻塞等待获取锁过程中可以响应中断
3、直接返回锁的获取结果的tryLock
4、可以等待一定时间的tryLock(long time,TimeUnit unit),因为其可定时功能,所以可用于轮询
5、newCondition,返回Condition对象,可用于完善wait、notify的功能
6、unlock,lock的锁属于代码层次控制,不会像synchronized由虚拟机监控在代码块执行结束自动释放,所以一般放在finally中进行释放
示例:
public class t{
public static void main(String[] args){
final ArrayList<Object> arr=new ArrayList<Object>();
final int len=3;
final Lock lock=new ReentrantLock();
final Condition in_condition=lock.newCondition();//两种不同的condition来分辨不同类型的任务
final Condition out_condition=lock.newCondition();
for(int i=0;i<10;i++){
new Thread(){ //写入数组
public void run(){
lock.lock();
try{
while(arr.size()==len){ //满则等待
in_condition.await();
}
arr.add(new Object());
System.out.println("arr.size()= "+arr.size());
out_condition.signal(); //唤醒移出线程
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock(); //锁的释放
}
}
}.start();
new Thread(){ //移出元素
public void run(){
lock.lock();
try{
while(arr.size()==0){ //空则等待
out_condition.await();
}
arr.remove(arr.size()-1);
System.out.println("arr.size()= "+arr.size());
in_condition.signal(); //唤醒写入线程
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}
}.start();
}
}
}
利用condition的await,signal方法替代wait,notify来区分对待不同类型的任务。
ReadWriteLock接口提供两个函数readLock、writeLock,返回Lock对象,ReentrantReadWriteLock实现ReadWriteLock接口,在readLock、writeLock中分别返回ReentrantReadWriteLock的内部类ReentrantReadWriteLock.WriteLock对象和ReentrantReadWriteLock.ReadLock对象。
读写锁的特点很明显,读锁与写锁、写锁与写锁互斥,读锁与读锁不互斥。因为在任务的并发执行中,可能存在的许多对共享数据的访问,而只有少部分对数据进行修改。
示例:
public class t{
public static void main(String[] args){
Object[] obj=new Object[3];
final LockArray<Object> arr=new LockArray<Object>(obj);
for(int i=0;i<10;i++){
new Thread(){
public void run(){
arr.add(new Object());//添加元素线程
}
}.start();
new Thread(){
public void run(){
arr.remove();//移除元素线程
}
}.start();
}
}
}
class LockArray<T>{
private final ReentrantReadWriteLock lock=new ReentrantReadWriteLock();
private final Lock w_lock=lock.writeLock();
private final Lock r_lock=lock.readLock();
private final Condition add_condition=w_lock.newCondition();//add添加元素
private final Condition rem_condition=w_lock.newCondition();//remove移除元素
private final T[] array; //基数组
private int index=0;
public LockArray(T[] array){
this.array=array;
}
public void add(T t){
try{
while(!w_lock.tryLock(1L,TimeUnit.SECONDS)){} //轮询等待获取锁
while(index==array.length){
System.out.println("full waiting");
add_condition.await();//数组满则等待通知
}
array[index++]=t;
System.out.println("after add array size = "+index);
rem_condition.signal(); //通知rem_condition移除元素线程
}catch(InterruptedException e){
e.printStackTrace();
}finally{
w_lock.unlock();
}
}
public T remove(){
T t=null;
try{
while(!w_lock.tryLock(1L,TimeUnit.SECONDS)){}//轮询等待获取锁
while(index==0){
System.out.println("empty waiting");
rem_condition.await();//数组为空,等待通知
}
t=array[--index];
System.out.println("after remove array size = "+index);
add_condition.signal();//通知add_condition添加元素线程
}catch(InterruptedException e){
e.printStackTrace();
}finally{
w_lock.unlock();
}
return t;
}
}
总结
API层次的lock加锁机制有着与synchronized相同的同步语义,提供了响应中断的阻塞等待机制,将加锁的范围由固定的代码块转为细粒度更高的lock锁对象本身上(JVM的优化已经使得synchronized已经成为轻量级锁,与lock的加锁性能开销几乎相同),而且在灵活性方面做出了补充,使用condition和读写锁来区别不同类型的任务。但是在锁的释放操作上需要手动释放,否则可能发生灾难。