在Java多线程编程中,Lock对象与前面分析过的Atomic系列的类都属于高级别的并发工具其在Java里面与内置锁synchronized关键字的作用类似,但功能却比synchronized更加强大和灵活。
锁的作用主要是为了守卫(guard)共享资源存在的,例如数据库,文件系统,数字计数器或者一个消息进程等等。
Lock接口是jdk5之后引入的高级工具类,完整的包名是java.util.concurrent.locks.Lock它自身是一个接口不能直接被实例化,它下面提供了两个子类分别是:
ReentrantLock
ReentrantReadWriteLock
其中ReentrantReadWriteLock内部实现了读写锁,两个静态类分别是:
ReentrantReadWriteLock.ReadLock
ReentrantReadWriteLock.WriteLock
一个通用的使用lock的模板写法如下:
Lock l = new ReentrantLock();
l.lock(); try {
// access the resource protected by this lock
} finally
{
l.unlock();
}
大部分时候我们都会使用ReentrantLock作为Lock的实现,其是Lock接口的一个实现类,提供了互斥锁的功能。那么它与synchronized相比有什么特点呢?
(1)ReentrantLock提供了公平性保证,而synchronized唤醒的线程都是随机的,没办法做到让BLOCK时间最长的线程先执行任务,而ReentrantLock则可以做到。
(2)提供了tryLock()方法,可以用来实现非阻塞式编程,如果tryLock成功就会获得锁然后执行,否则就继续去干别的事过一会再来看看是否可以索取,另一种方式是tryLock(timeout)可以指定一定的时间周期如果在此之内获得锁就执行,否则就先去干别的事,从而减少了线程的BLOCK状态。
(3)可以执行lockInterruptibly()方法对阻塞的线程进行打断,在synchronized中处于等待的线程是没法进行控制的
(4)提供了api可以获取当前阻塞的线程有多少个。
最后ReentrantLock相比synchronized有一些缺点:
增加了代码复杂度,在其出现的地方必须使用try-finally语句,而synchroized则是隐藏了加锁解锁的细节,比较精简不容易出错,如果忘记了释放锁那么有可能导致出现无限循环。
本文主要介绍了Java里面高级并发工具Lock接口的使用,以及其子类ReentrantLock特点与synchronized相比的优缺点,总得来说Lock接口提供了更加丰富api和灵活的功能,但同时也带来了编码的复杂性。如果一个程序员忘记了在finally块中释放锁,那么很有导致其他一些莫名奇妙的问题,从这一点来说在比较简单的多线程代码中还是优先推荐使用synchronized关键字来同步。