java中,synchronized 关键字与 ReentrantLock 重入锁的区别以及应用场景,注意事项

在 Java 并发编程中,synchronized 关键字和 ReentrantLock 都是用于实现线程同步的重要机制,但它们在实现方式、功能和性能特征上有所不同。

主要区别

特性 synchronized ReentrantLock
实现方式 Java 语言关键字,JVM 级别实现 JDK 提供的类,API 级别实现
锁获取方式 隐式获取和释放锁 显式获取和释放锁
灵活性 相对简单,功能有限 更灵活,提供更多功能
可中断性 不可中断等待 支持可中断的锁获取
公平性 非公平锁 可选择公平或非公平锁
条件变量 单一等待条件 支持多个条件变量
性能 Java 6+ 优化后性能相当 在高竞争环境下可能略有优势

应用场景

synchronized 适用场景:

  • 简单的同步需求
  • 代码块或方法级别的同步
  • 不需要高级功能的场景
  • 希望代码更简洁易读的情况

ReentrantLock 适用场景:

  • 需要可定时、可中断的锁获取
  • 需要公平锁机制
  • 需要多个条件变量
  • 需要尝试获取锁(tryLock)
  • 需要更细粒度的锁控制

注意事项

synchronized 注意事项:

  1. 自动释放锁,不会忘记释放
  2. 同步范围应尽量小,减少性能影响
  3. 避免嵌套同步导致的死锁
  4. 同步方法不能被继承重写

ReentrantLock 注意事项:

  1. 必须显式地在 finally 块中释放锁
  2. 不要将锁对象暴露给外部代码
  3. 注意锁的公平性选择对性能的影响
  4. 合理使用条件变量

下面通过更详细的代码示例来说明两者的区别、应用场景和注意事项。

基本用法对比

synchronized 基本用法

csharp 复制代码
public class SynchronizedExample {
    private int count = 0;
    private final Object lock = new Object();
    
    // 同步方法
    public synchronized void increment() {
        count++;
    }
    
    // 同步代码块
    public void incrementWithBlock() {
        synchronized (lock) {
            count++;
        }
    }
    
    // 静态同步方法
    public static synchronized void staticIncrement() {
        // 类级别的同步
    }
}

ReentrantLock 基本用法

csharp 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockExample {
    private int count = 0;
    private final ReentrantLock lock = new ReentrantLock();
    
    public void increment() {
        lock.lock(); // 获取锁
        try {
            count++;
        } finally {
            lock.unlock(); // 必须在finally块中释放锁
        }
    }
}

高级特性对比示例

可中断锁获取

java 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class InterruptibleLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void performTask() {
        try {
            // 可中断的锁获取
            lock.lockInterruptibly();
            try {
                // 执行需要同步的操作
                System.out.println("Task started by " + Thread.currentThread().getName());
                Thread.sleep(5000); // 模拟长时间操作
                System.out.println("Task completed by " + Thread.currentThread().getName());
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for lock");
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        InterruptibleLockExample example = new InterruptibleLockExample();
        
        Thread t1 = new Thread(example::performTask, "Thread-1");
        Thread t2 = new Thread(example::performTask, "Thread-2");
        
        t1.start();
        Thread.sleep(100); // 确保t1先获取锁
        
        t2.start();
        Thread.sleep(1000); // 让t2等待一会儿
        
        t2.interrupt(); // 中断t2的锁等待
    }
}

尝试获取锁与超时

csharp 复制代码
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

public class TryLockExample {
    private final ReentrantLock lock = new ReentrantLock();
    
    public void tryLockMethod() {
        // 尝试获取锁,立即返回结果
        if (lock.tryLock()) {
            try {
                System.out.println(Thread.currentThread().getName() + " acquired the lock");
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " could not acquire the lock");
        }
    }
    
    public void tryLockWithTimeout() {
        try {
            // 尝试获取锁,最多等待2秒
            if (lock.tryLock(2, TimeUnit.SECONDS)) {
                try {
                    System.out.println(Thread.currentThread().getName() + " acquired the lock with timeout");
                    Thread.sleep(3000); // 持有锁3秒
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " timed out waiting for lock");
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " was interrupted while waiting for lock");
        }
    }
    
    public static void main(String[] args) {
        TryLockExample example = new TryLockExample();
        
        // 测试tryLock()
        new Thread(example::tryLockMethod, "Thread-1").start();
        new Thread(example::tryLockMethod, "Thread-2").start();
        
        try { Thread.sleep(3000); } catch (InterruptedException e) {}
        
        // 测试带超时的tryLock()
        new Thread(example::tryLockWithTimeout, "Thread-3").start();
        new Thread(example::tryLockWithTimeout, "Thread-4").start();
    }
}

公平锁 vs 非公平锁

csharp 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class FairLockExample {
    private final ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
    private final ReentrantLock unfairLock = new ReentrantLock(false); // 非公平锁
    
    public void testFairLock() {
        for (int i = 0; i < 5; i++) {
            fairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " acquired fair lock");
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                fairLock.unlock();
            }
        }
    }
    
    public void testUnfairLock() {
        for (int i = 0; i < 5; i++) {
            unfairLock.lock();
            try {
                System.out.println(Thread.currentThread().getName() + " acquired unfair lock");
                Thread.sleep(100);
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            } finally {
                unfairLock.unlock();
            }
        }
    }
    
    public static void main(String[] args) {
        FairLockExample example = new FairLockExample();
        
        System.out.println("Testing fair lock:");
        for (int i = 0; i < 3; i++) {
            new Thread(example::testFairLock, "Fair-Thread-" + i).start();
        }
        
        try { Thread.sleep(3000); } catch (InterruptedException e) {}
        
        System.out.println("\nTesting unfair lock:");
        for (int i = 0; i < 3; i++) {
            new Thread(example::testUnfairLock, "Unfair-Thread-" + i).start();
        }
    }
}

