ReentrantLock可重入锁的使用

java除了使用关键字synchronized外,还可以使用ReentrantLock实现独占锁的功能。而且ReentrantLock相比synchronized而言功能更加丰富,使用起来更为灵活,也更适合复杂的并发场景。这篇文章主要是从使用的角度来分析一下ReentrantLock。

ReentrantLock常常对比着synchronized来分析

ReentrantLocksynchronized
独占锁,加锁和解锁的过程需要手动进行,不易操作,但非常灵活。独占锁,加锁和解锁的过程自动进行,易于操作,但不够灵活。
可重入,但加锁和解锁需要手动进行,且次数需一样,否则其他线程无法获得锁。可重入,因为加锁和解锁自动进行,不必担心最后是否释放锁;
可以响应中断不可响应中断,一个线程获取不到锁就一直等待
可以实现公平锁机制不可以实现公平锁机制

ps: 公平锁:在锁上等待时间最长的线程将获得锁的使用权。通俗的理解就是谁排队时间最长谁先执行获取锁。

1、简单使用
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        initSimpleReentrantLockTest();
    }

    /**
     * 初始化并启动线程
     */
    public static void initSimpleReentrantLockTest() {
        new Thread(ReentrantLockTest::simpleReentrantLockTest, "线程A").start();
        new Thread(ReentrantLockTest::simpleReentrantLockTest, "线程B").start();
        new Thread(ReentrantLockTest::simpleReentrantLockTest, "线程C").start();
        new Thread(ReentrantLockTest::simpleReentrantLockTest, "线程D").start();
    }

    /**
     * 测试锁的获取与释放
     */
    public static void simpleReentrantLockTest() {
        try {
            lock.lock();
            System.out.println(Thread.currentThread().getName() + "\t获取了锁");
            TimeUnit.SECONDS.sleep(2);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println(Thread.currentThread().getName() + "\t释放了锁");
            lock.unlock();
        }
    }
}

执行结果,

线程A 获取了锁
线程A 释放了锁
线程B 获取了锁
线程B 释放了锁
线程C 获取了锁
线程C 释放了锁
线程D 获取了锁
线程D 释放了锁

2、ReentrantLock和synchronized不一样的地方, 实现公平锁
本次测试非公平锁,代码如下: 实现公平锁要在创建时进行声明 new ReentrantLock(true);
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    private static final Lock lock = new ReentrantLock();

    public static void main(String[] args) {
        initFairLockReentrantLockTest();
    }

    /**
     * 初始化并启动线程
     */
    public static void initFairLockReentrantLockTest() {
        new Thread(ReentrantLockTest::unFairLockReentrantLockTest, "线程A").start();
        new Thread(ReentrantLockTest::unFairLockReentrantLockTest, "线程B").start();
        new Thread(ReentrantLockTest::unFairLockReentrantLockTest, "线程C").start();
        new Thread(ReentrantLockTest::unFairLockReentrantLockTest, "线程D").start();
    }

    /**
     * 测试公平锁锁的获取与释放
     */
    public static void unFairLockReentrantLockTest() {
        for (int i = 0; i < 2; i++) {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "\t获取了锁");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + "\t释放了锁");
                lock.unlock();
            }
        }
    }
}
运行结果如下:

由于CPU切片问题,在释放锁后能够及时的再去获取锁,造成了释放锁后能够立刻获得锁

线程A 获取了锁
线程A 释放了锁
线程A 获取了锁
线程A 释放了锁
线程B 获取了锁
线程B 释放了锁
线程B 获取了锁
线程B 释放了锁
线程C 获取了锁
线程C 释放了锁
线程C 获取了锁
线程C 释放了锁
线程D 获取了锁
线程D 释放了锁
线程D 获取了锁
线程D 释放了锁

本次测试公平锁,测试公平锁,代码如下: 实现公平锁要在创建时进行声明 new ReentrantLock(true);
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    private static final Lock lock = new ReentrantLock(true);

    public static void main(String[] args) {
        initFairLockReentrantLockTest();
    }

    /**
     * 初始化并启动线程
     */
    public static void initFairLockReentrantLockTest() {
        new Thread(ReentrantLockTest::fairLockReentrantLockTest, "线程A").start();
        new Thread(ReentrantLockTest::fairLockReentrantLockTest, "线程B").start();
        new Thread(ReentrantLockTest::fairLockReentrantLockTest, "线程C").start();
        new Thread(ReentrantLockTest::fairLockReentrantLockTest, "线程D").start();
    }

    /**
     * 测试公平锁锁的获取与释放
     */
    public static void fairLockReentrantLockTest() {
        for (int i = 0; i < 2; i++) {
            try {
                lock.lock();
                System.out.println(Thread.currentThread().getName() + "\t获取了锁");
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                System.out.println(Thread.currentThread().getName() + "\t释放了锁");
                lock.unlock();
            }
        }
    }
}
运行结果如下:

