-
ReentrantLock特点:
1.可中断
2.可设置超时时间:超过设置时间就取消竞争资源
3.可设置为公平锁:先进先出,先等待的优先获得锁
4.支持多个条件变量
-
与Synchronized相同点:
都支持可重入
基本语法:
//获取锁
reentrantLock.lock()
try{
//临界区
}finally{
//释放锁
reentrantLock.unlock();
}
可重入:
如果一个线程获得了一个锁,那么它可以再次获得这个锁;
不可重入:如果一个线程获得了一个锁,当他再次获得这个锁时会被自己挡住。因为它已经有这个锁了。
ReentrantLock特性示范:
lock的可重入:
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic = "TC13")
public class TC13 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
lock.lock();
try {
log.debug("enter main.");
m1();
}finally {
lock.unlock();
}
}
private static void m1() {
lock.lock();
try {
log.debug("enter m1.");
m2();
}finally {
lock.unlock();
}
}
private static void m2(){
lock.lock();
try {
log.debug("enter m2.");
}finally {
lock.unlock();
}
}
}
lock的可中断:
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic = "TC14")
public class TC14 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
log.debug("尝试获得锁");
//lockInterruptibly 可被打断
//如果没有竞争,此方法可以获得lock对象锁。
//如果有竞争,那么就会进入阻塞队列,可以用interrupt()中断等待
lock.lockInterruptibly();
} catch (InterruptedException e) {
e.printStackTrace();
log.debug("t1被打断");
return;
}
try {
log.debug("获得锁");
} finally {
lock.unlock();
}
}, "t1");
lock.lock();
//主线程已经调用了lock对象锁,t1线程调用lockInterruptibly()时会产生竞争,所以t1线程进入阻塞队列。
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//中断t1线程的阻塞等待状态。抛出InterruptedException异常。
t1.interrupt();
}
}
锁超时:
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic = "TC15")
public class TC15 {
private static ReentrantLock lock = new ReentrantLock();
public static void main(String[] args) {
Thread t1 = new Thread(() -> {
try {
//超过等待时间就是设置获取锁为false
if (!lock.tryLock(2, TimeUnit.SECONDS)) {
log.debug("t1获取锁失败");
return;
}
}catch (InterruptedException e) {
//线程被中断异常
e.printStackTrace();
log.debug("t1被中断");
return;
}
try {
log.debug("t1获取锁成功");
} finally {
lock.unlock();
log.debug("t1释放锁");
}
}, "t1");
log.debug("main获取到锁");
lock.lock();
//开始计算t1获取锁对象时间
t1.start();
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
//main 1秒后释放,t1等待2秒,故t1能等到锁对象
lock.unlock();
log.debug("main释放锁");
}
}
使用ReentrantLock解决科学家就餐问题:
import lombok.extern.slf4j.Slf4j;
import java.util.concurrent.locks.ReentrantLock;
public class TC16 {
public static void main(String[] args) {
ChopstickTest chopstickTest1 = new ChopstickTest(1);
ChopstickTest chopstickTest2 = new ChopstickTest(2);
ChopstickTest chopstickTest3 = new ChopstickTest(3);
ChopstickTest chopstickTest4 = new ChopstickTest(4);
ChopstickTest chopstickTest5 = new ChopstickTest(5);
Philosopher philosopher1 = new Philosopher("科学家1",chopstickTest1,chopstickTest2);
Philosopher philosopher2 = new Philosopher("科学家2",chopstickTest2,chopstickTest3);
Philosopher philosopher3 = new Philosopher("科学家3",chopstickTest3,chopstickTest4);
Philosopher philosopher4 = new Philosopher("科学家4",chopstickTest4,chopstickTest5);
Philosopher philosopher5 = new Philosopher("科学家5",chopstickTest5,chopstickTest1);
philosopher1.start();
philosopher2.start();
philosopher3.start();
philosopher4.start();
philosopher5.start();
}
}
@Slf4j(topic = "Philosopher")
class Philosopher extends Thread{
ChopstickTest left;
ChopstickTest right;
private String name;
public Philosopher(String name, ChopstickTest left, ChopstickTest right) {
this.name=name;
this.left=left;
this.right=right;
}
@Override
public void run() {
//让其一直吃饭,检查死锁问题不会复现
while(true) {
if (left.tryLock()) {
try{
//若右手筷处于竞争状态,那么该科学家拿右手筷false,不进入if判断,直接执行left的finally,释放左手筷
//让其他科学家能够拿该科学家的左手筷
if (right.tryLock()) {
try {
eat(name);
}finally {
right.unlock();
}
}
}finally {
left.unlock();
}
}
}
}
private void eat(String name) {
log.debug(name+"吃上饭了");
}
}
class ChopstickTest extends ReentrantLock {
private int id;
public ChopstickTest(int id) {
this.id=id;
}
}
公平锁:
在ReentrantLock的构造方法里有一个Boolean参数可设置公平锁,默认是false为不公平.
条件变量:
import lombok.extern.slf4j.Slf4j;
import sun.misc.ConditionLock;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
@Slf4j(topic="TC17")
public class TC17 {
static boolean hasCigarate = false;
static boolean hasTakeOut = false;
static ReentrantLock lock = new ReentrantLock();
static Condition waitCigarate = lock.newCondition();
static Condition waitTakeOut = lock.newCondition();
public static void main(String[] args) {
new Thread(()->{
lock.lock();
try {
log.debug("有烟没[{}]",hasCigarate);
while (!hasCigarate) {
log.debug("没烟再等会");
try {
waitCigarate.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (hasCigarate) {
log.debug("有烟了,开始干活");
}else{
log.debug("没干成活");
}
}finally {
lock.unlock();
}
},"小南").start();
new Thread(()->{
lock.lock();
try {
log.debug("有外卖没[{}]",hasTakeOut);
while (!hasTakeOut) {
log.debug("没外卖再等会");
try {
waitTakeOut.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if (hasTakeOut) {
log.debug("有外卖了,开始看电视");
}else{
log.debug("没看成电视");
}
}finally {
lock.unlock();
}
},"小丽").start();
new Thread(()->{
lock.lock();
try {
while (!hasCigarate) {
log.debug("开始送烟");
hasCigarate = true;
//唤醒waitCigarate.await()
waitCigarate.signal();
}
}finally {
lock.unlock();
}
},"送烟的").start();
new Thread(()->{
lock.lock();
try {
while (!hasTakeOut) {
log.debug("开始送外卖");
hasTakeOut = true;
waitTakeOut.signal();
}
}finally {
lock.unlock();
}
},"送外卖的").start();
}
}