一、什么是Lock 

java.util.concurrent.locks.Lock 是 Java 提供的比 synchronized 更灵活、功能更强的锁接口。它的核心作用:控制多线程同时访问共享资源,保证线程安全

最常用的实现类:ReentrantLock(可重入锁)


二、Lock 最标准、最安全的写法

Lock lock = new ReentrantLock();

lock.lock(); // 加锁
try {
    // 临界区(安全操作共享资源)
} finally {
    lock.unlock(); // 必须在 finally 里解锁!!
}

为什么必须放 finally?

因为如果代码中间抛异常,锁也能保证被释放,不会造成死锁。


三、ReentrantLock 5 大核心用法

我直接给你完整可运行代码,复制就能跑。

1. 基础用法:lock () /unlock ()(最常用)

作用:独占锁,拿不到就一直等待。

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

public class LockDemo1 {
    private static int count = 0;
    private static final Lock lock = new ReentrantLock();

    public static void increment() {
        // 加锁
        lock.lock();
        try {
            count++;
        } finally {
            // 解锁(必须放finally)
            lock.unlock();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) increment();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) increment();
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("最终结果:" + count); // 一定是 2000
    }
}

2. 可尝试获取锁:tryLock ()

作用:尝试拿锁,拿不到就不等待,直接返回 false避免死锁神器!

public void tryLockTest() {
    if (lock.tryLock()) {
        try {
            System.out.println("成功获取锁");
        } finally {
            lock.unlock();
        }
    } else {
        System.out.println("没拿到锁,去做别的事");
    }
}

3. 定时获取锁:tryLock (long time, TimeUnit unit)

作用:等一段时间,拿不到就放弃

try {
    // 等 1 秒
    if (lock.tryLock(1, TimeUnit.SECONDS)) {
        try {
            System.out.println("1秒内拿到锁");
        } finally {
            lock.unlock();
        }
    } else {
        System.out.println("1秒都没拿到,算了");
    }
} catch (InterruptedException e) {
    e.printStackTrace();
}

4. 可中断锁:lockInterruptibly ()

synchronized 不能中断,会一直死等。ReentrantLock 可以被别的线程打断等待

  1. 先看 synchronized 不能被中断 的效果
  2. 再看 lockInterruptibly () 可以被中断 的效果
public class SynchronizedInterruptDemo {
    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // 线程1:先占有锁,一直不释放
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("t1 拿到锁,开始睡觉");
                // 睡很久,不释放锁
                try { Thread.sleep(1000000); } catch (InterruptedException e) {}
            }
        });

        // 线程2:去抢锁,会一直阻塞
        Thread t2 = new Thread(() -> {
            System.out.println("t2 尝试获取 synchronized 锁");
            synchronized (lock) {
                System.out.println("t2 拿到锁了");
            }
            System.out.println("t2 正常结束");
        });

        t1.start();
        Thread.sleep(100); // 确保t1先拿到锁
        t2.start();

        Thread.sleep(1000);
        System.out.println("主线程尝试中断 t2");
        t2.interrupt(); // 试图中断阻塞中的t2
    }
}

运行结果你会看到:

  • t2 一直卡在 synchronized 那里
  • 主线程调用了 t2.interrupt()
  • t2 完全没反应,不会抛出异常,也不会退出这就是:synchronized 在等待锁时,不响应中断

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

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

    public static void main(String[] args) throws InterruptedException {
        // 线程1:先拿到锁,一直占着
        Thread t1 = new Thread(() -> {
            lock.lock();
            try {
                System.out.println("t1 拿到锁,开始睡觉");
                Thread.sleep(1000000); // 一直占锁
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                lock.unlock();
            }
        });

        // 线程2:用 lockInterruptibly() 抢锁
        Thread t2 = new Thread(() -> {
            System.out.println("t2 尝试获取锁(可中断)");
            try {
                // 关键方法:可中断的获取锁
                lock.lockInterruptibly();

                try {
                    System.out.println("t2 成功拿到锁");
                } finally {
                    lock.unlock();
                }
            } catch (InterruptedException e) {
                // 被中断后会进入这里!
                System.out.println("t2 在等锁的过程中被中断了!");
                e.printStackTrace();
            }
        });

        t1.start();
        Thread.sleep(100);
        t2.start();

        Thread.sleep(1000);
        System.out.println("主线程调用 t2.interrupt()");
        t2.interrupt(); // 中断正在等锁的 t2
    }
}

运行结果(非常关键)

t1 拿到锁,开始睡觉
t2 尝试获取锁(可中断)
主线程调用 t2.interrupt()
t2 在等锁的过程中被中断了!
java.lang.InterruptedException
	at java.base/java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireInterruptibly(AbstractQueuedSynchronizer.java:1264)

你学到了什么?

  • lock.lock():等锁时不响应中断,跟 synchronized 一样死等

  • lock.lockInterruptibly():等锁时响应中断一旦别的线程调用 thread.interrupt(),它会立刻抛出 InterruptedException,退出等待


5. 公平锁 / 非公平锁

  • 非公平锁(默认):谁抢到是谁的,速度快
  • 公平锁:谁等得久谁先拿,不会线程饥饿
Lock fairLock = new ReentrantLock(true);   // 公平锁
Lock noFairLock = new ReentrantLock();     // 非公平锁(默认)

四、ReentrantLock 最牛功能:Condition 线程精准通信

synchronized 只能用 wait/notify 随机唤醒。ReentrantLock 可以指定唤醒某个线程!

案例:生产者 - 消费者(精准唤醒)



