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

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

主要区别

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

应用场景

synchronized 适用场景:

  • 简单的同步需求

  • 代码块或方法级别的同步

  • 不需要高级功能的场景

  • 希望代码更简洁易读的情况

ReentrantLock 适用场景:

  • 需要可定时、可中断的锁获取

  • 需要公平锁机制

  • 需要多个条件变量

  • 需要尝试获取锁(tryLock)

  • 需要更细粒度的锁控制

注意事项

synchronized 注意事项:

  1. 自动释放锁,不会忘记释放

  2. 同步范围应尽量小,减少性能影响

  3. 避免嵌套同步导致的死锁

  4. 同步方法不能被继承重写

ReentrantLock 注意事项:

  1. 必须显式地在 finally 块中释放锁

  2. 不要将锁对象暴露给外部代码

  3. 注意锁的公平性选择对性能的影响

  4. 合理使用条件变量

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

基本用法对比

synchronized 基本用法

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 基本用法

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块中释放锁
        }
    }
}

高级特性对比示例

可中断锁获取

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的锁等待
    }
}

尝试获取锁与超时

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 非公平锁

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)

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();
    }
}

性能比较示例

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 注意事项

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 注意事项

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

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值