Java中有以下几种锁的实现方式:
- synchronized关键字:synchronized是Java语言内置的一种锁机制,用于实现线程同步,防止多个线程同时访问同一代码块或方法。synchronized可以用来修饰方法、代码块或静态方法,保证它们在同一时刻只有一个线程能够执行。
- ReentrantLock类:ReentrantLock是Java.util.concurrent包中提供的一种可重入的互斥锁。它比synchronized关键字更加灵活,可以实现公平锁或非公平锁,支持可中断的锁等特性。ReentrantLock也可以用来修饰代码块或方法。
- ReadWriteLock类:ReadWriteLock是Java.util.concurrent包中提供的读写锁机制。它允许多个线程同时读取共享数据,但在写入数据时必须互斥。ReadWriteLock包含一个读锁和一个写锁,读锁可以被多个线程同时持有,但写锁只能被一个线程持有。
- StampedLock类:StampedLock是Java.util.concurrent包中提供的一种乐观锁机制。它比ReadWriteLock更加灵活和高效,在某些场景下可以替代ReentrantLock和ReadWriteLock。StampedLock包含三种模式:读模式、写模式和乐观读模式,它们之间互斥,但乐观读模式不会阻塞写线程,因此效率更高。
在选择锁机制时,需要考虑到线程安全、并发度、性能和可维护性等因素。以下是一些示例,说明了如何选择不同的锁机制。
- 对于并发度较低的情况,可以使用synchronized关键字,它简单易用,能够满足大部分的同步需求。例如:
public synchronized void increment() {
count++;
}
- 对于并发度较高、线程等待时间较长的情况,可以选择ReentrantLock,它可以实现公平锁或非公平锁,支持可中断的锁等特性。例如:
private ReentrantLock lock = new ReentrantLock();
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
- 对于读多写少的情况,可以选择ReadWriteLock,它允许多个线程同时读取共享数据,但在写入数据时必须互斥。例如:
private ReadWriteLock lock = new ReentrantReadWriteLock();
public void read() {
lock.readLock().lock();
try {
// 读取共享数据
} finally {
lock.readLock().unlock();
}
}
public void write() {
lock.writeLock().lock();
try {
// 写入共享数据
} finally {
lock.writeLock().unlock();
}
}
- 对于高性能、低竞争的情况,可以选择StampedLock,它比ReentrantLock和ReadWriteLock更加高效。例如
private StampedLock lock = new StampedLock();
public void read() {
long stamp = lock.tryOptimisticRead();
// 读取共享数据
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
// 重新读取共享数据
} finally {
lock.unlockRead(stamp);
}
}
}
public void write() {
long stamp = lock.writeLock();
try {
// 写入共享数据
} finally {
lock.unlockWrite(stamp);
}
}
不同的锁机制在不同的场景下具有不同的性能表现,因此很难说哪种锁机制的效率最好。以下是一些常见的锁机制的性能特点:
- synchronized:在Java 6及以前的版本中,synchronized的性能比较低;而在Java 6及以后的版本中,通过一系列的优化,synchronized的性能得到了很大的提升。当然,synchronized的性能仍然比不上其他一些高级的锁机制。
- ReentrantLock:ReentrantLock的性能比synchronized略高,在并发度较高的情况下效果更加明显。但是,ReentrantLock的使用比较复杂,需要手动加锁和解锁,并且要注意锁的释放问题。
- ReadWriteLock:在读多写少的情况下,ReadWriteLock的性能比较好,因为它允许多个线程同时读取共享数据。但是,在写入数据时需要互斥,因此写操作的性能会受到影响。
- StampedLock:StampedLock是Java 8中新增的一种锁机制,它比ReentrantLock和ReadWriteLock更加高效。StampedLock的优势在于它提供了乐观锁和悲观锁两种模式,可以根据实际需求进行选择。
synchronized因为是java保留字,相对于其他锁机制是在java语言层面实现的锁机制,随着java版本升级,其有用更多性能提升的可能性。比如在Java 6中,对synchronized进行了一些优化,主要包括以下几个方面:
- 轻量级锁(Lightweight Locking):在竞争不激烈的情况下,synchronized会使用轻量级锁代替传统的重量级锁,以减小锁的粒度,提高并发性能。
- 自适应自旋锁(Adaptive Spinning):在竞争激烈的情况下,synchronized会自适应地使用自旋锁,以避免线程因等待锁而被阻塞,从而提高并发性能。
- 锁消除(Lock Elimination):当编译器发现某些代码块不存在竞争条件时,会将其中的锁自动消除,以避免不必要的锁竞争。
- 偏向锁(Biased Locking):在锁对象一直被同一线程持有的情况下,会自动将该锁对象转换为偏向锁,以避免多次加锁解锁操作,从而提高并发性能。
这些优化使得synchronized的性能得到了很大的提升,从而使得synchronized成为Java中最常用的锁机制之一。