ReentrantLock 锁

2022-08-06 15:57:04 浏览数 (3)

ReentrantLock 锁

实现 Lock 接口,使用时需导入 import java.util.concurrent.locks.*;

实现功能和 synchronized 关键字类似。但 synchronized 关键字是在 JVM 层面实现的,而 ReenTrantLock 是在 JDK 层面实现的。需要手动调用 lock 和 unlock 方法配合 try/finally 语句块来完成。

代码语言:javascript复制
public class ReentrantLockTest {
    // 创建锁对象
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        for(int i = 0; i < 5; i  ){
            new Thread(new MyThread()).start();
        }
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {                                   
            try {
                // 加锁,通常在 try 语句里完成
                lock.lock();
                Thread.sleep(500);
                System.out.println(Thread.currentThread().getName()   "excute");
            } catch (InterruptedException e) {}
            finally{
                // 解锁,必须在 finally 语句里完成
                lock.unlock();    
            }                      
            }
        }
    }
}Copy to clipboardErrorCopied

ReenTrantLock 比 synchronized 增加了一些高级功能,主要有以下三点:

实现等待中断

调用 lockInterruptibly 方法上锁,线程中断标志置为 true 时会抛出 InterruptedException 异常并释放锁。防止线程因为无法获得锁而一直等待,常用来从外部破坏线程死锁。

代码语言:javascript复制
public class ThreadDemo {
    // 创建锁对象
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyThread(),"thread1");
        Thread t2 = new Thread(new MyThread(),"thread2");
        t1.start();
        t2.start();
        Thread.sleep(500);
        // 提前中断线程
        t2.interrupt();                                      
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()   "begin");
            try {
                // 加可中断锁
                lock.lockInterruptibly();                    
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                System.out.println(Thread.currentThread().getName()   "out");
            } finally{
                try{
                    lock.unlock();
                } catch(IllegalMonitorStateException e) {}  
                System.out.println(Thread.currentThread().getName()   "end");
            }
        }
    }
}Copy to clipboardErrorCopied

调用 tryLock 方法上锁,可以从线程内部破坏死锁,可以更好地解决死锁问题。

  • 传入时间参数设定等待锁的时间,超时没有获得锁则中止。
  • 无参则返回锁申请的结果:true表示获取锁成功,false表示获取锁失败。
代码语言:javascript复制
public class ThreadDemo {
    // 创建锁对象
    static Lock lock = new ReentrantLock();

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(new MyThread(),"thread1");
        Thread t2 = new Thread(new MyThread(),"thread2");
        t1.start();
        t2.start();                                  
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            System.out.println(Thread.currentThread().getName()   "begin");
            try {
                // 加锁失败直接退出
                if(!lock.tryLock()) {                         
                    System.out.println(Thread.currentThread().getName()   "out");
                    return;
                }                  
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally{
                try{
                    lock.unlock();
                } catch(IllegalMonitorStateException e) {}
                System.out.println(Thread.currentThread().getName()   "end");
            }
        }
    }
}Copy to clipboardErrorCopied

实现公平锁

允许先等待的线程先获取锁,防止线程因无法获得锁而一直等待。但由于性能优势,默认情况下仍使用非公平锁。在构造锁对象时添加参数 true 即可实现。

代码语言:javascript复制
import java.util.concurrent.locks.*;

public class ReentrantLockTest {
    // 创建锁对象,且声明为公平锁
    static Lock lock = new ReentrantLock(true);

    public static void main(String[] args) throws InterruptedException {
        for(int i = 0; i < 5; i  ){
            new Thread(new MyThread()).start();
        }
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            lock.lock();                                     
            try {
                Thread.sleep(500);
                System.out.println(Thread.currentThread().getName()   "excute");
            } catch (InterruptedException e) {}
            lock.unlock();                                 
            }
        }
    }
}Copy to clipboardErrorCopied

选择性通知

ReentrantLock 对象可以创建一个或多个 Condition 对象,实现线程间的等待通知机制。比 synchronized 关键字 使用 wait/notify 方法更为简便和易用。

线程获得 Lock 锁之后便可调用 Condition 接口的 await 方法释放锁并等待,直到有其他线程调用 Condition 的 signal 方法唤醒线程。通过设置多个 condition 对象,多个线程等待不同的 condition 对象,可以实现选择性地叫醒线程。

代码语言:javascript复制
public class ThreadDemo {

    static ReentrantLock lock = new ReentrantLock();
    static Condition condition = lock.newCondition();

    public static void main(String[] args) throws InterruptedException {
        lock.lock();
        new Thread(new MyThread()).start();
        System.out.println("主线程等待通知");
        try {
            condition.await();
        } finally {
            lock.unlock();
        }
        System.out.println("主线程恢复运行");
    }

    static class MyThread implements Runnable {
        @Override
        public void run() {
            lock.lock();
            try {
                condition.signal();
                System.out.println("子线程通知");
            } finally {
                lock.unlock();
            }
        }
    }
}

0 人点赞