08ReentrantLock
程序员文章站
2024-03-24 19:25:40
...
重入锁,手动加锁和释放锁,退出临界区时必须手动释放,否则其它线程无法访问资源了。
主要有一下几个特性:
1. 可重入
同一线程可以多次获得锁,但是在释放锁时,也要释放相同的次数,如果释放次数多,会得到一个IllegalMonitorStateException
异常,如果释放次数少,相当于还持有锁。
public class Test01 {
public static class LockTest implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
public static int i = 0;
@Override
public void run() {
lock.lock();
lock.lock();
try {
for (int j = 0; j < 10000; j++) {
i++;
}
}finally {
lock.unlock();
lock.unlock();
}
}
}
@Test
public void test01() throws Exception{
LockTest lock1 = new LockTest();
LockTest lock2 = new LockTest();
Thread t1 = new Thread(lock1);
Thread t2 = new Thread(lock2);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(LockTest.i);
}
}
2. 可中断
可中断 lockInterruptibly()方法,可重入锁在等待过程中可以被中断,而synchronized在等待锁只有两种情况,要么获得锁要么继续等待。
首先构造一个死锁,然后中断其中一个锁。
public class Test02 {
public static class LockTest implements Runnable{
public static ReentrantLock lock1 = new ReentrantLock();
public static ReentrantLock lock2 = new ReentrantLock();
int lock;
public LockTest(int lock){
this.lock = lock;
}
@Override
public void run() {
try {
if(lock == 1){
lock1.lockInterruptibly();
Thread.sleep(500);
lock2.lockInterruptibly();
}else {
lock2.lockInterruptibly();
Thread.sleep(500);
lock1.lockInterruptibly();
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(lock1.isHeldByCurrentThread()){
lock1.unlock();
}
if(lock2.isHeldByCurrentThread()){
lock2.unlock();
}
System.out.println(Thread.currentThread().getId()+"线程退出");
}
}
}
@Test
public void test01() throws Exception{
LockTest r1 = new LockTest(1);
LockTest r2 = new LockTest(2);
Thread t1 = new Thread(r1);
Thread t2 = new Thread(r2);
t1.start();
t2.start();
Thread.sleep(2000);
t1.interrupt();
}
}
3. 可限时
除了等待外部通知,要避免死锁的另外一个方法:限时等待。tryLock(),若不加参数运行,锁被其它线程占用,不会等待,立即返回
public class Test03 {
public static class LockTest implements Runnable{
public static ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
try {
if(lock.tryLock(3000, TimeUnit.MILLISECONDS)) {
Thread.sleep(6000);
}else {
System.out.println("get lock failed");
}
}catch (Exception e){
e.printStackTrace();
}finally {
if(lock.isHeldByCurrentThread()){
lock.unlock();
}
}
}
}
@Test
public void test01() throws Exception{
LockTest lockTest = new LockTest();
Thread t1 = new Thread(lockTest);
Thread t2 = new Thread(lockTest);
t1.start();
t2.start();
t1.join();
t2.join();
}
}
4. 公平锁
大多数情况下,锁申请是不公平的,可以指定new ReentrantLock(true);实现公平锁获得锁时按照请求锁的顺序。
public class Test04 {
public static class TestLock implements Runnable {
public static ReentrantLock lock = new ReentrantLock(true);
@Override
public void run() {
while (true) {
try {
lock.lock();
System.out.println(Thread.currentThread().getName() + "获得锁");
} finally {
lock.unlock();
}
}
}
}
@Test
public void test01() throws Exception{
TestLock testLock = new TestLock();
Thread t1 = new Thread(testLock, "th1");
Thread t2 = new Thread(testLock, "th2");
Thread t3 = new Thread(testLock, "th3");
t1.start();
t2.start();
t3.start();
t1.join();
t2.join();
t3.join();
}
}
5. 重要方法整理
lock()
lockInterruptibly()
tryLock()
unlock
6. 重入锁实现主要三个要素:
- 原子状态:原子状态使用cas操作来储存当前锁的状态,判断锁是否已经被别的线程持有。
- 等待队列:所有没有请求到锁的线程,会进入等待队列进行等待。待有线程释放锁后,系统就能从等待中唤醒一个线程,继续工作。
- 阻塞原语park()和unpark():用来挂起和恢复线程。没有得到锁的线程将会被挂起。
实时内容请关注微信公众号,公众号与博客同时更新:程序员星星
推荐阅读