java模拟多线程买票问题


多线程买票是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