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

相关推荐
Mr.朱鹏1 天前
ShardingJDBC实战指南
java·jvm·数据库·spring·分库分表·shardingjdbc·shardingshere
学习OK呀1 天前
从 java8 升级 java17 的调整
java·后端
咔咔一顿操作1 天前
MySQL 事务管理与锁机制:解决并发场景下的数据一致性问题
java·数据库·mysql
莫克1 天前
resources\application.properties 配置大全
后端
渣哥1 天前
其实我不是很想和 Hashtable 说再见:一次跟“古董” HashMap 探险的的碎碎念
java
王中阳Go1 天前
go中的singleflight是如何实现的?
后端
AAA修煤气灶刘哥1 天前
缓存世界的三座大山:穿透、击穿、雪崩,今天就把它们铲平!
redis·分布式·后端
星梦清河1 天前
宋红康 JVM 笔记 Day16|垃圾回收相关概念
java·jvm·笔记
用户4099322502121 天前
需求驱动测试:你的代码真的在按需行事吗?
后端·ai编程·trae
双向331 天前
前后端接口调试提效:Postman + Mock Server 的工作流
后端