在之前的多线程编程的文章中我们讲到了如何使用关键字synchronized加锁来实现同步访问。本文我们继续来探讨锁这个问题,从Java 5之后,在java.util.concurrent.locks包下提供了另外一种锁的方式来实现同步访问,那就是Lock。
既然都可以通过synchronized锁来实现同步访问了,那么为什么Java还提供java.util.concurrent.locks包来实现锁的机制? 即生瑜,何生亮?
这里就不得不罗列一下synchronized的不足之处:使用synchronized关键字会锁住某一段程序,别的程序如果需要调用的话就必须等待,这样减少了速度、效率。并发的情况下有可能产生死锁,导致程序中断。而且对多线程环境中,使用synchronized后,线程要么获得锁,执行相应的代码,要么无法获得锁处于等待状态,对于锁的处理不灵活。而Lock提供了多种基于锁的处理机制,比如:
- lockInterruptibly() 如果当前线程未被中断,则获取锁。
- tryLock() 仅在调用时锁为空闲状态才获取该锁。
- unlock() 释放锁。
多线程竞争一个锁(synchronized锁)时,其余未得到锁的线程只能不停的尝试获得锁,而不能中断。高并发的情况下会导致性能下降。ReentrantLock的 lockInterruptibly()方法可以优先考虑响应中断。 一个线程等待时间过长,它可以中断自己,然后ReentrantLock响应这个中断,不再让这个线程继续等待。有了这个机制,使用 ReentrantLock时就不会像synchronized那样产生死锁了。
一:关于java.util.concurrent.locks包
查看JDK源码,先了解一下locks包下都有哪些class文件,大致熟悉下包里文件的个数、文件名
查看包文件后,接下来就要查看每一个文件,进行分类,哪些class文件是interface类型,哪些是class类型,以及接口与实现类之间的对应关系。
整理包下的类关系图为:
说明:
- 其中类ReadLock和WriteLock的代码是位于ReentrantReadWriteLock.java文件里,且有加public static关键字进行修饰。
- 其中类ReentrantLock.java的文件里还含有:Sync、NonfairSync、FairSync三个静态类。其中Sync extends AbstractQueuedSynchronizer
二:Lock源码
package java.util.concurrent.locks;
import java.util.concurrent.TimeUnit;
/**
* {@code Lock} implementations provide more extensive locking
* operations than can be obtained using {@code synchronized} methods
* and statements. They allow more flexible structuring, may have
* quite different properties, and may support multiple associated
* {@link Condition} objects.
*/
public interface Lock {
/**
* Acquires the lock.
*/
void lock();
/**
* Acquires the lock unless the current thread is
* {@linkplain Thread#interrupt interrupted}.
*/
void lockInterruptibly() throws InterruptedException;
/**
* Acquires the lock only if it is free at the time of invocation.
*/
boolean tryLock();
/**
* Acquires the lock if it is free within the given waiting time and the
* current thread has not been {@linkplain Thread#interrupt interrupted}.
*/
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
/**
* Releases the lock.
*/
void unlock();
/**
* Returns a new {@link Condition} instance that is bound to this
* {@code Lock} instance.
*
*/
Condition newCondition();
}
说明:
-
lock()、tryLock()、tryLock(long time, TimeUnit unit)和lockInterruptibly()是用来获取锁的。
- unLock()方法是用来释放锁的。
- newCondition()用于线程协作。
三:Lock的使用
用一个简单的例子来看看Lock是怎么用的
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import org.apache.commons.lang.math.RandomUtils;
public class LockDemo {
Lock lock = new ReentrantLock();
public static void main(String[] args){
final LockDemo d = new LockDemo();
new Thread(){
@Override
public void run(){
d.insert(Thread.currentThread());
}
}.start();
new Thread(){
@Override
public void run(){
d.insert(Thread.currentThread());
}
}.start();
}
public void insert(Thread thread){
//加锁
lock.lock();
try{
System.out.println(thread.getName()+"获得了锁");
// TODO 做些事情吧...
Thread.sleep(RandomUtils.nextInt(100));
}catch(Exception e){
e.printStackTrace();
}finally{
//释放锁
lock.unlock();
System.out.println(thread.getName()+"释放了锁");
}
}
}
运行结果:
Thread-0获得了锁
Thread-0释放了锁
Thread-1获得了锁
Thread-1释放了锁
说明:申请锁是通过Lock lock = new ReentrantLock();语句实现的。在数据同步代码块里,先通过lock.lock();语句进行加锁,在退出数据同步代码块时通过lock.unlock();释放锁。需要注意的是lock.unlock()是放在finally语句里的,即这个语句的执行很重要!
参考资料:
https://www.ibm.com/developerworks/cn/java/j-jtp10264/
http://www.cnblogs.com/dolphin0520/p/3923167.html
http://blog.csdn.net/aesop_wubo/article/details/7544148
http://www.blogjava.net/BucketLi/archive/2010/09/30/333471.html
http://blog.csdn.net/chy996633/article/details/8627903