JAVA中多线程高并发场景下保证线程安全通常都会考虑加锁。但是在特殊场景下我们也可以采用java.util.concurrent包提供的线程安全的对象,避免加锁从而达到高效的目的。
但是,这些线程安全的对象仅仅指的是针对于原子性操作是线程安全的,如果多个方法同时调用无法保证线程安全,只能考虑加锁。这里我们举个列子:假设我们在多线程高并发场景下使用java.util.concurrent.ConcurrentLinkedQueue这个对象来获取队列中的元素,可以直接调用poll()方法,不用加锁也可以保证线程安全,假如我们在获取的时候需要先判断队列是否为空然后获取,也就是先调用isEmpty()方法、接着调用poll()方法,这种情况就无法保证线程安全,因为这里是两步操作,无法保证原子性。
ReentrantLock也是多线程并发时候加锁一种方式,通常会用来和synchronized做比较。我简单整理一下如下:
1、从使用方法角度对比
- ReentrantLock必须手动释放锁,synchronized不用考虑(发生异常自动释放锁)
- ReentrantLock可以尝试锁定 trylock
- ReentrantLock还可以调用lockInterruptibly方法,可以对线程interrupt方法做成响应
- ReentrantLock可以指定公平锁Lock lock = new ReentrantLock(true)
2、从性能角度对比
- JDK1.5以前synchronized性能略低于ReentrantLock,包含JDK1.5版本
- JDK1.5以后Java虚拟机对synchronized做了很多优化,增加了偏向锁、轻量级锁(多数情况下是自旋锁)、重量级锁等细节。性能和ReentrantLock差不多。
我们来分析ReentrantLock 手动加锁lock()、释放锁unlock()、尝试锁定tryLock()
package com.reentranlock;import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;/*** ReentrantLock讲解* lock()、unlock()、尝试锁定tryLock()** @author 小辉哥/小辉GE* <p>* 2019年8月10日 下午15:30:00*/
public class ReentrLock {Lock lock = new ReentrantLock();public void lock1() {try {lock.lock();System.out.println("lock1方法调用了");TimeUnit.SECONDS.sleep(5);} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void lock2() {try {lock.lock();System.out.println("lock2方法调用了");} finally {lock.unlock();}}public void lock3() {boolean status = false;try {// status = lock.tryLock();try {status = lock.tryLock(2, TimeUnit.SECONDS);System.out.println("lock3方法尝试加锁,返回status为:" + status);} catch (InterruptedException e) {e.printStackTrace();}} finally {if (status)lock.unlock();}}public static void main(String[] args) {ReentrLock reentrLock = new ReentrLock();new Thread(reentrLock::lock1).start();try {TimeUnit.SECONDS.sleep(1);} catch (InterruptedException e) {e.printStackTrace();}new Thread(reentrLock::lock2).start();//尝试加锁new Thread(reentrLock::lock3).start();}
}
测试输出结果如下:
结果分析:
1、从输出结果我们可以看出ReentrantLock手动加锁是需要释放的,一般与try/catch/finally一起使用,且在finally手动释放锁。
2、lock1方法、lock3方法调用完之后才执行lock2方法,也就是保证了多个线程获取锁只能等待其它已获得锁的线程执行完毕。
3、lock3方法可以不等到lock1方法执行完毕就调用,这句是tryLock作用,可以在另一个线程加锁的同时,尝试去加锁,能获取锁返回true,不能获取锁返回false。如果想让lock3可以获取锁,我们可以把tryLock等待时间调大一点即可。
以上代码仅供参考,如有不当之处,欢迎指出!!!
更多干货,欢迎大家关注和联系我。期待和大家一起更好的交流、探讨技术!!!