🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
**1. java.util.concurrent.ReentrantLock** ReentrantLock 类也可以实现线程同步。使用ReentrantLock的基本结构如下: ``` Lock myLock = new ReentrantLock(); myLock.lock(); // 加锁 try { // 共享资源任务代码 } finally { myLock.unlock(); // 解锁,一定要在finally块中解锁 } ``` * 该锁以串行方式提供服务,即线程需要排队等待被运行; * `lock`方法和`unlock`方法必须配对使用,因为线程要调用`lock`加锁,调用`unlock`来解锁。 * `unlock`方法必须放在`finally`语句块中,因为防止当`try`语句块中抛出异常时,导致锁不能被释放,造成其他线程永远被阻塞。 * 该锁不允许使用带有资源的`try`语句。 * 需要注意因为`try`语句块抛出异常而导致提前退出`try`语句块,这样的话`finally`将提前解锁,使对象可能处于一种受损状态。 **2. ReentrantLock 锁可重入** ``` public void transfer(int from, int to, double amount) throws InterruptedException { myLock.lock(); try { // 资源共享代码 } getTotalBanlance(); // 调用方法,该方法也使用了ReentrantLock锁 } finally { mykLock.unlock(); } } public double getTotalBanlance() { myLock.lock(); try { // 资源共享代码 } finally { myLock.unlock(); } } ``` 上面的代码中`transfer`方法调用了`getTotalBanlance`方法,而且两个方法都调用了`myLock.lock()`和`myLock.unlock()`,这是嵌套使用了ReentrantLock锁。 <br/> ReentrantLock锁有一个持有计数器,上面代码中嵌套使用了两对`lock`和`unlock`,持有计数器就是2。当退出`getTotalBanlance`方法,持有计数器为1,再退出`transfer`方法,持有计数器为0,这就是可重入。 <br/> **3. 案例演示** 使用`for`创建10个线程,这10个线程共同让变量`sum`进行递增。比较使用同步代码块与不适用同步代码块的区别。 ```java public class ReentrantLockTest { static int sum = 0; // 创建锁对象 private static Lock myLock = new ReentrantLock(); /** * 同步块 */ public static void userReentrantLock() { // 使用for创建10个线程 for (int i = 1; i <= 10; i++) { Runnable r = () -> { try { while (true) { myLock.lock(); // 加锁 try { ++sum; System.out.println(Thread.currentThread().getName() + " sum=" + sum); Thread.sleep(100); } finally { myLock.unlock(); // 解锁 } } } catch (InterruptedException e) { } }; Thread t = new Thread(r); t.setName("线程" + i); t.start(); // 启动线程 } } /** * 不使用同步块 */ public static void noReentrantLock() { // 使用for创建10个线程 for (int i = 1; i <= 10; i++) { Runnable r = () -> { try { while (true) { ++sum; System.out.println(Thread.currentThread().getName() + " sum=" + sum); Thread.sleep(100); } } catch (InterruptedException e) { } }; Thread t = new Thread(r); t.setName("线程" + i); t.start(); // 启动线程 } } public static void main(String[] args) { //让这两个方法分别单独运行,不要一起运行 // userReentrantLock(); noReentrantLock(); } } ``` 运行结果如下: ``` 同步块结果-递增 不使用同步块结果-乱 线程1 sum=55 线程6 sum=11 线程1 sum=56 线程8 sum=13 线程1 sum=57 线程8 sum=13 线程1 sum=58 线程10 sum=14 线程1 sum=59 线程7 sum=15 线程3 sum=60 线程3 sum=16 线程3 sum=61 线程6 sum=18 线程2 sum=62 线程4 sum=16 线程2 sum=63 线程5 sum=16 ``` 结果递增,说明资源访问并不冲突。 结果乱,说明资源访问冲突了。 * java.util.concurrent.locks.Lock 5.0 * void lock() 对共享资源部分加锁。 * void unlock() 对共享资源部分解锁。 * java.util.concurrent.locks.ReentrantLock 5.0 * ReentrantLock() 构建一个可以被用来保护临界区的可重入锁。 * ReentrantLock(boolean fair) 构建一个可以被用来保护临界区的公平策略锁。它偏爱等待时间最长的线程,但是这个策略导致性能大大下降,其实这并不公平。默认情况不强制为公平。