相对于synchronized,RenentrantLock有这么几个特点
可以被中断,可以设置超时时间,支持多个条件变量,可以设置成公平锁。
同时RenentrantLock和synchronized都是可重入的
一、可重入
可重入指的是如果一个线程首次获取了锁,它还可以再次获取到这个锁,举个例子来说明
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
LOGGER.info("main 获取锁");
m2();
} finally {
//释放锁
lock.unlock();
}
}
});
t1.start();
}
public static void m2(){
lock.lock();
try {
LOGGER.info("m2 获取锁");
} finally {
lock.unlock();
}
}
}
上边的代码中线程t先获取到锁,然后其调用方法m2再次加锁,再次加锁可以成功,这就是可重入,一个线程已经持有锁后再次获取同一把锁。
二、可中断(打断)
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
LOGGER.info("尝试获取锁");
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
LOGGER.info("未获取到锁,被其他线程打断了");
return;
}
//获取到锁
LOGGER.info("获取到了锁");
}
});
//主线程先获取锁
lock.lock();
t1.start();
//这时t1获取不到锁,被阻塞,处于BLOCKED状态
Thread.sleep(2000);
//2s后打断线程t1,t1恢复运行
LOGGER.info("主线程打断");
t1.interrupt();
}
}
当调用ReentrantLock对象的lockInterruptibly
方法来加锁时此方法支持被打断,如果获取不到锁就会阻塞住,这时其他线程可以打断它,就会抛出InterruptedException
异常并恢复运行。
注意如果t1已经获取到锁了并开始执行自己的代码,这时候其他线程再去打断t1是没有效果的
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
try {
LOGGER.info("尝试获取锁");
lock.lockInterruptibly();
while (true){
LOGGER.info("ooo");
}
} catch (InterruptedException e) {
e.printStackTrace();
LOGGER.info("未获取到锁,被其他线程打断了");
}
}
});
t1.start();
Thread.sleep(2000);
LOGGER.info("主线程打断");
t1.interrupt();
就像这样t1会一直循环,主线程打断也不起作用
三、设置超时时间
调用tryLock
方法可以设置超时时间
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
public static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
boolean res = false;
try {
LOGGER.info("尝试获取锁");
res = lock.tryLock(2, TimeUnit.SECONDS);
} catch (InterruptedException e) {
e.printStackTrace();
}
if(res){
//成功获取到锁
try {
LOGGER.info("成功获取到锁");
} finally {
lock.unlock();
}
} else{
LOGGER.info("未获取到锁");
}
}
});
//主线程先获取锁
lock.lock();
t1.start();
}
}
t1先尝试获取锁,因为主线程持有了锁所以获取不到,t1等待2s后就会返回false表示未获取到锁。
还有一个不带参数的tryLock方法调用后如果获取不到锁就会立即返回false。
注意带参数的tryLock方法是可以被打断的,不带参数的不可以
四、设置成公平锁
ReentrantLock默认情况下和synchronized一样,都是非公平锁,多个线程进入waitset进行等待,被唤醒后会竞争锁,跟进入waitset的顺序没关系。公平锁的情况下先进入等待的就会先获取到锁。
如果创建ReentrantLock对象时传入一个true参数创建的就是公平锁
public static ReentrantLock lock = new ReentrantLock(true);
五、条件变量
使用synchronized时底层使用的是Monitor对象,调用wait方法时线程是在waitset中等待,一个Monitor对象中只有一个waitset。而使用ReentrantLock时,一个锁对象中相可以有多个waitset,条件不同可以到不同的休息室中等待,这样唤醒时就不需要像synchronized那样把waitset中的所有线程都唤醒
public class Test7 {
private static final Logger LOGGER = LoggerFactory.getLogger(Test7.class);
public static ReentrantLock lock = new ReentrantLock(true);
//创建两个休息室
public static Condition condition1 = lock.newCondition();
public static Condition condition2 = lock.newCondition();
public static boolean flag1 = false;
public static boolean flag2 = false;
public static void main(String[] args) throws InterruptedException {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
while (!flag1){
// 没有烟不能干活
try {
LOGGER.info("没烟不能干活");
condition1.await();//在休息室1中等待
} catch (InterruptedException e) {
e.printStackTrace();
return;
}
}
LOGGER.info("有烟了可以干活");
} finally {
lock.unlock();
}
}
},"t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
lock.lock();
try {
while (!flag2){
LOGGER.info("没有外卖不能干活");
try {
condition2.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
LOGGER.info("有外卖了,可以干活");
} finally {
lock.unlock();
}
}
},"t2");
t1.start();
t2.start();
Thread.sleep(1000);
try {
lock.lock();
flag1=true;//送烟
condition1.signal();//叫醒
} finally {
lock.unlock();
}
Thread.sleep(2000);
try {
lock.lock();
flag2=true;//送外卖
condition2.signal();
} finally {
lock.unlock();
}
}
}
标签:简单,lock,ReentrantLock,t1,static,使用,new,LOGGER,public
From: https://www.cnblogs.com/chengxuxiaoyuan/p/16945041.html