AbstractQueuedSynchronizer(AQS)提供了两种类型的锁:独占锁和共享锁。
-
独占锁(Exclusive Lock):
- 独占锁模式只允许一个线程同时获取锁。当一个线程获取了独占锁时,其他线程必须等待该线程释放锁之后才能获取。
- 在AQS中,独占锁通过实现
tryAcquire
和tryRelease
方法来控制锁的获取和释放过程。通常,这些方法会被子类覆盖以提供特定的锁语义。 - 例如,
ReentrantLock
就是一个基于AQS实现的独占锁,它允许线程在重入时多次获取同一把锁。
-
共享锁(Shared Lock):
- 共享锁模式允许多个线程同时获取锁,以便并发访问共享资源。然而,在某些情况下,共享锁可能会导致饥饿或死锁。
- 在AQS中,共享锁通过实现
tryAcquireShared
和tryReleaseShared
方法来控制锁的获取和释放过程。这些方法也可以被子类覆盖以提供自定义的共享锁语义。 - 例如,
ReadWriteLock
就是基于AQS实现的一个常见的共享锁,它允许多个线程同时获取读锁,但只允许一个线程获取写锁。
ReentrantLock
下面是一个使用 ReentrantLock
的简单示例:
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockExample {private final Lock lock = new ReentrantLock();private int count = 0;public void increment() {lock.lock(); // 获取锁try {count++; // 对共享变量进行操作} finally {lock.unlock(); // 释放锁}}public int getCount() {return count;}public static void main(String[] args) {ReentrantLockExample example = new ReentrantLockExample();// 创建多个线程对共享变量进行操作Runnable task = () -> {for (int i = 0; i < 1000; i++) {example.increment();}};Thread thread1 = new Thread(task);Thread thread2 = new Thread(task);thread1.start();thread2.start();try {thread1.join();thread2.join();} catch (InterruptedException e) {e.printStackTrace();}// 输出最终的共享变量值System.out.println("Final count: " + example.getCount());}
}
在这个例子中,ReentrantLockExample
类包含一个 ReentrantLock
对象和一个共享变量 count
。increment()
方法用于对 count
变量执行递增操作,其中使用 lock()
方法获取锁,执行完操作后使用 unlock()
方法释放锁。在 main
方法中,创建了两个线程分别执行 increment()
方法,最后输出 count
变量的最终值。
使用 ReentrantLock
的优点之一是它支持重入性,允许同一线程在持有锁的情况下多次获取该锁,避免了死锁的发生。
ReadWriteLock
下面是一个使用 ReadWriteLock
的简单示例:
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockExample {private final ReadWriteLock lock = new ReentrantReadWriteLock();private int data = 0;public void writeData(int newData) {lock.writeLock().lock(); // 获取写锁try {data = newData; // 写入新数据System.out.println("Write data: " + newData);} finally {lock.writeLock().unlock(); // 释放写锁}}public int readData() {lock.readLock().lock(); // 获取读锁try {System.out.println("Read data: " + data); // 读取数据return data;} finally {lock.readLock().unlock(); // 释放读锁}}public static void main(String[] args) {ReadWriteLockExample example = new ReadWriteLockExample();// 创建多个线程读取数据Runnable readTask = () -> {for (int i = 0; i < 5; i++) {example.readData();}};// 创建一个线程写入数据Runnable writeTask = () -> {for (int i = 1; i <= 5; i++) {example.writeData(i);try {Thread.sleep(1000); // 休眠1秒} catch (InterruptedException e) {e.printStackTrace();}}};// 启动读线程和写线程Thread reader1 = new Thread(readTask);Thread reader2 = new Thread(readTask);Thread writer = new Thread(writeTask);reader1.start();reader2.start();writer.start();}
}
在这个例子中,ReadWriteLockExample
类包含一个 ReadWriteLock
对象和一个共享变量 data
。writeData()
方法用于写入新数据,其中使用 writeLock()
方法获取写锁,执行完写操作后释放写锁。readData()
方法用于读取数据,其中使用 readLock()
方法获取读锁,读取完数据后释放读锁。
在 main
方法中,创建了两个读线程和一个写线程,分别执行读取和写入操作。读线程可以同时持有读锁,但写线程必须独占写锁,以确保写操作的原子性和一致性。
这个例子展示了 ReadWriteLock
的特性,读线程可以并发地访问共享数据,而写线程在执行写操作时会独占资源,以避免读写操作之间的竞争和冲突。