java ReentrantLock 设计的好处与缺点?与简单的synchronized关键字对比

ReentrantLock 是 Java 中用于实现锁机制的一种可重入锁。它具有一些优点和一些缺点,下面是关于ReentrantLock的主要优点和缺点:

优点:

  1. 可重入性(Reentrancy)ReentrantLock 支持锁的可重入性,也就是说同一个线程可以多次获得同一个锁,而不会导致死锁。这使得在复杂的程序中更容易实现正确的同步。

  2. 公平性(Fairness)ReentrantLock 可以选择是否使用公平性策略。在公平模式下,等待锁的线程会按照请求的顺序获得锁,而不会出现饥饿现象。这有助于避免某些线程永远无法获取锁的问题。

  3. 灵活性(Flexibility)ReentrantLock 提供了丰富的方法和选项,可以更灵活地控制锁的行为。例如,你可以设置锁的超时时间、中断等待锁的线程,以及尝试非阻塞地获取锁等。

  4. 性能(Performance):在某些情况下,ReentrantLock 可以比内置的synchronized关键字更好地支持高并发性能,因为它具有更细粒度的控制和更少的竞争。

缺点:

  1. 复杂性(Complexity):相对于简单的synchronized关键字,ReentrantLock 使用起来更复杂。需要手动管理锁的获取和释放,容易出现编程错误,如忘记释放锁或死锁。

  2. 资源占用(Resource Usage)ReentrantLock 使用的资源比synchronized多,因为每个锁都需要额外的对象来表示,这可能会导致内存占用增加。

  3. 性能变化(Performance Variability):虽然ReentrantLock 在某些情况下可以提供更好的性能,但在其他情况下,它的性能可能不如synchronized。性能取决于具体的应用场景和锁的使用方式。

总的来说,ReentrantLock 是一个强大且灵活的同步工具,它在某些情况下可以比synchronized更有优势,但需要谨慎使用,并且需要仔细考虑如何使用以确保线程安全。在选择使用它时,你应该考虑你的应用需求、复杂性和性能要求。

 

在大多数情况下,ReentrantLocksynchronized关键字在性能上没有明显的差异,因为它们都可以用于实现互斥访问。性能的主要差异通常来自于如何使用它们,以及锁的粒度。

以下是一些比较ReentrantLocksynchronized的关键差异和考虑因素:

  1. 灵活性:ReentrantLock提供了更多的灵活性。你可以使用它来实现更复杂的同步需求,如可中断锁、定时锁、公平锁等。它还允许你在不同的代码块中获得和释放锁,这可以对性能进行微调。与之相比,synchronized关键字较为简单,但功能相对受限。

  2. 锁粒度:性能的关键因素之一是锁的粒度。如果你在整个方法上使用synchronized,那么只有一个线程能够执行整个方法,这可能导致性能瓶颈。与之相比,ReentrantLock允许你更细粒度地控制锁的范围,这可以减小锁的竞争,提高性能。你可以在需要同步的代码块上获取和释放锁,而不是整个方法。

  3. 易用性:synchronized相对于ReentrantLock更容易使用,因为它是Java语言的一部分,无需导入额外的库,而且语法更简单。

  4. 分布式环境:如果你的应用需要在分布式环境中实现并发控制,ReentrantLock通常更适合,因为它可以很容易地扩展到分布式锁实现,如基于ZooKeeper或Redis的分布式锁。

总的来说,性能上的差异通常不大,但在具体情况下,你可能会发现一种方式比另一种方式更适合你的应用需求。要选择哪种方式,需要综合考虑你的应用场景、性能需求以及代码的复杂性。如果你的应用需求比较简单,而且性能不是主要关注点,那么synchronized可能更方便。如果你需要更高级的同步功能或更细粒度的控制,那么ReentrantLock可能更适合。

 
public class CodeService {
    private Lock lock1 = new ReentrantLock();
    private Lock lock2 = new ReentrantLock();
    private Lock lock3 = new ReentrantLock();

    public DResult updateSNO() {
        DResult dr = new DResult();
        try {
            lock1.lock(); // 获取锁
            // 具体更新逻辑
        } catch (Exception e) {
            // 异常处理
        } finally {
            lock1.unlock(); // 释放锁
        }
        return dr;
    }

    public DResult updateSNO1() {
        DResult dr = new DResult();
        try {
            lock2.lock(); // 获取锁
            // 具体更新逻辑
        } catch (Exception e) {
            // 异常处理
        } finally {
            lock2.unlock(); // 释放锁
        }
        return dr;
    }

    public DResult updateSNO2() {
        DResult dr = new DResult();
        try {
            lock3.lock(); // 获取锁
            // 具体更新逻辑
        } catch (Exception e) {
            // 异常处理
        } finally {
            lock3.unlock(); // 释放锁
        }
        return dr;
    }
}

这种方式允许每个方法在自己的独立锁上执行,不会相互阻塞,从而实现了并发执行。

以下方式同一线程只能执行一个 updateSNO 方法。这是因为你使用了一个共享的 ReentrantLock 锁实例 lock 来同步这三个方法,当一个线程获取了锁执行其中一个方法时,其他线程必须等待该线程释放锁才能执行其中任何一个方法。

如果你希望同一线程可以并发地执行这些方法,你可以为每个方法创建不同的锁实例,这样每个方法都有自己的独立锁,不会相互阻塞。应该用前一种方式

public class CodeService {
    private Lock lock= new ReentrantLock();

    public DResult updateSNO() {
        DResult dr = new DResult();
        try {
            lock1.lock(); // 获取锁
            // 具体更新逻辑
        } catch (Exception e) {
            // 异常处理
        } finally {
            lock1.unlock(); // 释放锁
        }
        return dr;
    }

    public DResult updateSNO1() {
        DResult dr = new DResult();
        try {
            lock.lock(); // 获取锁
            // 具体更新逻辑
        } catch (Exception e) {
            // 异常处理
        } finally {
            lock.unlock(); // 释放锁
        }
        return dr;
    }

    public DResult updateSNO2() {
        DResult dr = new DResult();
        try {
            lock.lock(); // 获取锁
            // 具体更新逻辑
        } catch (Exception e) {
            // 异常处理
        } finally {
            lock.unlock(); // 释放锁
        }
        return dr;
    }
}

 此时与如下的代码效果差不多了

public class CodeService {
    // 定义一个虚拟的锁对象
    private final Object codeDAOLock = new Object();

    public DResult updateSNO() {
        DResult dr = new DResult();
        try {
            // 锁定 codeDAOLock 对象,只锁定 codeDAO.updateSNO 方法的调用
            synchronized (codeDAOLock) {
                ....
            }
        } catch (Exception e) {
            dr.setContent("失败:出现意外错误," + e.toString());
            dr.setStatusCode(0);
        }
        return dr;
    }
}

 



posted @ 2023-09-16 11:09  锐洋智能  阅读(53)  评论(0编辑  收藏  举报