JDK并发包学习-ReentrantLock 重入锁
2018-05-20 本文已影响0人
楼主楼主
可以把ReentrantLock
当做一个synchronized
关键字的功能拓展
之所以叫“重入锁”,因为,在同一个线程中,是可以对一个重入锁重复加锁的,当然在完成对资源的处理后,也需要同样数量的解锁才能解除对资源的占用。(不过我还是不明白“重入”到底有什么实际意义)
这段代码 打印了,当前线程持有该锁的数量,如果该锁不是由当前线程持有的话,则为零
ReentrantLock lock = new ReentrantLock();
new Thread(()->{
System.out.println(lock.getHoldCount());
lock.lock();
System.out.println(lock.getHoldCount());
lock.lock();
System.out.println(lock.getHoldCount());
lock.unlock();
System.out.println(lock.getHoldCount());
lock.unlock();
System.out.println(lock.getHoldCount());
}).start();
结果
0
1
2
1
0
使用重入锁模拟售票机卖车票
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class LocksDemo {
private static long tickets =20; // 有20张票可出售
public static void main (String[] args) throws InterruptedException {
ReentrantLock reentrantLock = new ReentrantLock();
//三个售票窗口
Thread window1 =new Thread(new Runnable1(reentrantLock,"售票机1"));
Thread window2 =new Thread(new Runnable1(reentrantLock,"售票机2"));
Thread window3 =new Thread(new Runnable1(reentrantLock,"售票机3"));
long time = System.currentTimeMillis();
window1.start();
window2.start();
window3.start();
window1.join();
window2.join();
window3.join();
System.out.println("车票售罄,耗时"+(System.currentTimeMillis()-time));
}
private static class Runnable1 implements Runnable{
private final Lock lock;
private String name;
Runnable1(Lock lock,String name){
this.lock =lock;
this.name = name;
}
@Override
public void run() {
while (tickets>0) {
try {
lock.lock();
if(tickets>0){
tickets--;
System.out.println(name+"卖出车票,余票"+tickets);
}else {
System.out.println(name+" 车票卖光了");
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
System.out.println(name+"结束工作");
}
}
}
结果
售票机1卖出车票,余票19
售票机2卖出车票,余票18
售票机2卖出车票,余票17
售票机2卖出车票,余票16
售票机2卖出车票,余票15
售票机2卖出车票,余票14
售票机3卖出车票,余票13
售票机1卖出车票,余票12
售票机1卖出车票,余票11
售票机1卖出车票,余票10
售票机1卖出车票,余票9
售票机1卖出车票,余票8
售票机1卖出车票,余票7
售票机1卖出车票,余票6
售票机1卖出车票,余票5
售票机1卖出车票,余票4
售票机2卖出车票,余票3
售票机2卖出车票,余票2
售票机2卖出车票,余票1
售票机3卖出车票,余票0
售票机2结束工作
售票机3结束工作
售票机1 车票卖光了
售票机1结束工作
车票售罄,耗时2109
公平锁
用上述方式卖票,看起来售票机1卖的票比另外两个窗口都多,排在2,3号售票机买票的人觉得不公平!可以用“公平锁”解决这个问题
使用此方式得到重入锁,它将是一个“公平锁” ReentrantLock reentrantLock = new ReentrantLock(true);
上述代码使用公平锁后,它的运行结果如下,三个窗口交替买票,排队的人不再有怨言 :) 不过,公平锁性能会相对更加底下,没有特殊需求时,就别用了吧
售票机1卖出车票,余票19
售票机3卖出车票,余票18
售票机2卖出车票,余票17
售票机1卖出车票,余票16
售票机3卖出车票,余票15
售票机2卖出车票,余票14
售票机1卖出车票,余票13
售票机3卖出车票,余票12
售票机2卖出车票,余票11
售票机1卖出车票,余票10
售票机3卖出车票,余票9
售票机2卖出车票,余票8
售票机1卖出车票,余票7
售票机3卖出车票,余票6
售票机2卖出车票,余票5
售票机1卖出车票,余票4
售票机3卖出车票,余票3
售票机2卖出车票,余票2
售票机1卖出车票,余票1
售票机3卖出车票,余票0
售票机3结束工作
售票机2 车票卖光了
售票机2结束工作
售票机1 车票卖光了
售票机1结束工作
车票售罄,耗时2366
锁的好搭档 Condition 条件
现在,售票系统升级了,会不断地放出新的车票,这样,售票机就不用停止工作了。但是当车票售空时,售票机要等待添加新的车票才能继续工作,稍微修改上面的代码,并用Condition来实现这个逻辑
import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
public class TicketsDemo1 {
private static ReentrantLock lock = new ReentrantLock(true);
private static Condition noTicket = lock.newCondition(); // 一个与lock绑定,代表票已售空的Condition
public static void main (String[] args) throws InterruptedException {
TicketSystem tickets = new TicketSystem(5);// 售票系统中初始有5张车票
//三个售票窗口
Thread window1 =new Thread(new TicketMachine(tickets,"售票机1"));
Thread window2 =new Thread(new TicketMachine(tickets,"售票机2"));
Thread window3 =new Thread(new TicketMachine(tickets,"售票机3"));
Thread adder = new TicketsAdder(tickets);
adder.start();
window1.start();
window2.start();
window3.start();
}
private static class TicketSystem {
private int num;// 车票数量
TicketSystem(int num){
this.num = num;
System.out.println("初始车票数量:" + num);
}
//车票余量
public int getRemaining() {
return num;
}
//卖出车票
void sale(){
num--;
}
//添加车票
void add(int num){
this.num +=num;
}
}
private static class TicketMachine extends Thread{
private final TicketSystem tickets;
TicketMachine(TicketSystem tickets, String name){
this.setName(name);
this.tickets = tickets;
}
@Override
public void run() {
while (true) {
try {
lock.lock();
if(tickets.getRemaining()>0){
tickets.sale();
System.out.println(getName()+"卖出车票,余票"+tickets.getRemaining());
}else {
System.out.println(getName()+" 车票卖光了,等待有新的车票放出。。。");
noTicket.await(); // 等待系统中有足够的车票
}
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
}
private static class TicketsAdder extends Thread{
private final TicketSystem tickets;
private Random random = new Random();
TicketsAdder(TicketSystem tickets){
this.tickets =tickets;
}
@Override
public void run() {
while (true) {
try {
lock.lock();
int ticketsNumber = random.nextInt(5)+1;//随机添加车票
tickets.add(ticketsNumber);
System.out.println("新增车票:"+ticketsNumber+",余票"+tickets.getRemaining());
noTicket.signalAll();//通知窗口已经有车票了
lock.unlock();
Thread.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
运行结果
初始车票数量:5
新增车票:1,余票6
售票机1卖出车票,余票5
售票机2卖出车票,余票4
售票机3卖出车票,余票3
售票机1卖出车票,余票2
售票机2卖出车票,余票1
售票机3卖出车票,余票0
售票机1 车票卖光了,等待有新的车票放出。。。
新增车票:4,余票4
售票机2卖出车票,余票3
售票机3卖出车票,余票2
售票机2卖出车票,余票1
售票机3卖出车票,余票0
售票机1 车票卖光了,等待有新的车票放出。。。
售票机2 车票卖光了,等待有新的车票放出。。。
新增车票:5,余票5
售票机3卖出车票,余票4
售票机3卖出车票,余票3
售票机1卖出车票,余票2
售票机2卖出车票,余票1
售票机3卖出车票,余票0
新增车票:5,余票5
售票机1卖出车票,余票4
···