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

<think>首先,用户的问题是基于之前的引用内容,但系统级指令要求我使用中文回答,并确保LaTeX格式正确。用户的问题是:“我想了解Javasynchronized关键字是否可重入以及递归调用是否会引发死锁 请问Java synchronized关键字是否支持可重入,递归调用是否会死锁” 从引用中,我可以看到: - 引用[1]:Javasynchronized 关键字支持重入性。通过使用内置的锁机制,synchronized 确保同一个线程可以多次获得同一把锁,从而允许在同一个锁保护下进行递归调用和方法嵌套。 - 引用[2]:synchronizedJava 中用于控制多个线程对共享资源访问的关键字。它通过提供互斥锁(mutex lock)机制,确保在同一时刻只有一个线程可以执行同步代码块或方法,从而避免了数据竞争问题。可重入性。 - 引用[3]:当synchronized用于方法时,它会将整个方法变为同步方法。这意味着在同一时间内,只有一个线程可以执行该方法。对于实例方法,锁是当前实例对象(this);对于静态方法,锁是当前类的Class对象。 - 引用[4]:在 Java 中提供了同步机制,可以有效地防止资源冲突。同步机制使用 synchronized 关键字。 - 引用[5]:一个代码示例展示了可重入锁的使用,其中同一个线程多次进入同步块。 所以,synchronized 关键字支持可重入性,这意味着同一个线程可以多次获得同一个锁,而不会导致死锁。递归调用不会引发死锁,因为线程已经持有锁,可以重新进入同步块。 我的回答需要: 1. 使用中文回答。 2. 结构清晰,帮助用户逐步解决问题。 3. 确保回答真实可靠,基于提供的引用。 4. 在回答中引用的段落末尾自然地添加引用标识,例如:[^1] 5. 生成相关问题(在最后)。 6. 对于数学表达式,使用 $...$ 或 $$...$$ 格式。但在这个问题中,可能没有数学表达式,所以我可以忽略这个部分,除非必要。 回答结构: - 先确认问题:用户想知道 synchronized 是否支持可重入,以及递归调用是否会死锁。 - 逐步解释: a. 解释 synchronized 的可重入性。 b. 解释为什么递归调用不会死锁。 c. 提供代码示例或引用。 - 最后添加相关问题。 生成相关问题: - 基于主题,如 Java 锁机制、可重入锁、死锁预防等。 相关问题示例: 1. 什么是可重入锁?它在 Java 中如何实现? 2. 除了 synchronizedJava 中还有哪些可重入锁机制? 3. 如何避免死锁在 Java 多线程编程中? 4. synchronizedReentrantLock区别是什么? 5. 可重入锁在递归场景中的应用。 现在,构建回答。 回答大纲: 1. 引言:直接回答问题。 2. 解释可重入性。 3. 解释递归调用和死锁。 4. 总结。 5. 相关问题。 确保引用:在引用内容的末尾添加引用标识。</think>### Java synchronized 关键字的可重入性及递归调用死锁分析 在 Java 中,`synchronized` 关键字是用于实现线程同步的核心机制,其设计支持 **可重入性(reentrancy)**,这意味着同一个线程可以多次获得同一把锁,而不会引发死锁。递归调用在 `synchronized` 保护下也不会导致死锁,因为线程在持有锁时可以安全地重新进入同步代码块或方法。下面我将逐步解释这一机制,确保回答真实可靠,基于 Java 官方文档和常见实践[^1][^2]。 #### 1. **synchronized 支持可重入性** - `synchronized` 通过内置的锁机制(称为 **监视器锁(monitor lock)**)实现可重入性。当一个线程首次获得锁时,JVM 会记录锁的持有者和计数器。当同一个线程再次请求同一把锁时,计数器会增加(而不是阻塞),允许线程多次进入同步区域[^1][^2]。 - **关键点**: - 可重入性避免了线程在递归或嵌套调用中自我阻塞。例如,在递归方法中,线程可以重复进入同一个同步块,而不会导致锁争用[^1]。 - 这种机制简化了代码设计,尤其在递归场景中,无需额外的锁管理[^2][^5]。 #### 2. **递归调用不会引发死锁的原因** - **死锁条件分析**:死锁通常需要四个条件同时满足(互斥、持有并等待、不可剥夺、循环等待)。但在 `synchronized` 的可重入机制下: - 同一个线程在递归调用中始终持有锁,因此不会出现“持有并等待”其他锁的情况。 - JVM 内部维护锁计数器:当线程退出最外层的同步块时,计数器才会归零,释放锁[^1][^5]。这确保了递归调用不会触发死锁。 - **代码示例**: 以下示例演示了 `synchronized` 在递归调用中的安全行为(基于引用[5]修改)。线程 `t1` 可以多次进入同一个锁保护的同步块,而不会死锁。 ```java public class ReentrantDemo { static Object lock = new Object(); // 共享锁对象 public static void main(String[] args) { new Thread(() -> { synchronized (lock) { // 第一次获得锁 System.out.println("外层同步块"); recursiveMethod(1); // 递归调用 } }, "t1").start(); } public static void recursiveMethod(int depth) { synchronized (lock) { // 同一个线程再次获得锁(可重入) System.out.println("递归深度: " + depth); if (depth < 3) { recursiveMethod(depth + 1); // 继续递归 } } } } ``` - **输出结果**: ``` 外层同步块 递归深度: 1 递归深度: 2 递归深度: 3 ``` - **分析**:线程 `t1` 在递归过程中多次获取 `lock` 锁,但不会死锁,因为 JVM 允许可重入[^5]。如果不可重入,线程会在第二次进入同步块时自我阻塞,导致死锁。 #### 3. **总结注意事项** - **支持可重入**:`synchronized` 是 Java 中默认的可重入锁,适用于递归、嵌套方法调用等场景,确保线程安全[^1][^2]。 - **死锁风险**:尽管递归调用本身不会死锁,但死锁可能发生在多个线程和多个锁的交互中(例如,线程 A 持有锁 X 并等待锁 Y,线程 B 持有锁 Y 并等待锁 X)。因此,在复杂同步设计中,建议: - 避免嵌套多个不同的锁。 - 使用 `ReentrantLock` 类(来自 `java.util.concurrent` 包)提供更灵活的锁控制,如超时机制[^3][^4]。 - **最佳实践**:在递归算法中使用 `synchronized` 是安全的,但需确保同步范围最小化,以减少性能开销[^3][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值