Lock出现原因
- synchronized的缺陷: 如果一个代码块被synchronized修饰了,当一个线程获取了对应的锁,并执行该代码块时,其他线程便只能一直等待,等待获取锁的线程释放锁,如果这个获取锁的线程由于要等待IO或者其他原因被阻塞了,但是又没有释放锁,其他线程便只能干巴巴地等待:如果多个线程都只是进行读操作,当一个线程在进行读操作时,其他线程只能等待无法进行读操作。当有多个线程读写文件时,读操作和写操作会发生冲突现象,写操作和写操作会发生冲突现象,但是读操作和读操作不会发生冲突现象,很影响程序执行效率。因此需要一种可以不让等待的线程一直无期限地等待下去的机制(比如只等待一定的时间或者能够响应中断)
- 另外,通过Lock可以知道线程有没有成功获取到锁。这个是synchronized无法办到的
Lock和synchronized的区别
- synchronized 是 Java语言的关键字,因此是内置特性。Lock是一个类,通过这个类可以实现同步访问
- 采用synchronized 不需要用户去手动释放锁。而Lock 则必须要用户去手动释放锁
java.util.concurrent.locks包的探索
Lock接口
public interface Lock { // Lock是一个接口
void lock();
void lockInterruptibly() throws InterruptedException;
boolean tryLock();
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
void unlock();
Condition newCondition();
}
lock( )
lock( ) 方法是平常使用得最多的一个方法,就是用来获取锁。如果锁已被其他线程获取,则进行等待。
采用Lock类就必须主动去释放锁,并且在发生异常时,不会自动释放锁。因此一般来说,使用 Lock必须在try{}catch{}块中进行,并且将释放锁的操作放在 finally块 中进行,以保证锁一定被被释放,防止死锁的发生。
Lock lock = new ReentrantLock();
lock.lock();
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
tryLock( )
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false。也就说这个方法无论如何都会立即返回,在拿不到锁时不会一直在那等待。
tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false
Lock lock = ...;
if(lock.tryLock()) {
try{
//处理任务
}catch(Exception ex){
}finally{
lock.unlock(); //释放锁
}
}else {
//如果不能获取锁,则直接做其他事情
}
lockInterruptibly( )
lockInterruptibly() 方法比较特殊,当通过这个方法去获取锁时,正在等待获取锁的这个线程能够响应中断,即用lockinterruptibly( )方法去获取锁而未成功时可以用interrupt()方法将其等待中断。比如,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
lock.lockInterruptibly( )必须放在try块中或者在调用lockInterruptibly()的方法外声明 抛出InterruptedException。
- 当一个线程获取了锁之后,是不会被interrupt() 方法中断的。因为单独调用interrupt()方法不能中断正在运行过程中的线程,只能中断阻塞过程中的线程。
ReentrantLock类
ReentrantLock,意思是“可重入锁”,ReentrantLock是唯一实现了Lock接口的类,并且ReentrantLock提供了更多的方法。
public class Test {
private ArrayList<Integer> arrayList = new ArrayList<Integer>();
private Lock lock = new ReentrantLock(); //在这个地方声明Lock对象
public static void main (String[] args) {
final Test test = new Test();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
new Thread(){
public void run() {
test.insert(Thread.currentThread());
};
}.start();
}
public void insert(Thread thread) {
/* Lock lock = new ReentrantLock();
如果这句写在方法里,那么每个线程都会创建一个Lock对象,
每个线程都会获取到test对象的锁,无法实现同步 */
lock.lock(); // 在这里调用lock()方法获取锁
try {
System.out.println(thread.getName()+"得到了锁");
for(int i=0;i<5;i++) {
arrayList.add(i);
}
} catch (Exception e) {
// TODO: handle exception
}finally {
System.out.println(thread.getName()+"释放了锁");
lock.unlock();
}
}
}