ReentrantLock
使用synchronized关键字会隐式地创建锁,但是却将锁的获取和释放固定化了。当然这种方式也便捷了程序员不用手动锁定和释放锁,但是也带来了一定的不方便,而用Lock却可以更加的灵活。
重入锁ReentrantLock
,支持重进入的锁,表示该锁能够支持一个线程对资源的重复加锁。在其调用lock()方法时,已经获取到锁的线程,能够再次调用lock()方法获取锁而不被阻塞。并且ReentrantLock还支持获取锁时的公平和非公平锁
。
公平和非公平锁
公平锁就是严格按照请求时间,就是等待时间最长的线程最优先获取锁
,而非公平锁却不安这个顺序来获取锁。其实公平的锁机制往往没有非公平的效率高,但是公平锁能够减少“饥饿“发生的概率。
Lock接口提供的synchronized关键字不具备的特性
1、尝试非阻塞地获取锁:当前线程尝试获取锁,如果这一时刻没有被其他线程获取,则成功获取并持有锁
2、能被中断的获取锁:于synchronized不同,获取到锁的线程能响应中断,当获取到锁的线程被中断时,中断异常被抛出,同事锁被释放
3、超时获取锁:在指定的截止时间之前获取锁,若没获取到,则返回
ReentrantLock锁的释放
ReentrantLock锁的释放是逐级释放的,也就是说在可重入性场景中,必须要等到场景内所有的加锁的方法都释放了锁,当前线程持有的锁才会被释放!
ReentrantLock用法
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadDomain04 {
private Lock lock = new ReentrantLock(true);
public void testMethod() {
try {
lock.lock();
for (int i = 0; i < 2; i++) {
System.out.println("ThreadName = " + Thread.currentThread().getName() +
", i = " + i);
}
} finally {
lock.unlock();
}
}
}
public class MyThread08 {
public static void main(String[] args) throws InterruptedException {
final ThreadDomain04 th = new ThreadDomain04();
Thread threadA = new Thread(new MyThread08.aRunner(th), "aThread");
Thread threadB = new Thread(new MyThread08.aRunner(th), "bThread");
Thread threadC = new Thread(new MyThread08.aRunner(th), "cThread");
threadA.start();
threadB.start();
threadC.start();
}
static class aRunner implements Runnable {
private ThreadDomain04 thd;
public aRunner(ThreadDomain04 thd) {
this.thd = thd;
}
@Override
public void run() {
{
thd.testMethod();
}
}
}
}
// ThreadName = bThread, i = 0
// ThreadName = bThread, i = 1
// ThreadName = aThread, i = 0
// ThreadName = aThread, i = 1
// ThreadName = cThread, i = 0
// ThreadName = cThread, i = 1
结果分组打印证明了ReentrantLock加锁的功能
ReentrantLock锁的对象监视器
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class ThreadDomain05 {
private Lock lock = new ReentrantLock(true);
public void testMethodA() throws InterruptedException {
lock.lock();
try {
System.out.println("MethodA begin ThreadName = " + Thread.currentThread().getName());
Thread.sleep(5000);
System.out.println("MethodA end ThreadName = " + Thread.currentThread().getName());
} finally {
lock.unlock();
}
}
public void testMethodB() throws InterruptedException {
lock.lock();
try {
System.out.println("MethodB begin ThreadName = " + Thread.currentThread().getName());
System.out.println("MethodB end ThreadName = " + Thread.currentThread().getName());
} finally {
lock.unlock();
}
}
}
public class MyThread09 {
public static void main(String[] args) throws InterruptedException {
final ThreadDomain05 th = new ThreadDomain05();
Thread threadA = new Thread(new MyThread09.aRunner(th), "aThread");
Thread threadB = new Thread(new MyThread09.bRunner(th), "bThread");
threadA.start();
threadB.start();
}
static class aRunner implements Runnable {
private ThreadDomain05 thd;
public aRunner(ThreadDomain05 thd) {
this.thd = thd;
}
@Override
public void run() {
{
try {
thd.testMethodA();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
static class bRunner implements Runnable {
private ThreadDomain05 thd;
public bRunner(ThreadDomain05 thd) {
this.thd = thd;
}
@Override
public void run() {
{
try {
thd.testMethodB();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
// MethodA begin ThreadName = aThread
// MethodA end ThreadName = aThread
// MethodB begin ThreadName = bThread
// MethodB end ThreadName = bThread
结果A线程begin和end之间的确经过了5s之后才会有B线程的启动,证明了ReentrantLock持有的是对象监视器。
注意: 不要讲获取锁的过程写在try块里面,因为如果在获取锁时发生了异常,异常抛出的同时会导致锁无故释放。
tryLock()和tryLock(long timeout, TimeUnit unit)
tryLock()方法的作用是,在调用try()方法的时候,如果锁没有被另外一个线程持有,那么就返回true,否则返回false
tryLock(long timeout, TimeUnit unit)是tryLock()另一个重要的重载方法,表示如果在指定等待时间内获得了锁,则返回true,否则返回false
注意一下,tryLock()只探测锁是否,并没有lock()的功能
,要获取锁,还得调用lock()
方法。