电影院卖票-线程

电影院卖票-线程

需求

有100张票,有三个窗口卖票,设计一个程序模拟电影院卖票

思路

1.定义一个类SellTicket,实现Runnable接口,里面定义一个成员变量:private int tickets=100

2.在SellTicket类中重写run()方法实现卖票:判断票数大于0,就卖票,告知是哪一个窗口;卖票后,总票数-1;用死循环让卖票的动作一直执行

3.定义一个测试类SellTicketDemo,里面有main方法:创建SellTicket类的对象;创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,病给出对应的窗口的名称;启动线程

//SellTicketDemo.java
/*
 * #### 需求

有100张票,有三个窗口卖票,设计一个程序模拟电影院卖票

#### 思路

1.定义一个类SellTicket,实现Runnable接口,里面定义一个成员变量:
private int tickets=100

2.在SellTicket类中重写run()方法实现卖票:判断票数大于0,就卖票,告知是哪一个窗口;
卖票后,总票数-1;用死循环让卖票的动作一直执行

3.定义一个测试类SellTicketDemo,里面有main方法:创建SellTicket类的对象;
创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口的名称;
启动线程
 */
public class SellTicketDemo {
	public static void main(String[] args) {
//		创建SellTicket类的对象
		SellTicket st = new SellTicket();
//		创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口的名称'
		Thread t1 = new Thread(st,"窗口1");
		Thread t2 = new Thread(st,"窗口2");
		Thread t3 = new Thread(st,"窗口3");
//		启动线程
		t1.start();
		t2.start();
		t3.start();
	}
}

//SellTicket.java
//定义一个类SellTicket,实现Runnable接口,里面定义一个成员变量:
//private int tickets=100
public class SellTicket implements Runnable{
	private int tickets = 100;
	public void run() {
//		判断票数大于0,就卖票,告知是哪一个窗口;
//		卖票后,总票数-1;
//		用死循环让卖票的动作一直执行
		while(true) {
			if(tickets > 0) {
				System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
				tickets--;
			}
		}
		
	}
}

问题1:卖票间隔一秒

