Java中的锁是一种同步机制,用于控制多个线程对共享资源的访问,避免数据竞争造成的错误。Java提供了多种锁,下面将介绍Java中五种常用的锁及其应用场景。
一、synchronized锁
synchronized是Java中最基本的一种锁。它是通过对方法或代码块加锁的方式,实现对共享资源的访问控制。
使用synchronized锁时,需要注意以下几点:
- synchronized锁只能保证单个线程对共享资源的访问,不能保证在多线程条件下的访问。
- 使用synchronized锁时,必须在同一线程内去获取锁和释放锁,否则会引起死锁。
- synchronized锁在性能上有影响,因为需要进行锁的获取和释放操作,会引起线程上下文切换导致性能下降。
以下是一个使用synchronized锁的示例代码:
public class SynchronizedLockDemo {
private int count = 0;
public synchronized void increment() {
count++;
}
public synchronized int getCount() {
return count;
}
}
二、ReentrantLock锁
ReentrantLock是一个可重入锁,它可以更灵活地控制共享资源的访问。与synchronized锁不同的是,ReentrantLock可以在不同的线程中进行获取和释放锁的操作。
使用ReentrantLock锁时,需要注意以下几点:
- 使用ReentrantLock必须先获取锁再进行操作,操作完成后需要释放锁,否则会引起死锁。
- 使用ReentrantLock时需要保证加锁和解锁是在try-finally代码块中,防止出现异常时无法释放锁的情况。
- ReentrantLock比synchronized锁更灵活,可以根据需要进行公平或非公平的锁的分配。
以下是一个使用ReentrantLock锁的示例代码:
public class ReentrantLockDemo {
private ReentrantLock lock = new ReentrantLock();
private int count = 0;
public void increment() {
lock.lock();
try {
count++;
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
return count;
} finally {
lock.unlock();
}
}
}
三、ReadWriteLock锁
ReadWriteLock锁是一种特殊的锁,它支持对共享资源的读写分离。ReadWriteLock提供了读锁和写锁两种不同的锁,读锁用于支持多个线程同时读取共享资源,写锁用于控制对共享资源的独占访问。
使用ReadWriteLock锁时,需要注意以下几点:
- 当存在多个线程同时读取同一共享资源时,可以使用读锁进行访问,这可以提高并发效率。
- 当需要写入共享资源时,需要获取写锁。
- 读锁可以共享,写锁是互斥的。
以下是一个使用ReadWriteLock锁的示例代码:
public class ReadWriteLockDemo {
private ReadWriteLock lock = new ReentrantReadWriteLock();
private int count = 0;
public void increment() {
lock.writeLock().lock();
try {
count++;
} finally {
lock.writeLock().unlock();
}
}
public int getCount() {
lock.readLock().lock();
try {
return count;
} finally {
lock.readLock().unlock();
}
}
}
四、StampedLock锁
StampedLock锁是一种乐观锁,它可以实现比ReadWriteLock更高效的读写分离。StampedLock锁的思想是在读的过程中也允许写入操作,只要写入时不会改变数据结构的完整性即可。
使用StampedLock锁时,需要注意以下几点:
- 使用StampedLock锁的时候,要求共享资源必须是基于内部对象实现的。
- StampedLock锁提供了读锁、写锁和乐观读锁三种不同的锁。
- 使用StampedLock锁时需要调用tryOptimisticRead方法获取乐观读锁,并且每次获取乐观读锁之后需要验证版本号是否发生变化,如果发生变化需要重新获取读锁。
以下是一个使用StampedLock锁的示例代码:
public class StampedLockDemo {
private StampedLock lock = new StampedLock();
private int count = 0;
public void increment() {
long stamp = lock.writeLock();
try {
count++;
} finally {
lock.unlockWrite(stamp);
}
}
public int getCount() {
long stamp = lock.tryOptimisticRead();
int c = count;
if (!lock.validate(stamp)) {
stamp = lock.readLock();
try {
c = count;
} finally {
lock.unlockRead(stamp);
}
}
return c;
}
}
五、Condition锁
Condition锁是Java中一种高级锁,它可以让线程在等待某个条件成立时,自动地释放锁并进入等待状态,等待条件成立后自动唤醒线程重新获取锁。
使用Condition锁时,需要注意以下几点:
- 使用Condition锁时需要先调用Lock().newCondition()方法获取Condition对象。
- 使用Condition锁需要在获取锁的前提下进行,否则会抛IllegalMonitorStateException异常。
以下是一个使用Condition锁的示例代码:
public class ConditionDemo {
private Lock lock = new ReentrantLock();
private Condition condition = lock.newCondition();
private int count = 0;
public void increment() {
lock.lock();
try {
while (count == 10) {
condition.await();
}
count++;
condition.signalAll();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public int getCount() {
lock.lock();
try {
while (count == 0) {
condition.await();
}
int c = count--;
condition.signalAll();
return c;
} catch (InterruptedException e) {
e.printStackTrace();
return -1;
} finally {
lock.unlock();
}
}
}
以上是Java中常用的五种锁及其应用场景的简介,使用不同的锁可以根据实际业务需求进行选择,提高程序的并发性能。在使用锁的过程中,需要根据实际情况选择最适合的锁和锁的级别,避免出现死锁等问题。