关键区别详解
1、等待集的个数与等待条件(最核心的区别)
1.1、这是两者最本质的区别,决定了它们在处理复杂同步问题时的能力。
synchronized:单一等待集
一个锁对象(monitor)只有一个隐式的等待队列。所有调用wait()的线程都在这个队列里等待。当调用notify()时,JVM会从整个等待集中随机唤醒一个线程;notifyAll()会唤醒所有等待线程。
问题:
如果因为不同的条件而等待(例如,生产者等待“队列非满”,消费者等待“队列非空”),它们都在同一个队列里。使用notifyAll()会唤醒所有等待的线程(包括生产者和消费者),但被唤醒的线程可能发现自己的等待条件仍未满足(例如,唤醒了生产者但队列还是满的),这会导致“虚假唤醒”或不必要的竞争,效率较低。
Lock+ Condition:多个等待集
一个 Lock可以创建多个 Condition对象(例如:Condition notEmpty = lock.newCondition(); Condition notFull = lock.newCondition();)。这样,可以将等待不同条件的线程放到不同的队列中。
优势:
生产者可以在notFull条件上等待(notFull.await()),消费者可以在notEmpty条件上等待(notEmpty.await())。当生产者生产了一个数据后,它只需要调用notEmpty.signal()来精确唤醒一个在“队列非空”条件上等待的消费者线程,而不会误唤醒其他生产者。这大大提高了效率和精确度。
1.2、功能特性
synchronized:
简单易用:语法简洁,不需要手动释放锁,JVM 会保证锁的释放,避免了死锁的可能性(在代码块结束时自动释放)。
功能有限:不支持尝试获取锁、超时获取锁、中断正在等待获取锁的线程。
Lock+ Condition:
功能丰富:
尝试获取锁:tryLock(),获取不到立即返回false,不会阻塞。
超时获取锁:tryLock(long time, TimeUnit unit),在指定时间内获取不到则返回。
可中断的锁获取:lockInterruptibly(),在等待锁的过程中可以响应中断。
需要手动管理:必须在 finally块中手动调用 unlock()来释放锁,否则容易导致死锁。
性能
在早期版本中,synchronized是重量级锁,性能远差于Lock。但经过JDK 的不断优化(如偏向锁、轻量级锁、自旋锁等),现在两者的性能差距已经非常小,甚至在很多场景下synchronized性能更优。
因此,性能不再是选择的主要依据。选择的关键在于功能的灵活性和对复杂场景的建模能力。
如何选择?
优先使用 synchronized:
对于大多数简单的同步场景。
当你需要简单、不易出错的代码(自动释放锁)。
在性能不是极端敏感的情况下。
考虑使用 Lock+ Condition:
当需要实现复杂的同步逻辑时,例如有界队列、复杂的资源池管理等。
当需要多个条件谓词(多个等待集)来实现更精细的线程通知时。
当需要尝试获取锁、超时、可中断等高级特性时。
一句话总结:synchronized是“瑞士军刀”,简单通用;而 Lock+ Condition是“专业工具套装”,功能强大且灵活,用于解决更复杂的问题。
526

被折叠的 条评论
为什么被折叠?