import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionDemo {
    private final Lock lock=new ReentrantLock();
    private final Condition produceCond=lock.newCondition();
    private final Condition consumerCond=lock.newCondition();

    private boolean hasGoods=false;

    public void produce() throws InterruptedException {
        lock.lock();
        try{
            while(hasGoods){
                produceCond.await();
            }
            System.out.println(Thread.currentThread().getName()+"生产商品");
            hasGoods=true;
            consumerCond.signal();

        } finally {
            lock.unlock();
        }
    }

    public void consume() throws InterruptedException {
        lock.lock();
        try{
            while(hasGoods==false){
                consumerCond.await();
            }
            System.out.println(Thread.currentThread().getName()+"消费产品");
            hasGoods=false;
            produceCond.signal();

        }finally {
            lock.unlock();
        }

    }


}

package bagu_code;

public class Main {
    public static void main(String[] args) throws InterruptedException {
        //User user=User.Builder().name("张三").age(18).build();
        ConditionDemo demo=new ConditionDemo();

        Thread p1=new Thread(()->{
            try {
                while(true){
                    demo.produce();
                    Thread.sleep(100);
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"p1");
        Thread c1=new Thread(()->{
            try {
                while(true){
                    demo.consume();
                    Thread.sleep(100);
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        },"c1");
        Thread c2=new Thread(()->{
            try {
                while (true){
                    demo.consume();
                    Thread.sleep(100);
                }

            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

        },"c2");

        p1.start();
        c1.start();
        c2.start();
        p1.join();
        c1.join();
        c2.join();


    }
}

可以运行看看效果。


五、synchronized VS ReentrantLock

表格

对比项synchronizedReentrantLock
释放自动释放必须手动 unlock
可中断❌ 不支持✅ 支持
公平锁❌ 不支持✅ 支持
等待队列一个多个(Condition)
尝试加锁✅ tryLock()
性能低并发够用高并发更强
使用难度简单稍复杂

六.ReentrantLock的使用场景

你只要记住这 4 个场景:

  1. 需要尝试获取锁(不想死等)
  2. 需要定时获取锁
  3. 需要公平锁
  4. 需要多个等待队列、精准唤醒线程

七、总结

  1. ReentrantLock = 可重入、功能强大的锁
  2. 必须 lock () + try-finally + unlock ()
  3. 核心方法:
    • lock() 一直等
    • tryLock() 尝试拿
    • tryLock(1s) 定时拿
    • lockInterruptibly() 可中断
  4. Condition 可以精准唤醒线程
  5. 高并发、需要灵活控制 → 用 ReentrantLock

八、一些使用场景

案例 1:tryLock (超时时间) + 可中断(真实业务推荐

这是接口超时控制、防止线程堆积最常用写法:

  • 最多等 3 秒
  • 3 秒内拿不到 → 放弃
  • 等待过程中可被中断
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

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

    public static void businessMethod() {
        boolean acquired = false;
        try {
            // 最多等 3 秒,且支持中断
            acquired = lock.tryLock(3, TimeUnit.SECONDS);

            if (!acquired) {
                System.out.println(Thread.currentThread().getName() +
                        ":3秒内未获取到锁,接口超时,直接返回失败");
                return;
            }

            // 拿到锁,执行业务
            System.out.println(Thread.currentThread().getName() + ":获取锁成功,开始执行业务");
            Thread.sleep(2000); // 模拟业务执行

        } catch (InterruptedException e) {
            // 等待过程中被中断
            System.out.println(Thread.currentThread().getName() + ":等待锁时被中断");
            Thread.currentThread().interrupt(); // 恢复中断标志(规范写法)
        } finally {
            if (acquired) {
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + ":释放锁");
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 线程1先拿锁5秒
        new Thread(() -> {
            lock.lock();
            try {
                System.out.println("线程1拿到锁,占用5秒");
                Thread.sleep(5000);
            } catch (InterruptedException e) {
            } finally {
                lock.unlock();
            }
        }, "thread-1").start();

        Thread.sleep(100);

        // 线程2尝试抢锁,只等3秒
        new Thread(TryLockTimeoutExample::businessMethod, "thread-2").start();
    }
}

执行结果

线程1拿到锁,占用5秒
thread-2:3秒内未获取到锁,接口超时,直接返回失败

案例 2:综合实战(接口限流 + 超时 + 防死锁)

模拟一个订单接口,同一用户只能一个线程进入,超时直接返回。

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

public class OrderServiceDemo {
    // 用户锁
    private final Lock userLock = new ReentrantLock();

    public void createOrder(Long userId) {
        System.out.println("用户" + userId + "开始请求创建订单");

        boolean lockSuccess = false;
        try {
            // 最多等 2 秒
            lockSuccess = userLock.tryLock(2, TimeUnit.SECONDS);

            if (!lockSuccess) {
                System.err.println("用户" + userId + "请求超时:系统繁忙,请稍后重试");
                return;
            }

            // 模拟下单逻辑
            System.out.println("用户" + userId + "进入下单逻辑");
            Thread.sleep(3000); // 下单耗时3秒
            System.out.println("用户" + userId + "订单创建成功");

        } catch (InterruptedException e) {
            System.err.println("用户" + userId + "请求被中断");
            Thread.currentThread().interrupt();
        } finally {
            if (lockSuccess) {
                userLock.unlock();
            }
        }
    }

    public static void main(String[] args) {
        OrderServiceDemo service = new OrderServiceDemo();

        // 模拟两个并发请求
        new Thread(() -> service.createOrder(1001L)).start();
        new Thread(() -> service.createOrder(1001L)).start();
    }
}

结果

用户1001开始请求创建订单
用户1001开始请求创建订单
用户1001进入下单逻辑
用户1001请求超时:系统繁忙,请稍后重试
用户1001订单创建成功
Logo

汇聚全球AI编程工具,助力开发者即刻编程。

更多推荐