实现类
LockSupport
这是一个实用工具类,提供基本的线程阻塞原语,如park()和unpark(),用于创建自定义的同步机制。
类的方法如下:
- park(): 阻塞当前线程直到它被唤醒。
- parkUntil(long): 阻塞当前线程直到指定的绝对时间(毫秒)。
- parkUntil(Object, long): 阻塞当前线程直到指定的条件变量状态发生变化或达到指定的绝对时间。
- parkNanos(Object, long): 阻塞当前线程直到指定的条件变量状态发生变化或达到指定的纳秒数。
- getBlocker(Thread): 返回指定线程的阻塞对象。
- setBlocker(Thread, Object): 设置指定线程的阻塞对象。
- nextSecondarySeed(): 返回下一个次要种子值。
- parkNanos(long): 阻塞当前线程直到指定的纳秒数。
- park(Object): 阻塞当前线程直到指定的条件变量状态发生变化。
- unpark(Thread?): 唤醒一个正在等待的线程。
使用场景
使用场景主要包括以下几种:
- 线程同步:当一个线程需要等待另一个线程完成某个操作时,可以使用 LockSupport 的 park() 方法将当前线程阻塞,等待其他线程唤醒。
- 实现自定义同步组件:例如,可以实现一个简单的生产者-消费者模型,其中生产者线程在生产数据后调用 park() 阻塞,消费者线程在消费数据后调用 unpark(Thread) 唤醒生产者线程。
- 实现定时任务:可以使用 LockSupport 的 parkNanos() 或 parkUntil() 方法实现线程的延时执行。
示例:
import java.util.concurrent.locks.LockSupport;
public class LockSupportExample {
public static void main(String[] args) {
Thread producer = new Thread(() -> {
System.out.println("生产者开始生产...");
// 模拟生产数据
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("生产者生产完毕,准备唤醒消费者...");
LockSupport.unpark(consumer); // 唤醒消费者线程
});
Thread consumer = new Thread(() -> {
System.out.println("消费者等待生产者生产...");
LockSupport.park(); // 阻塞消费者线程,等待生产者唤醒
System.out.println("消费者收到通知,开始消费...");
});
consumer.start(); // 先启动消费者线程
producer.start(); // 然后启动生产者线程
}
}
在这个示例中,生产者线程在生产完数据后调用 LockSupport.unpark(consumer)
唤醒消费者线程,消费者线程在启动时调用 LockSupport.park()
将自己阻塞,等待生产者线程唤醒。
ReentrantLock
Lock接口的一个实现,它允许单个线程多次获得锁,是一个可重入的互斥锁。
类的方法如下:
-
isLocked(): boolean
- 返回此锁是否被线程持有。 -
lockInterruptibly(): void
- 获取锁,但如果线程在等待获取锁的过程中被中断,将抛出InterruptedException。 -
hasWaiters(Condition): boolean
- 返回给定条件的等待集中是否有任何等待的线程。 -
getWaitQueueLength(Condition): int
- 返回等待在给定条件的等待集中的线程数。 -
hasQueuedThreads(): boolean
- 返回是否有线程等待获取此锁。 -
hasQueuedThread(Thread): boolean
- 返回给定线程是否在等待获取此锁的队列中。 -
getHoldCount(): int
- 返回当前线程在此锁上重复获取的次数。 -
toString(): String
- 返回此锁的字符串表示形式。 -
getQueueLength(): int
- 返回等待获取此锁的线程数。 -
lock(): void
- 获取锁,如果锁不可用,使当前线程等待。 -
newCondition(): Condition
- 创建并返回一个新的Condition实例,该实例绑定到此锁。 -
isFair(): boolean
- 返回此锁是否为公平锁。 -
getWaitingThreads(Condition): Collection<Thread>
- 返回正在等待给定条件的线程。 -
tryLock(): boolean
- 尝试获取锁,仅在调用时锁是空闲的才获取锁。 -
getQueuedThreads(): Collection<Thread>
- 返回一个集合,它包含可能正在等待获取此锁的所有线程。 -
tryLock(long, TimeUnit): boolean
- 尝试在给定的等待时间内获取锁。 -
getOwner(): Thread
- 返回持有此锁的线程,如果没有线程持有则返回null。 -
unlock(): void
- 释放锁。 -
isHeldByCurrentThread(): boolean
- 返回当前线程是否持有此锁。
使用场景
使用场景:
- 需要更细粒度的锁定控制,例如可中断的锁定、公平锁和非公平锁等。
- 需要实现多个条件变量的同步,而不仅仅是一个。
- 需要在锁定期间执行一些操作,然后释放锁,再重新获取锁。
- 需要支持锁的尝试获取,以避免死锁。
示例:
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class ReentrantLockExample {
private final ReentrantLock lock = new ReentrantLock();
private final Condition condition = lock.newCondition();
private int count = 0;
public void increment() {
lock.lock(); // 获取锁
try {
count++;
System.out.println("Count: " + count);
condition.signalAll(); // 唤醒所有等待的线程
} finally {
lock.unlock(); // 释放锁
}
}
public void decrement() {
lock.lock(); // 获取锁
try {
count--;
System.out.println("Count: " + count);
condition.signalAll(); // 唤醒所有等待的线程
} finally {
lock.unlock(); // 释放锁
}
}
public void waitForZero() throws InterruptedException {
lock.lock(); // 获取锁
try {
while (count != 0) {
condition.await(); // 等待直到 count 为 0
}
System.out.println("Count is zero");
} finally {
lock.unlock(); // 释放锁
}
}
public static void main(String[] args) {
ReentrantLockExample example = new ReentrantLockExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.decrement();
}
});
Thread t3 = new Thread(() -> {
try {
example.waitForZero();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
t1.start();
t2.start();
t3.start();
}
}
在这个示例中,我们使用了 ReentrantLock 来保护对共享资源(count)的访问。我们创建了三个线程,分别执行递增、递减和等待计数器为零的操作。通过使用 ReentrantLock 和 Condition,我们可以确保在多线程环境下对这些操作的正确同步。
ReentrantReadWriteLock
ReadWriteLock接口的一个实现,支持可重入的读锁和写锁功能。
类的方法如下:
getThreadId(Thread): long
- 获取指定线程的ID。getReadLockCount(): int
- 获取当前持有读锁的线程数量。getQueuedReaderThreads(): Collection<Thread>
- 返回等待获取读锁的线程集合。getQueuedThreads(): Collection<Thread>
- 返回等待获取任何锁(读或写)的线程集合。hasQueuedThreads(): boolean
- 是否有线程在等待获取锁。getWaitQueueLength(Condition): int
- 返回给定条件的等待队列中的线程数。writeLock(): WriteLock
- 获取写锁。getWriteHoldCount(): int
- 获取当前持有写锁的线程数量。hasWaiters(Condition): boolean
- 返回给定条件是否有等待的线程。getQueuedWriterThreads(): Collection<Thread>
- 返回等待获取写锁的线程集合。isWriteLockedByCurrentThread(): boolean
- 当前线程是否持有写锁。getWaitingThreads(Condition): Collection<Thread>
- 返回正在等待给定条件的线程集合。readLock(): ReadLock
- 获取读锁。getQueueLength(): int
- 返回等待获取任何锁(读或写)的线程数。toString(): String
- 返回此锁的字符串表示形式。hasQueuedThread(Thread): boolean
- 检查指定的线程是否在等待队列中。getOwner(): Thread
- 返回当前持有锁的线程,如果没有线程持有则返回null。isFair(): boolean
- 判断此锁是否为公平锁。isWriteLocked(): boolean
- 判断此锁是否被写锁定。getReadHoldCount(): int
- 获取当前持有读锁的线程数量。
使用场景
使用场景:
- 当多个线程需要频繁读取共享资源时,使用读锁可以提高性能,因为读锁之间不会阻塞。
- 当只有一个线程需要修改共享资源时,使用写锁可以确保数据的一致性和完整性。
- 在读写比例较高的场景下,ReentrantReadWriteLock 比使用普通的synchronized关键字更高效。
示例:
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
public class ReentrantReadWriteLockExample {
private final ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
private int count = 0;
public void increment() {
readWriteLock.writeLock().lock(); // 获取写锁
try {
count++;
System.out.println("Incremented count to: " + count);
} finally {
readWriteLock.writeLock().unlock(); // 释放写锁
}
}
public void decrement() {
readWriteLock.writeLock().lock(); // 获取写锁
try {
count--;
System.out.println("Decremented count to: " + count);
} finally {
readWriteLock.writeLock().unlock(); // 释放写锁
}
}
public void getCount() {
readWriteLock.readLock().lock(); // 获取读锁
try {
System.out.println("Current count: " + count);
} finally {
readWriteLock.readLock().unlock(); // 释放读锁
}
}
public static void main(String[] args) {
ReentrantReadWriteLockExample example = new ReentrantReadWriteLockExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.decrement();
}
});
Thread t3 = new Thread(() -> {
for (int i = 0; i < 5; i++) {
example.getCount();
}
});
t1.start();
t2.start();
t3.start();
}
}
在这个示例中,我们使用了 ReentrantReadWriteLock 来保护对共享资源(count)的访问。我们创建了三个线程,分别执行递增、递减和获取计数器值的操作。通过使用 ReentrantReadWriteLock,我们可以确保在多线程环境下对这些操作的正确同步。
StampedLock
一个乐观读锁的实现,它提供了一种通过戳记来控制读写访问的方法,可以有效处理高并发的数据访问。
类方法如下:
unlockWrite(long stamp)
: 释放写锁。readLockInterruptibly()
: 获取读锁,可中断。validate(long stamp)
: 验证给定的stamp是否有效。acquireWrite(boolean interruptible, long nanos)
: 尝试获取写锁,可以指定是否可中断以及等待时间。tryReadLock(long time, TimeUnit unit)
: 尝试在指定的时间内获取读锁。writeLock()
: 获取写锁。getReadLockCount(long stamp)
: 获取给定stamp对应的读锁计数。tryUnlockWrite()
: 尝试释放写锁,如果成功则返回true,否则返回false。tryIncReaderOverflow(long stamp)
: 尝试增加溢出计数器,如果成功则返回新的stamp,否则返回0。isReadLocked()
: 判断是否有线程持有读锁。tryWriteLock(long time, TimeUnit unit)
: 尝试在指定的时间内获取写锁。readObject(ObjectInputStream in)
: 从输入流中读取对象。getReadLockCount()
: 获取当前读锁计数。toString()
: 返回类的字符串表示形式。unstampedUnlockRead()
: 释放未标记的读锁。tryConvertToWriteLock(long stamp)
: 尝试将给定的stamp转换为写锁,如果成功则返回新的stamp,否则返回0。isWriteLocked()
: 判断是否有线程持有写锁。release(WNode node)
: 释放节点持有的锁。tryOptimisticRead()
: 尝试进行乐观读操作。writeLockInterruptibly()
: 获取写锁,可中断。cancelWaiter(WNode w, WNode next, boolean isShared)
: 取消等待队列中的节点。tryConvertToReadLock(long stamp)
: 尝试将给定的stamp转换为读锁,如果成功则返回新的stamp,否则返回0。tryConvertToOptimisticRead(long stamp)
: 尝试将给定的stamp转换为乐观读锁,如果成功则返回新的stamp,否则返回0。asReadWriteLock()
: 返回一个ReadWriteLock实例。tryUnlockRead()
: 尝试释放读锁,如果成功则返回true,否则返回false。asWriteLock()
: 返回一个Lock实例,用于写操作。tryDecReaderOverflow(long stamp)
: 尝试减少溢出计数器,如果成功则返回新的stamp,否则返回0。acquireRead(boolean interruptible, long nanos)
: 尝试在指定的时间内获取读锁。unstampedUnlockWrite()
: 释放未标记的写锁。unlockRead(long stamp)
: 释放给定stamp对应的读锁。asReadLock()
: 返回一个Lock实例,用于读操作。tryWriteLock()
: 尝试获取写锁。readLock()
: 获取读锁。unlock(long stamp)
: 释放给定stamp对应的锁。
使用场景
使用场景:
- 当多个线程需要频繁读取共享资源时,使用 StampedLock 可以提高性能,因为读锁之间不会阻塞。
- 当只有一个线程需要修改共享资源时,使用 StampedLock 可以确保数据的一致性和完整性。
- 在读写比例较高的场景下,StampedLock 比使用普通的synchronized关键字更高效。
示例:
import java.util.concurrent.locks.StampedLock;
public class StampedLockExample {
private int x = 0;
private final StampedLock stampedLock = new StampedLock();
public void increment() {
long stamp = stampedLock.writeLock();
try {
x++;
} finally {
stampedLock.unlockWrite(stamp);
}
}
public int getX() {
long stamp = stampedLock.tryOptimisticRead();
int currentX = x;
if (!stampedLock.validate(stamp)) {
stamp = stampedLock.readLock();
try {
currentX = x;
} finally {
stampedLock.unlockRead(stamp);
}
}
return currentX;
}
public static void main(String[] args) {
StampedLockExample example = new StampedLockExample();
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
example.increment();
}
});
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
System.out.println("Current value of x: " + example.getX());
}
});
t1.start();
t2.start();
}
}
在这个示例中,我们使用了 StampedLock 来保护对共享资源(x)的访问。我们创建了两个线程,一个线程用于递增 x 的值,另一个线程用于获取 x 的当前值。通过使用 StampedLock,我们可以确保在多线程环境下对这些操作的正确同步。