在 Java 中,除了使用 synchronized 关键字实现线程同步外,还可以使用
java.util.concurrent.locks.Lock 接口及其实现类(如 ReentrantLock)来实现更灵活的线程同步。Lock 提供了比 synchronized 更强大的功能,例如可重入锁、公平锁、尝试获取锁、超时获取锁等。
1.Lock 的基本使用
Lock 是一个接口,常用的实现类是 ReentrantLock。以下是 Lock 的基本用法:
示例:使用ReentrantLock实现线程同步
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LockDemo {
private static int counter = 0;
private static final Lock lock = new ReentrantLock(); // 创建锁对象
public static void main(String[] args) throws InterruptedException {
Runnable task = () -> {
for (int i = 0; i < 1000; i++) {
lock.lock(); // 加锁
try {
counter++; // 操作共享资源
} finally {
lock.unlock(); // 释放锁
}
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
thread1.join();
thread2.join();
System.out.println("Counter: " + counter); // 输出 2000
}
}2.Lock 的特点
- o 显式锁:需要手动加锁和释放锁。
- o 可重入锁:同一个线程可以多次获取同一把锁。
- o 公平锁:支持公平锁和非公平锁(默认是非公平锁)。
- o 尝试获取锁:可以尝试获取锁,如果锁不可用则立即返回或等待一段时间。
- o 超时获取锁:可以设置超时时间,避免死锁。
3.尝试获取锁
使用 tryLock() 方法尝试获取锁,如果锁不可用则立即返回 false,避免线程阻塞。
示例:尝试获取锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockDemo {
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) {
Runnable task = () -> {
if (lock.tryLock()) { // 尝试获取锁
try {
System.out.println(Thread.currentThread().getName() + " 获取到锁,正在执行任务...");
Thread.sleep(1000); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock(); // 释放锁
}
} else {
System.out.println(Thread.currentThread().getName() + " 未获取到锁,执行其他操作...");
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
}
}4.超时获取锁
使用 tryLock(long time, TimeUnit unit) 方法在指定时间内尝试获取锁,超时后返回 false。
示例:超时获取锁
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class TryLockTimeoutDemo {
private static final Lock lock = new ReentrantLock();
public static void main(String[] args) {
Runnable task = () -> {
try {
if (lock.tryLock(1, TimeUnit.SECONDS)) { // 尝试在 1 秒内获取锁
try {
System.out.println(Thread.currentThread().getName() + " 获取到锁,正在执行任务...");
Thread.sleep(2000); // 模拟任务执行
} finally {
lock.unlock(); // 释放锁
}
} else {
System.out.println(Thread.currentThread().getName() + " 未获取到锁,放弃执行...");
}
} catch (InterruptedException e) {
e.printStackTrace();
}
};
Thread thread1 = new Thread(task);
Thread thread2 = new Thread(task);
thread1.start();
thread2.start();
}
}5.公平锁
ReentrantLock 支持公平锁和非公平锁。公平锁会按照线程请求锁的顺序分配锁,而非公平锁则允许插队。
示例:公平锁
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class FairLockDemo {
private static final Lock lock = new ReentrantLock(true); // 创建公平锁
public static void main(String[] args) {
Runnable task = () -> {
for (int i = 0; i < 3; i++) {
lock.lock();
try {
System.out.println(Thread.currentThread().getName() + " 获取到锁,执行任务...");
Thread.sleep(100); // 模拟任务执行
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Thread thread1 = new Thread(task, "Thread-1");
Thread thread2 = new Thread(task, "Thread-2");
Thread thread3 = new Thread(task, "Thread-3");
thread1.start();
thread2.start();
thread3.start();
}
}6.Condition 实现线程通信
Lock 可以与 Condition 结合使用,实现更灵活的线程通信(类似于 wait() 和 notify())。
示例:使用Condition实现生产者-消费者模型
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ProducerConsumerDemo {
private static final Lock lock = new ReentrantLock();
private static final Condition notFull = lock.newCondition(); // 条件变量:队列未满
private static final Condition notEmpty = lock.newCondition(); // 条件变量:队列未空
private static final int CAPACITY = 10;
private static final int[] queue = new int[CAPACITY];
private static int count = 0, putIndex = 0, takeIndex = 0;
public static void main(String[] args) {
Runnable producer = () -> {
for (int i = 0; i < 20; i++) {
lock.lock();
try {
while (count == CAPACITY) {
notFull.await(); // 队列已满,等待
}
queue[putIndex] = i;
putIndex = (putIndex + 1) % CAPACITY;
count++;
notEmpty.signal(); // 通知消费者
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
Runnable consumer = () -> {
for (int i = 0; i < 20; i++) {
lock.lock();
try {
while (count == 0) {
notEmpty.await(); // 队列为空,等待
}
int item = queue[takeIndex];
takeIndex = (takeIndex + 1) % CAPACITY;
count--;
notFull.signal(); // 通知生产者
System.out.println("消费: " + item);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
};
new Thread(producer).start();
new Thread(consumer).start();
}
}总结
- o Lock 提供了比 synchronized 更灵活的线程同步机制。
- o 使用 tryLock() 和超时机制可以避免死锁。
- o Condition 可以实现复杂的线程通信。
- o 推荐在需要高级功能(如公平锁、尝试获取锁等)时使用 Lock,否则可以使用 synchronized。
希望这些示例能帮助你掌握 Java 中 Lock 的使用!
