多线程买票是java中的一个经典案例,其主要思想无非包括2点,synchronized和锁,两者中,前者实现同步,后者为同步的线程提供锁,从而实现多个线程共享同一份资源时候,能够同步进行;
经典的方式是synchronized + 锁对象,同样采用诸如Lock即显式的声明concurrent中的锁也可以实现同样的效果,可根据实际情况酌情使用,以下分别采用不同的方式实现模拟多窗口买票场景
方式1:
public class SaleTicket implements Runnable{
public int total;
public int count;
public SaleTicket() {
total = 100;
count = 0;
}
public void run() {
while (total > 0) {
synchronized (this) {
if(total > 0){
try {
Thread.sleep(new Random().nextInt(1000));
} catch (Exception e) {
e.printStackTrace();
}
count++;
total--;
System.out.println(Thread.currentThread().getName()+"\t当前票号:" + count);
}
}
}
}
public static void main(String[] args) {
SaleTicket st = new SaleTicket();
for(int i=0;i<=5;i++){
new Thread(st, "售票点" + i).start();
}
}
}
要注意的是,这个synchronized里面的this的含义,锁的应该是当前的这个class执行类,由于该类实现了Runnable接口,故多个线程对应的该类同时启动的时候,锁才有意义,但是这样的方式并没有显示得使用对象锁;
执行效果:
方式2:
public class SaleTicketThread extends Thread{
public static int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
while(true){
synchronized (obj) {
if(tickets > 0){
System.out.printf("%s线程正在卖出第%d张票\n",Thread.currentThread().getName(),tickets);
--tickets;
}else{
break;
}
}
}
}
public static void main(String[] args) {
SaleTicketThread st1 = new SaleTicketThread();
st1.start();
SaleTicketThread st2 = new SaleTicketThread();
st2.start();
}
}
运行效果:
可以看到,此处使用了对象作为锁,多个线程并发的访问当前资源的时候,抢占到对象锁的线程将执行买票动作;
方式3,采用Lock方式:
public class MyThreadDemo implements Runnable {
private int tickets = 25;
private Lock lock = new ReentrantLock();
public void run() {
while (true) {
lock.lock();
if(tickets > 0){
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "售出了第" + (tickets--) + "张票");
}
lock.unlock();
}
}
public static void main(String[] args) {
MyThreadDemo type1 = new MyThreadDemo();
Thread t1 = new Thread(type1,"窗口1");
Thread t2 = new Thread(type1,"窗口2");
Thread t3 = new Thread(type1,"窗口3");
t1.start();
t2.start();
t3.start();
}
运行效果:
这种方式采用JDK自身的concurrent包下的Lock方式为抢占到资源的线程加锁,抢占到了锁则调用lock.lock(),使用完毕,调用lock.unlock()释放锁;
总结:
以上几种方式,均可使用,但实际场景中,采用对象加锁的方式更加通用,配合synchronized关键字一起使用也比较容易理解。