ReetrantLock重入锁
ReetrantLock, JDK1.5 时JUC包下添加的一个类,实现于Lock接口,作用与synchronized相同,不过对比于synchronized更加灵活,但是使用时需要我们手动获取/释放锁。
ReetrantLock本身是支持重入的一把锁,即支持当前获取锁的线程对锁资源进行多次重复的锁获取,在此同时还支持公平锁与非公平锁。
这里的公平与非公平指的是获取锁操作执行后锁资源获取的先后顺序,如果先执行获取锁操作的线程先获取锁,那么就代表当前的锁是公平的,反之,如果先执行获取锁操作的线程还需要和后面执行获取锁操作的线程竞争锁资源,那么则代表当前锁是非公平的。
这里值得注意的是:非公平锁虽然会出现线程竞争锁资源的情况,但是一般而言非公平锁的效率在绝大部分情况下也远远超出公平锁。不过在某些特殊的业务场景下,比如更加注重锁资源获取的先后顺序,那么公平锁才是最好的选择。
在前面我们也提到过ReetrantLock支持锁重入即当前线程能够多次执行获取锁操作,但是我们在使用ReetantLock过程中要明白的是:ReetrantLock执行了几次获取锁操作也需要执行多少次释放锁操作。
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
publicclassTaskimplementsRunnable {
publicstatic Lock lock = new ReentrantLock();
publicstaticint count = 0;
@Override
public void run() {
for (int i = 0; i<10000;i++){
lock.lock(); // 第一次获取锁
lock.lock(); // 第二次获取锁
try {
count++; // 非原子性操作:存在线程安全问题
} finally {
lock.unlock(); // 第一次释放锁
lock.unlock(); // 第二次释放锁
}
}
}
public static void main(String[] args) throws InterruptedException {
Task task = new Task();
Thread t1 = new Thread(task);
Thread t2 = new Thread(task);
t1.start();
t2.start();
t1.join();
t2.join();
System.out.println(count);
// 执行结果:20000
}
}
上面例子,t1,t2两个线程同时对共享资源count进行++的非原子性操作,我们在这里使用ReentrantLock锁解决存在的线程安全问题。同时我们在上述代码中,获取了两次锁资源,因为ReentrantLock支持锁重入,所以此时获取两次锁是没有问题的,不过在finally中执行释放锁资源时需要注意:也应该执行两次unlock释放锁的操作。从上述案例中分析我们可以发现,其实ReentrantLock的用法相对比较简单。