java并发Lock接口 java并发之Lock接口的深入讲解
毅航同学 人气:0Juc中各种各样锁信息
在java的juc包为我们提供了各种各样的锁信息。如果细心来看其大部分都会实现一个名为LOCK的接口信息本文皆在帮你回顾Lock信息;
通过本文你将了解到如下内容:
1.Lock和synchronized的对比
2.Lock中常见API的总结
synchronized面临缺点
锁的出现主要是为了保证在并发访问共享资源时不出现错。 在java中如果不考虑性能损耗问题,那么对共享的资源信息加上synchrionzed关键字基本就可以解决大多数并发带来的问题,但是也随之带来灵活性和效率上的问题:
效率方面:
1. 此种情况下锁的释放情况较少,很容易到导致一直独占资源而导致性能的下降。
2. 当我们试图获取锁时不能直接指定具体条件
3. 不能中断正在试图获得锁的线程
灵活性:
1. 当获得锁资源后,无法得知是否获得锁信息
2. 仅当程序异常或顺利执行完时才会释放锁信息,缺乏主动释放锁的时机。
不适用的场景
场景1 :
当我们使用synchronized时,假如某线程获取到锁之后由于要等待IO或者其他原因进入阻塞状态,同时未释放锁信息,那么此时其他线程就只能一直等待。所以此时就需要synchronized有一种机制:避免等待的线程一直无期限地等待下去。
场景2 :
在读文件信息形式,不同线程的写操作是相互冲突的。但是读操作并不会导致冲突。如果我们不加考虑的为资源信息加上synchronized关键字,那么当多线程同时操作时,只有一个线程可以获取到资源,其他未获得锁信息的线程只能进入等待状态,从而导致读写效率不高。
Lock接口
Lock接口是对关键字synchronized的补充和扩展,它允许我们可以在线程安全的情况下更加灵活的操作共享资源信息。
常见用法:
Lock最佳实践:
1.lock(),unlock()
一般来说,使用Lock必须在try…catch…块中进行,并且将释放锁的操作放在finally块中进行。这是因为lock并不会像synchronized那样在异常时释放锁,所以必须保证有手动释放的过程,这样才能保证其它线程有获取锁的机会。
// 加锁 lock.lock(); try{ //处理任务 }catch(Exception ex){ }finally{ //释放锁 (锁的释放一般放入到finally块中进行,这样保证了总会对锁信息进行释放) lock.unlock(); }
2. tryLock() & tryLock(long time, TimeUnit unit)
tryLock()方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true;如果获取失败(即锁已被其他线程获取),则返回false,也就是说,这个方法无论如何都会立即返回即使其无法获取到锁也不会一致等待。
tryLock(long time, TimeUnit unit)方法和tryLock()方法是类似的,只不过区别在于这个方法在拿不到锁时会等待一定的时间,在时间期限之内如果还拿不到锁,就返回false
如果一开始拿到锁或者在等待期间内拿到了锁,则返回true。一般情况下,通过tryLock来获取锁时是这样使用的:
Lock lock = ...; if(lock.tryLock()) { try{ //处理任务 }catch(Exception ex){ }finally{ lock.unlock(); //释放锁 } }else { //如果不能获取锁,则直接做其他事情 }
3. lockInterruptibly()
lockInterruptibly()方法比较特殊,当通过这个方法去获取锁时,如果线程 正在等待获取锁,则这个线程能够 响应中断,即中断线程的等待状态。
例如,当两个线程同时通过lock.lockInterruptibly()想获取某个锁时,假若此时线程A获取到了锁,而线程B只有在等待,那么对线程B调用threadB.interrupt()方法能够中断线程B的等待过程。
public void method() throws InterruptedException { lock.lockInterruptibly(); try { //..... } finally { lock.unlock(); } }
当一个线程获取了锁之后,是不会被interrupt()方法中断的。因为interrupt()方法只能中断阻塞过程中的线程而不能中断正在运行过程中的线程。而在 synchronized 中,当一个线程处于等待某个锁的状态,是无法被中断的,只有一直等待下去,这也就是我们需要手动释放锁的原因。
给出如下的例子来进行验证:创建两个线程来共同争抢lock锁信息
public class LockInterruptibly implements Runnable { private Lock lock = new ReentrantLock(); public static void main(String[] args) { LockInterruptibly lockInterruptibly = new LockInterruptibly(); Thread thread0 = new Thread(lockInterruptibly); Thread thread1 = new Thread(lockInterruptibly); thread0.start(); try { Thread.sleep(2000); } catch (InterruptedException e) { e.printStackTrace(); } thread1.start(); thread1.interrupt(); } // 任务执行逻辑 @Override public void run() { System.out.println(Thread.currentThread().getName() + "尝试获取锁"); try { lock.lockInterruptibly(); try { System.out.println(Thread.currentThread().getName() + "获取到了锁"); Thread.sleep(5000); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "睡眠期间被中断了"); } finally { lock.unlock(); System.out.println(Thread.currentThread().getName() + "释放了锁"); } } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "获得锁期间被中断了"); } } }
执行结果:
Thread-0尝试获取锁
Thread-0获取到了锁
Thread-1尝试获取锁
Thread-1获得锁期间被中断了
Thread-0释放了锁
通过结果信息我们可以看出,lockInterruptibly()仅能中断正在等待的线程信息,而无法中断正在运行的线程。
对比 Lock和tryLock的区别
lock和tryLock都可以获取到锁信息,但两者之间还是存在些差异的。 具体如下:
1: lock拿不到锁会一直等待。tryLock是去尝试,拿不到就返回false,拿到返回true。
2: tryLock是可以被打断的,被中断的,lock是不可以。
// 实例代码 public class LockDemo implements Runnable{ static Lock lock1 = new ReentrantLock(); @Override public void run() { // 分别演示 lock,trylock区别 // lock1.lock(); lock1.tryLock(); System.out.println("线程 " + Thread.currentThread().getName() + " 获取到锁信息 "); } public static void main(String[] args) throws InterruptedException { LockDemo r1 = new LockDemo(); LockDemo r2 = new LockDemo(); r1.flag = true; r2.flag = false; Thread t1 = new Thread(r1); Thread t2 = new Thread(r2); t1.start(); Thread.sleep(1000); // 中断 t2.start(); t2.interrupt(); } }
结果信息:
当执行lock1.lock()时的输出:可以看到lock方法并不能响应中断信息,如果不解锁则会一致持有锁信息!
对于tryLock而言其可以响应中断
总结
本篇对Lock接口中常用到的Api进行了分析和总结,同时分析了Lock接口和synchronized关键之间的关系,希望对你能有所启发.
加载全部内容