Lock

原创
2016/08/28 16:00
阅读数 267

1、JavaSE5的synchronized都是重量级的,而在JavaSE5之后出现了Lock接口以及相关接口来实现锁的功能。而且在JavaSE6 中Lock性能和Lock性能相差无几,但是synchronized能实现的功能Lock都能实现,原因是Lock功能更全面。

 

2、Lock接口提供的synchronized关键字不具备的主要特性有

1)尝试非阻塞地获取锁,通过 tryLock 进行非阻塞的尝试获取锁

2)能被中断地获取锁,通过lockInterruptibly 进行可中断的竞争锁,在锁竞争过程中可以被中断

3)超时获取锁,通过在tryLock获取锁时设置最大超时时间进行超时获取

4)必须手工释放锁,因为synchronized代码块中发生异常会自动释放锁,但是Lock则不会。

5)获取等待通知组件,通过调用 newCondition ,而且可以有多个Condition.

 

3、使用Lock的伪代码

Lock lock = new ReentrantLock();

lock.lock();

try{

// do something.

}finally{

lock.unlock();

}

1)注意必须在finally里面释放锁,原因是Lock不会自动释放

2)获取锁的过程最好写在try外面,原因是自定义的Lock可能在初始化Lock时发生异常,可能这个时候都还没有获取到锁,然后又调用释放锁,就会出现代码异常。

 

4、重入锁(ReentrantLock)

重入锁是指支持重进入的锁,也就是获取锁之后继续获取该锁是不会被阻塞。synchronized隐式含有重入锁,所以可以递归调用synchronized修饰的方法也不会被阻塞等待。

ReetrantLock 还可以支持公平锁和非公平锁,通过构造方法Boolean参数决定。

公平锁是指先对锁进行请求的锁一定先被满足。这意味着等待时间最长的线程最有可能获取到锁,这样可以避免活锁的发生。如果不管线程等待的时间任意分配锁则是非公平锁。

公平锁和非公平锁相对,公平性虽然保证了锁的获取按照了FIFO原则,而代价则是进行大量的线程切换,非公平锁虽然可能造成线程出现活锁的情况,但是避免线程的切换可以保证更大的吞吐量。

 

5、读写锁(ReentrantReadWriteLock)

无论是synchronized还是ReentrantLock都是排他锁,在同一时刻只允许一个线程进行访问,而读写锁在同一时刻可以运行多个读线程同时访问,但是在写线程访问时,所有的读线程和其他写线程都被阻塞。

读写锁维护了一对锁,一个读锁和一个写锁,通过分离读锁和写锁,使得并发性相比一般的排他锁有很大的提升。

读写锁可以降级(从写锁降级为读锁),但是不可以升级(从读锁升级为写锁),为什么不能升级呢?是因为假设有多个线程同时获取了读锁,突然有一个线程升级为写锁,那么计算他修改了数据,但是其他拥有读锁的线程读取的数据可能不是最新的,避免脏读数据所以不支持锁的升级。

如果读远远大于写,那么读写锁可谓是最佳之选了,下面就是使用读写锁实现缓存的代码

private static Map<String,Object> cache = new HashMap<>();

private static ReadWriteLock lock = new ReentrantReadWriteLock();

private static Lock readLock = lock.readLock();

private static Lock writeLock = lock.writeLock();

public static Object get(String key){

readLock.lock();

try{

return cache.get(key);

}finally{

readLock.unlock();

}

}

public static void put(String key, Object value){

writeLock.lock();

try{

cache.put(key, value);

}finally{

writeLock.unlock();

}

}

 

6、LockSupport

LockSupport是JDK中比较底层的类,用来创建锁和其他同步工具类的基本线程阻塞原语。

java锁和同步器框架的核心 AQS: AbstractQueuedSynchronizer,就是通过调用

LockSupport.park() 和 LockSupport.unpark()实现线程的阻塞和唤醒的。

LockSupport是可不重入。

示例代码(一直阻塞在 start,除非被唤醒 ):

public static void main(String[] args) {

System.out.println("start");

LockSupport.park();

System.out.println("end.");

}

 

 

 

 

展开阅读全文
打赏
0
2 收藏
分享
加载中
没没明白,读数据为什么还需要加锁呢?
2019/03/27 08:47
回复
举报
更多评论
打赏
1 评论
2 收藏
0
分享
返回顶部
顶部