假设电影院正在上映某电影,该电影有100张电影票可供出售,现在假设有3个窗口售票。请设计程序模拟窗口售票的场景。
分析:
3个窗口互不影响,同时售票
3个窗口共同出售这100张电影票
下面是两种用多线程实现的方式,但是都有问题(会出现卖的票重复、卖不存在的票、票的顺序出错)
部分运行结果
方式一
//方式一:将类声明为Thread的子类
public class Test {
public static void main(String[] args) {
//创建线程对象
SellTicket t1 = new SellTicket();
SellTicket t2 = new SellTicket();
SellTicket t3 = new SellTicket();
//各线程起名字
t1.setName("一号窗口");
t2.setName("二号窗口");
t3.setName("三号窗口");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
class SellTicket extends Thread {
static int ticket = 100;//初始有100张票
@Override
public void run() {
while (ticket > 0) {//一直卖票,直到票卖完
try {//模拟选中票后的网络延迟
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(getName() + "卖了第" + ticket-- + "张票");
}
}
}
方式二
//方式二:声明实现 Runnable 接口的类
public class Test {
public static void main(String[] args) {
//创建一个Runnable子类对象
SellTicket myRunnable = new SellTicket();
//创建多个线程并起名
Thread t1 = new Thread(myRunnable, "一号窗口");
Thread t2 = new Thread(myRunnable, "二号窗口");
Thread t3 = new Thread(myRunnable, "三号窗口");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable {
int ticket = 100;//初始有100张票,
//因为多个线程都是从同一个Runnable子类对象来的,可以共享这个普通成员变量
@Override
public void run() {
while (ticket > 0) {//一直卖票,直到票卖完
try {//模拟选中票后的网络延迟
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket-- + "张票");
}
}
}
出错原因:
ticket--操作不是原子操作,它可以细分为三步:取值 、做-1运算、重新赋值,所以会因为线程切换导致不同结果。建议把像ticket这样的共享数据做锁对象处理
加锁后
方式二:声明实现 Runnable 接口的类
public class Test {
public static void main(String[] args) {
//创建一个Runnable子类对象
SellTicket myRunnable = new SellTicket();
//创建多个线程并起名
Thread t1 = new Thread(myRunnable, "一号窗口");
Thread t2 = new Thread(myRunnable, "二号窗口");
Thread t3 = new Thread(myRunnable, "三号窗口");
//启动线程
t1.start();
t2.start();
t3.start();
}
}
class SellTicket implements Runnable {
int ticket = 100;//初始有100张票,
//因为多个线程都是从同一个Runnable子类对象来的,可以共享这个普通成员变量
@Override
public void run() {
while (ticket > 0) {//一直卖票,直到票卖完
synchronized (this) {
try {//模拟选中票后的网络延迟
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "卖了第" + ticket-- + "张票");
}
}
}
}