一、Lock锁概述
在Java 5之后,引入了一个新的并发API,位java.util.concurrent.locks
包下,提供了比synchronized关键字更灵活的锁定机制。Lock
接口是其中的核心,它提供了比synchronized更丰富的功能,比如尝试非阻塞地获取锁、能被中断的锁获取以及尝试获取锁时提供超时等。
二、Lock锁的使用
以下是使用ReentrantLock
(实现了Lock
接口的一个具体实现)的代码例子及注释:
java
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class CounterWithLock {
// 共享资源:计数器
private int count = 0;
// 创建一个ReentrantLock实例
private final Lock lock = new ReentrantLock();
// 使用Lock增加计数器
public void increment() {
// 获取锁
lock.lock();
try {
count++; // 访问共享资源
} finally {
// 释放锁
lock.unlock();
}
}
// 使用Lock减少计数器
public void decrement() {
// 获取锁
lock.lock();
try {
count--; // 访问共享资源
} finally {
// 释放锁
lock.unlock();
}
}
// 获取当前计数器的值
public int getCount() {
return count;
}
public static void main(String[] args) {
CounterWithLock counter = new CounterWithLock();
// 创建线程1,执行增加操作
Thread t1 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.increment();
}
});
// 创建线程2,执行减少操作
Thread t2 = new Thread(() -> {
for (int i = 0; i < 1000; i++) {
counter.decrement();
}
});
// 启动线程1和线程2
t1.start();
t2.start();
// 等待线程1和线程2执行完毕
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
// 输出最终计数器的值
System.out.println("Final count: " + counter.getCount());
}
}
代码解释:
-
定义了一个名为
CounterWithLock
的类,其中包含一个共享资源count
和一个ReentrantLock
实例。 -
increment()
和decrement()
方法用于增加和减少计数器。这两个方法中,通过调用lock.lock()
获取锁,然后执行操作,最后在finally
块中调用lock.unlock()
释放锁。这样可以确保即使发生异常,锁也能被正确释放。 -
在
main()
方法中,创建了两个线程t1
和t2
,分别执行增加和减少操作。 -
通过调用
t1.start()
和t2.start()
启动线程1和线程2。 -
使用
t1.join()
和t2.join()
等待线程1和线程2执行完毕。 -
输出最终计数器的值。 通过上述代码,我们可以看到,使用
Lock
锁可以保证在多线程环境下共享资源count
的正确性。与synchronized
相比,Lock
提供了更灵活的锁定操作,比如可以尝试获取锁而不立即阻塞,或者可以响应中断。 请注意,虽然Lock
提供了更多的灵活性,但也需要更谨慎地使用。例如,在finally
块中释放锁是一个很好的实践,以避免死锁的发生。此外,使用Lock
时,获取锁和释放锁的操作必须成对出现,否则可能导致资源泄露或其他线程无法获取锁。