【Java】jdk 中的可重入锁

jdk 中的可重入锁有synchronizedReentrantLock

可重入锁,也称为递归锁。一个持有锁的线程,在释放锁之前,如果再次访问了该同步锁的其他方法,这个线程不需要再次竞争锁,只需要记录重入次数。重入锁的设计目的是为了解决死锁的问题
p.s. 以 synchronized 举例,若一个 synchronized 方法1 调用 synchronized 方法2,两方法的锁都是同一个,则某线程执行方法1时,会一并执行方法2,并不会因为方法1拿了锁,方法2没有而不执行

synchronized

synchronized 有四种用法:

  • 修饰实例方法
  • 修饰静态方法
  • 修饰实例方法的代码块
  • 修饰静态方法的代码块

synchronized 代码块的参数:this表示当前类对象(静态方法中不能用);xxx.class表示类本身;还可以传入其他参数

// 修饰实例方法:锁的是类对象
public synchronized void printHello() {
    System.out.println("hello");
}

// 修饰静态方法:锁的是类本身
public static synchronized void printHi() {
    System.out.println("hi");
}

// 修饰实例方法的代码块
public void printHello() {
    synchronized (this) {
        System.out.println("hello");
    }
}

// 修饰静态方法的代码块
public static void printHi() {
    synchronized (MultithreadDemo.class) {
        System.out.println("hello");
    }
}

ReentrantLock

ReentrantLock 类实现了 Lock接口,除此以外还有其他实现类👇

常用方法

  • lock():等待锁,无视中断
  • lockInterruptibly():等待锁,考虑中断。中断会抛出 InterruptedException
  • tryLock(long timeout, TimeUnit unit):指定时间等待锁,考虑中断。指定时间内获得锁返回 true,否则 false,中断会抛出 InterruptedException
  • tryLock():直接获取锁。成功 true,否则 false,相当于tryLock(0, TimeUnit unit)
  • unlock():解锁。必须有持有锁的线程执行,否则抛出 IllegalMonitorStateException
    1. 为了避免死锁,unlock()最好放在 finally 中
    2. ReentrantLock 是可重入锁,因此一个线程可能多次加锁,但是unlock()执行一次解锁一次,因此加几次锁,就要解锁几次

lock() 和 unlock() 的简单使用👇

private Lock lock = new ReentrantLock();
public void run () {
    lock.lock();
    try {
        for (int i = 0; i < 5; i++)
            System.out.println(Thread.currentThread().getName() + ":" + i);
    } finally {
        lock.unlock();
    }
}

lock()、lockInterruptibly()、tryLock(long timeout, TimeUnit unit) 在线程中断情况下的表现👇

ReentrantLock lock = new ReentrantLock();
Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        try {
            lock.lock();
            System.out.println("t1拿到锁");
            Thread.sleep(1000L);
            System.out.println("t1执行结束");
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }
});
Thread t2 = new Thread(new Runnable() {
    @Override
    public void run() {
        boolean flag = true;
        try {
            System.out.println("t2等待拿锁");
//                    ****************** start **********************
//                    lock.lock();
//                    lock.lockInterruptibly();
            flag = lock.tryLock(200, TimeUnit.MILLISECONDS);
//                    ****************** end **********************
            if (flag) {
                System.out.println("t2拿到锁");
                System.out.println("t2执行结束");
            } else {
                System.out.println("指定时间内没有拿到锁,over");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (flag) {
                lock.unlock();
            }
        }
    }
});
t1.start();
// 保证 t1 拿到锁
Thread.sleep(10L);
t2.start();
// 保证 t2 在等待锁
Thread.sleep(10L);
// 若隐藏线程中断语句,t2 正常等待锁 或 指定时间内等待
t2.interrupt();
// 在测试类中需要保持运行一会
Thread.sleep(2000L);

其他方法

  • int getHoldCount():当前锁的加锁次数,如果无线程持有锁就返回0
  • int getQueueLength():当前竞争此锁队列长度
  • int getWaitQueueLength(Condition condition):当前锁等待队列长度。需要与Condition联合使用
  • boolean isFair():是否是公平锁
  • boolean isLocked():当前锁是否处于锁定状态
  • boolean isHeldByCurrentThread():当前锁是否被当前线程持有
  • boolean hasWaiters(Condition condition):当前condition是否有线程在等待,需要与Condition联合使用
  • boolean hasQueuedThread(Thread thread):目标线程是否在这个锁的竞争队列中
  • boolean hasQueuedThreads():是否有线程在当前锁的竞争队列中

两者区别

posted @ 2023-02-18 20:36  洗衣机o  阅读(73)  评论(0)    收藏  举报