条件变量 (Condition)

ini 复制代码
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ConditionExample {
    private final ReentrantLock lock = new ReentrantLock();
    private final Condition notFull = lock.newCondition();
    private final Condition notEmpty = lock.newCondition();
    
    private final Object[] items = new Object[5];
    private int putPtr, takePtr, count;
    
    public void put(Object x) throws InterruptedException {
        lock.lock();
        try {
            while (count == items.length) {
                System.out.println("Buffer is full, waiting...");
                notFull.await(); // 等待"不满"条件
            }
            
            items[putPtr] = x;
            if (++putPtr == items.length) putPtr = 0;
            ++count;
            System.out.println("Produced: " + x);
            
            notEmpty.signal(); // 通知"不空"条件
        } finally {
            lock.unlock();
        }
    }
    
    public Object take() throws InterruptedException {
        lock.lock();
        try {
            while (count == 0) {
                System.out.println("Buffer is empty, waiting...");
                notEmpty.await(); // 等待"不空"条件
            }
            
            Object x = items[takePtr];
            if (++takePtr == items.length) takePtr = 0;
            --count;
            System.out.println("Consumed: " + x);
            
            notFull.signal(); // 通知"不满"条件
            return x;
        } finally {
            lock.unlock();
        }
    }
    
    public static void main(String[] args) {
        ConditionExample example = new ConditionExample();
        
        // 生产者线程
        Thread producer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    example.put(i);
                    Thread.sleep(200);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        // 消费者线程
        Thread consumer = new Thread(() -> {
            try {
                for (int i = 0; i < 10; i++) {
                    example.take();
                    Thread.sleep(500);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        });
        
        producer.start();
        consumer.start();
    }
}

性能比较示例

ini 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class PerformanceComparison {
    private int counter = 0;
    private final Object syncLock = new Object();
    private final ReentrantLock reentrantLock = new ReentrantLock();
    
    public void incrementWithSynchronized() {
        synchronized (syncLock) {
            counter++;
        }
    }
    
    public void incrementWithReentrantLock() {
        reentrantLock.lock();
        try {
            counter++;
        } finally {
            reentrantLock.unlock();
        }
    }
    
    public static void main(String[] args) throws InterruptedException {
        PerformanceComparison example = new PerformanceComparison();
        int numThreads = 10;
        int iterations = 1000000;
        
        // 测试synchronized性能
        long startTime = System.currentTimeMillis();
        Thread[] syncThreads = new Thread[numThreads];
        for (int i = 0; i < numThreads; i++) {
            syncThreads[i] = new Thread(() -> {
                for (int j = 0; j < iterations; j++) {
                    example.incrementWithSynchronized();
                }
            });
            syncThreads[i].start();
        }
        
        for (Thread t : syncThreads) {
            t.join();
        }
        long syncTime = System.currentTimeMillis() - startTime;
        
        // 重置计数器
        example.counter = 0;
        
        // 测试ReentrantLock性能
        startTime = System.currentTimeMillis();
        Thread[] lockThreads = new Thread[numThreads];
        for (int i = 0; i < numThreads; i++) {
            lockThreads[i] = new Thread(() -> {
                for (int j = 0; j < iterations; j++) {
                    example.incrementWithReentrantLock();
                }
            });
            lockThreads[i].start();
        }
        
        for (Thread t : lockThreads) {
            t.join();
        }
        long lockTime = System.currentTimeMillis() - startTime;
        
        System.out.println("Synchronized time: " + syncTime + "ms");
        System.out.println("ReentrantLock time: " + lockTime + "ms");
        System.out.println("Final counter value: " + example.counter);
    }
}

注意事项与最佳实践

synchronized 注意事项

java 复制代码
public class SynchronizedPitfalls {
    private final Object lock1 = new Object();
    private final Object lock2 = new Object();
    
    // 潜在的死锁问题
    public void method1() {
        synchronized (lock1) {
            // 一些操作...
            method2(); // 可能造成死锁
        }
    }
    
    public void method2() {
        synchronized (lock2) {
            // 一些操作...
            method1(); // 可能造成死锁
        }
    }
    
    // 更好的设计 - 避免嵌套锁
    public void betterMethod1() {
        synchronized (lock1) {
            // 只操作与lock1相关的资源
        }
        // 在锁外调用其他方法
        betterMethod2();
    }
    
    public void betterMethod2() {
        synchronized (lock2) {
            // 只操作与lock2相关的资源
        }
    }
}

ReentrantLock 注意事项

csharp 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockPitfalls {
    private final ReentrantLock lock = new ReentrantLock();
    
    // 错误示例 - 可能忘记释放锁
    public void riskyMethod() {
        lock.lock();
        // 如果这里抛出异常,锁永远不会被释放
        // 应该使用try-finally结构
        // lock.unlock(); // 可能永远不会执行
    }
    
    // 正确示例 - 使用try-finally确保锁释放
    public void safeMethod() {
        lock.lock();
        try {
            // 受保护的代码
        } finally {
            lock.unlock();
        }
    }
    
    // 避免在持有锁时调用外部方法
    public void problematicMethod() {
        lock.lock();
        try {
            externalMethod(); // 危险!外部方法可能执行很长时间或尝试获取其他锁
        } finally {
            lock.unlock();
        }
    }
    
    private void externalMethod() {
        // 可能执行很长时间或尝试获取其他锁
    }
    
    // 更好的设计 - 最小化锁内代码
    public void betterMethod() {
        // 在锁外执行尽可能多的操作
        Object data = prepareData();
        
        lock.lock();
        try {
            // 只执行必须同步的最小操作
            updateSharedState(data);
        } finally {
            lock.unlock();
        }
    }
    
    private Object prepareData() {
        return new Object();
    }
    
    private void updateSharedState(Object data) {
        // 更新共享状态
    }
}

在实际开发中,大多数情况下 synchronized 已经足够且更安全,因为它自动管理锁的释放。但当需要更高级的同步功能时,ReentrantLock 提供了更大的灵活性和控制能力。

相关推荐
2401_841495647 分钟前
【数据结构】红黑树的基本操作
java·数据结构·c++·python·算法·红黑树·二叉搜索树
学编程的小鬼14 分钟前
SpringBoot 自动装配原理剖析
java·spring boot·后端
码事漫谈42 分钟前
SIMD编程入门:让性能飞起来的实践指南
后端
码事漫谈44 分钟前
从汇编角度看C++优化:编译器真正做了什么
后端
@@神农2 小时前
maven的概述以及在mac安装配置
java·macos·maven
杜子不疼.2 小时前
【C++】玩转模板:进阶之路
java·开发语言·c++
夜晚中的人海2 小时前
【C++】异常介绍
android·java·c++
老葱头蒸鸡2 小时前
(28)ASP.NET Core8.0 SOLID原则
后端·asp.net
Le1Yu2 小时前
2025-9-28学习笔记
java·笔记·学习
C++chaofan2 小时前
项目中为AI添加对话记忆
java·数据结构·人工智能·redis·缓存·个人开发·caffeine