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 提供了更大的灵活性和控制能力。

相关推荐
ERP老兵_冷溪虎山5 分钟前
从ASCII到Unicode:"国际正则"|"表达式"跨国界实战指南(附四大语言支持对比+中医HIS类比映射表)
后端·面试
HyggeBest8 分钟前
Golang 并发原语 Sync Cond
后端·架构·go
老张聊数据集成12 分钟前
数据建模怎么做?一文讲清数据建模全流程
后端
S妖O风F18 分钟前
IDEA报JDK版本问题
java·ide·intellij-idea
Mr. Cao code21 分钟前
使用Tomcat Clustering和Redis Session Manager实现Session共享
java·linux·运维·redis·缓存·tomcat
纪莫23 分钟前
DDD领域驱动设计的理解
java·ddd领域驱动设计
颜如玉32 分钟前
Kernel bypass技术遥望
后端·性能优化·操作系统
一块plus1 小时前
创造 Solidity、提出 Web3 的他回来了!Gavin Wood 这次将带领波卡走向何处?
javascript·后端·面试
山中月侣1 小时前
Java多线程编程——基础篇
java·开发语言·经验分享·笔记·学习方法
用户298698530141 小时前
C#代码:Word文档加密与解密(Spire.Doc)
后端