多线程买票是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关键字一起使用也比较容易理解。
原文链接:https://blog.csdn.net/zhangcongyi420/article/details/82501484