public void run() {
//		判断票数大于0,就卖票,告知是哪一个窗口;
//		卖票后,总票数-1;
//		用死循环让卖票的动作一直执行
		while(true) {
			if(tickets > 0) {
				//通过sleep()方法模拟出票时间
				try {
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
				tickets--;
			}
		}
		
	}

问题2:一张票卖出三次

public void run() {
//		判断票数大于0,就卖票,告知是哪一个窗口;
//		卖票后,总票数-1;
//		用死循环让卖票的动作一直执行
		
		//相同的票出现了多次
		while(true) {
			//tickets=100,t1,t2,t3
			//t1抢到cpu执行权
			if(tickets > 0) {
				
				//通过sleep()方法模拟出票时间
				try {
					//t1休息
					//t2抢到cpu执行权,t2休息
					//t3抢到cpu执行权,t3休息
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//假设线程按照顺序苏醒,t1抢到cpu执行权,在控制台输出100
				System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
				//t2抢到cpu的执行权,在控制台输出100
				//t3抢到cpu的执行权,在控制台输出100
				tickets--;
				//如果三个线程按顺序执行,执行三次--操作,票就变成了97
			}
		}
		
	}

问题3:出现票为-1的情况

//假设线程按照顺序苏醒,t1抢到cpu执行权,在控制台输出1
				//假设t1继续抢到cpu执行权,在tickets-- = 0
				//假设t2抢到cpu执行权,在控制台输出0
				//假设t1继续抢到cpu执行权,在tickets-- = -1
				System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
				tickets--;

问题原因:线程执行的随机性

数据安全问题的解决

是否是多线程环境

是否有共享数据

是否有多条语句操作共享数据

解决

引用锁机制:同步代码块实现

synchronized(对象){
 多条语句操作共享数据的代码;
}
//定义锁对象
	private Object obj = new Object();
	public void run() {
//		判断票数大于0,就卖票,告知是哪一个窗口;
//		卖票后,总票数-1;
//		用死循环让卖票的动作一直执行
		
		//相同的票出现了多次
		while(true) {
			synchronized(obj) {
				//t1进来后,把这段代码锁上
				if(tickets > 0) {
					
					//通过sleep()方法模拟出票时间
					try {
						//t1休息
						//t2抢到cpu执行权,t2休息
						//t3抢到cpu执行权,t3休息
						Thread.sleep(100);
					} catch (InterruptedException e) {
						// TODO Auto-generated catch block
						e.printStackTrace();
					}
					//假设线程按照顺序苏醒,t1抢到cpu执行权,在控制台输出1
					//假设t1继续抢到cpu执行权,在tickets-- = 0
					//假设t2抢到cpu执行权,在控制台输出0
					//假设t1继续抢到cpu执行权,在tickets-- = -1
					System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
					tickets--;
				}
				//t1出来后,这段代码就被释放
			}
			
		}
		
	}

当线程很多时,因为每个线程都会判断同步上的锁,耗费资源,会降低程序的运行效率

同步方法

把synchronized关键字加到方法上

同步方法的锁对象是this

public void run() {
//		判断票数大于0,就卖票,告知是哪一个窗口;
//		卖票后,总票数-1;
//		用死循环让卖票的动作一直执行
		
		//相同的票出现了多次
		while(true) {
			if(x %2 ==0) {
				synchronized(obj) {
					//t1进来后,把这段代码锁上
					if(tickets > 0) {
						
						//通过sleep()方法模拟出票时间
						try {
							//t1休息
							//t2抢到cpu执行权,t2休息
							//t3抢到cpu执行权,t3休息
							Thread.sleep(100);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						//假设线程按照顺序苏醒,t1抢到cpu执行权,在控制台输出1
						//假设t1继续抢到cpu执行权,在tickets-- = 0
						//假设t2抢到cpu执行权,在控制台输出0
						//假设t1继续抢到cpu执行权,在tickets-- = -1
						System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
						tickets--;
					}
					//t1出来后,这段代码就被释放
				}
			}else {

				sellTicket();
			}
			
			
		}
		
		
	}
	public synchronized void sellTicket() {
		synchronized(obj) {
			//t1进来后,把这段代码锁上
			if(tickets > 0) {
				
				//通过sleep()方法模拟出票时间
				try {
					//t1休息
					//t2抢到cpu执行权,t2休息
					//t3抢到cpu执行权,t3休息
					Thread.sleep(100);
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				//假设线程按照顺序苏醒,t1抢到cpu执行权,在控制台输出1
				//假设t1继续抢到cpu执行权,在tickets-- = 0
				//假设t2抢到cpu执行权,在控制台输出0
				//假设t1继续抢到cpu执行权,在tickets-- = -1
				System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
				tickets--;
			}
			//t1出来后,这段代码就被释放
		}
	}

this:非静态方法锁对象

同步静态方法

把synchronized关键字加到静态方法上

static synchronized 返回值类型 方法名(方法参数)

SellTicket.class:静态方法的同步锁对象(类名.class)

synchronized(SellTicket.class) {

线程安全的类

StringBuffer

线程安全,可变的字符序列,JDK5开始,被StringBuilder类替代,更快但不执行同步

Vector

被同步

如果不使用同步,使用ArrayList代替

Hashtable

被同步

不需要线程同步,使用HashMap代替

Lock锁

提供比使用synchroniezd方法和语句可以获得更广泛的锁操作

void lock():获得锁

void unlock():释放锁

是接口,不能直接实例化,采用它的实现类ReentranLock实例化

ReentranLock():创建ReentranLock实例

public class SellTicket implements Runnable{
	
	private int tickets = 100;
	private int x = 0;
	private Lock lock = new ReentrantLock();
	
	public void run() {
//		判断票数大于0,就卖票,告知是哪一个窗口;
//		卖票后,总票数-1;
//		用死循环让卖票的动作一直执行
		
		//相同的票出现了多次
		while(true) {
			if(x %2 ==0) {
				try {
					lock.lock();
					//t1进来后,把这段代码锁上
					if(tickets > 0) {
						
						//通过sleep()方法模拟出票时间
						try {
							//t1休息
							//t2抢到cpu执行权,t2休息
							//t3抢到cpu执行权,t3休息
							Thread.sleep(100);
						} catch (InterruptedException e) {
							// TODO Auto-generated catch block
							e.printStackTrace();
						}
						//假设线程按照顺序苏醒,t1抢到cpu执行权,在控制台输出1
						//假设t1继续抢到cpu执行权,在tickets-- = 0
						//假设t2抢到cpu执行权,在控制台输出0
						//假设t1继续抢到cpu执行权,在tickets-- = -1
						System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
						tickets--;
					}
					//t1出来后,这段代码就被释放
				}finally {
					lock.unlock();
				}
				
				
				
			}
			
			
		}
		
		
	}
	
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值