Java并发-显式锁篇【可重入锁+读写锁】 (2)

最后我们来看下lockInterruptibly()方法,它也是阻塞获取锁,只是比lock()多了个中断异常,即获取锁时,如果线程被中断,则抛出中断异常

public class ReentrantLockDemo { private Lock lock = new ReentrantLock(); private int i = 0; public void interruptAdd(){ try { lock.lockInterruptibly(); i++; } catch (InterruptedException e) { e.printStackTrace(); } finally { System.out.println(i); lock.unlock(); } } public static void main(String[] args) throws InterruptedException { ReentrantLockDemo demo = new ReentrantLockDemo(); ExecutorService service = Executors.newFixedThreadPool(5); for (int i = 0; i < 100; i++) { // 第10次,立马关闭线程池,停止所有的线程(包括正在执行的和正在等待的) if (10 == i){ service.shutdownNow(); } service.submit(()->{ demo.interruptAdd(); }); } } }

多运行几次,有可能输出如下:

1 2 3 4 5 6 6 6 6 6 java.lang.InterruptedException at ......

这就是因为前面几个都是正常获取到锁并执行了i++,但是后面的几个线程因为被突然停止,所以抛出中断异常

最后就是释放锁, unlock()

这个就很简单了,上面的代码都有涉及到这个释放锁

不过细心的朋友可能发现了,上面的unlock()都是在finally块中编写的

这是因为在获取锁并执行任务时,有可能抛出异常,此时如果不把unlock()放到finally块中,那么锁不被释放,这在后期是一个很大的隐患(其他线程无法再次获取到这个锁,如果是lock()形式的获取锁,则线程会一直阻塞)

这也是显式锁无法完全替代内置锁的一个原因,有危险

2. 读写锁 ReadWriteLock

读写锁内部就两个方法,分别返回读锁和写锁

读锁属于共享锁,而写锁属于独占锁(前面介绍的可重入锁和内置锁也是独占锁)

读锁允许多个线程同时获取一个锁,因为读不会修改数据,它很适合读多写少的场合

下面我们用代码来看下

先看下读锁,代码如下:

public class ReadWriteLockDemo { private int i = 0; private Lock readLock; private Lock writeLock; public ReadWriteLockDemo() { ReadWriteLock lock = new ReentrantReadWriteLock(); this.readLock = lock.readLock(); this.writeLock = lock.writeLock(); } public void readFun(){ readLock.lock(); System.out.println("=== 获取到 读锁 ==="); try { System.out.println(i); }finally { readLock.unlock(); System.out.println("=== 释放了 读锁 ==="); } } public static void main(String[] args) throws InterruptedException { ReadWriteLockDemo demo = new ReadWriteLockDemo(); ExecutorService executors = Executors.newFixedThreadPool(2); for (int i = 0; i < 10; i++) { executors.submit(()->{ demo.readFun(); }); } } }

多次运行,有可能输出下面的结果:

=== 获取到 读锁 === 0 === 获取到 读锁 ===

可以看到,两个线程都获取到了读锁,这就是读锁的优势,多个线程同时读

下面看下写锁,代码如下:(这里用到了ReentrantReadWriteLock类,表示可重入的读写锁)

public class ReadWriteLockDemo { private int i = 0; private Lock readLock; private Lock writeLock; public ReadWriteLockDemo() { ReadWriteLock lock = new ReentrantReadWriteLock(); this.readLock = lock.readLock(); this.writeLock = lock.writeLock(); } public void writeFun(){ writeLock.lock(); System.out.println("=== 获取到 写锁 ==="); try { i++; System.out.println(i); }finally { writeLock.unlock(); System.out.println("=== 释放了 写锁 ==="); } } public static void main(String[] args) throws InterruptedException { ReadWriteLockDemo demo = new ReadWriteLockDemo(); ExecutorService executors = Executors.newFixedThreadPool(2); for (int i = 0; i < 10; i++) { executors.submit(()->{ demo.writeFun(); }); } } }

输出如下:可以看到,写锁类似上面的重入锁的lock()方法,阻塞获取写锁

=== 获取到 写锁 ===1=== 释放了 写锁 ====== 获取到 写锁 ===2=== 释放了 写锁 ====== 获取到 写锁 ===3=== 释放了 写锁 ====== 获取到 写锁 ===4=== 释放了 写锁 ====== 获取到 写锁 ===5=== 释放了 写锁 ====== 获取到 写锁 ===6=== 释放了 写锁 ====== 获取到 写锁 ===7=== 释放了 写锁 ====== 获取到 写锁 ===8=== 释放了 写锁 ====== 获取到 写锁 ===9=== 释放了 写锁 ====== 获取到 写锁 ===10=== 释放了 写锁 ===

关于读写锁,需要注意的一点是,读锁和写锁必须基于同一个ReadWriteLock类才有意义

如果读锁和写锁分别是从两个ReadWrite Lock类中获取的,那么读锁和写锁就是完全无关的两个锁,也就不会起到锁的作用(阻止其他线程访问)

这就类似synchronized(a)和synchronized(b),分别锁了两个对象,此时单个线程是可以同时访问这两个锁的

3. 区别

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zygjzy.html