由于增加公平锁,在释放锁后能够及时的再去获取锁也不能进行获取,获取锁的现状为顺序执行

线程A 获取了锁
线程A 释放了锁
线程B 获取了锁
线程B 释放了锁
线程C 获取了锁
线程C 释放了锁
线程D 获取了锁
线程D 释放了锁
线程A 获取了锁
线程A 释放了锁
线程B 获取了锁
线程B 释放了锁
线程C 获取了锁
线程C 释放了锁
线程D 获取了锁
线程D 释放了锁

3、测试响应中断

响应中断就是一个线程获取不到锁,不会傻傻的一直等下去,ReentrantLock会给予一个中断回应。在这里我们举一个死锁的案例。

import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockTest {
    private static final Lock lock = new ReentrantLock();
    private static final Lock lock2 = new ReentrantLock();

    public static void main(String[] args) {
        initInterruptReentrantLockTest();
    }

    /**
     * 初始化响应中断并启动线程
     */
    public static void initInterruptReentrantLockTest() {
        Thread threadA = new Thread(ReentrantLockTest::interruptReentrantLockTest, "线程A");
        Thread threadB = new Thread(ReentrantLockTest::interruptReentrantLockTest, "线程B");
        threadA.start();
        threadB.start();
        for (int i = 0; i < 5; i++) {
            try {
                TimeUnit.SECONDS.sleep(1);
                System.out.println("等待 "+(i+1)+" s");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        //中断结束A线程, B执行正常
        threadA.interrupt();
    }

    /**
     * 测试响应中断的锁的获取与释放
     * 		让线程发生死锁
     */
    public static void interruptReentrantLockTest() {
        String threadName = Thread.currentThread().getName();
        try {
            if (Objects.equals(threadName, "线程A")) {
                System.out.println(threadName + "\t开始获取lock!");
                lock.lockInterruptibly();
                System.out.println(threadName + "\t获取lock 开始等待获取lock2!");
                TimeUnit.SECONDS.sleep(1);
                lock2.lockInterruptibly();
            } else {
                System.out.println(threadName + "\t开始获取lock2!");
                lock2.lockInterruptibly();
                System.out.println(threadName + "\t获取lock2 开始等待获取lock!");
                TimeUnit.SECONDS.sleep(1);
                lock.lockInterruptibly();
            }
        } catch (InterruptedException e) {
            System.out.println(threadName + "\t异常!");
            e.printStackTrace();
        } finally {
            lock.unlock();
            lock2.unlock();
            System.out.println(threadName + "\t获取到了资源,正常结束!");
        }
    }
}
执行结果

线程A 开始获取lock!
线程B 开始获取lock2!
线程A 获取lock 开始等待获取lock2!
线程B 获取lock2 开始等待获取lock!
等待 1 s
等待 2 s
等待 3 s
等待 4 s
等待 5 s
线程A 异常!
线程B 获取到了资源,正常结束!
java.lang.InterruptedException
at java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireInterruptibly(AbstractQueuedSynchronizer.java:898)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1222)
at java.util.concurrent.locks.ReentrantLock.lockInterruptibly(ReentrantLock.java:335)
at self.studycode.lock.ReentrantLockTest.interruptReentrantLockTest(ReentrantLockTest.java:103)
at java.lang.Thread.run(Thread.java:748)
Exception in thread “线程A” java.lang.IllegalMonitorStateException
at java.util.concurrent.locks.ReentrantLock$Sync.tryRelease(ReentrantLock.java:151)
at java.util.concurrent.locks.AbstractQueuedSynchronizer.release(AbstractQueuedSynchronizer.java:1261)
at java.util.concurrent.locks.ReentrantLock.unlock(ReentrantLock.java:457)
at self.studycode.lock.ReentrantLockTest.interruptReentrantLockTest(ReentrantLockTest.java:116)
at java.lang.Thread.run(Thread.java:748)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值