先看一个案例:
需求:某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
思路:
- 定义一个类ticket实现Runnable接口,
里面定义一个成员变量: private int tickets= 100; - 在ticket类中重写run()方法实现卖票, 代码步骤如下:
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:卖了票之后,总票数要减1
C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行 - 定义一个测试类, 里面有main方法,代码步骤如下
A:创建SellTicket类的对象
B:创建三个Thread类的对象,把SelITicke对象作为构造方法的参数,
并给出对应的窗口名称
C:启动线程
先看一个初步的实现:
ticket类:
package 卖票;
//定义一个类SellTicket实现Runnable接口,
//里面定义一个成员变量: private int tickets= 100;
public class ticket implements Runnable{
private int tickets=100;
@Override
public void run() {
//A:判断票数大于0,就卖票,并告知是哪个窗口卖的
//B:卖了票之后,总票数要减1
//C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
while(true) {
if(tickets>0) {
int temp=101-tickets;
System.out.println(Thread.currentThread().getName()+"正在出售第"+temp+"张票");
tickets--;
}
}
}
}
main类:
package 卖票;
/*
* 需求:某电影院目前正在上映国产大片,共有100张票,
* 而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
思路:
1. 定义一个类SellTicket实现Runnable接口,
里面定义一个成员变量: private int tickets= 100;
2. 在ellTicket类中重写run()方法实现卖票, 代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:卖了票之后,总票数要减1
C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
3. 定义一个测试类, 里面有main方法,代码步骤如下
A:创建SellTicket类的对象
B:创建三个Thread类的对象,把SelITicke对象作为构造方法的参数,
并给出对应的窗口名称
C:启动线程
*/
public class ticketDemo {
public static void main(String[] args) {
ticket t=new ticket();
Thread t1=new Thread(t,"窗口一");
Thread t2=new Thread(t,"窗口二");
Thread t3=new Thread(t,"窗口三");
t1.start();
t2.start();
t3.start();
}
}
运行完会发现这个程序存在两个问题:
1.出现负数的票
2.一张票卖多次
出现问题满足的三个条件:
- 是否是多线程环境
- 是否有数据共享
- 是否有多条语句操作共享数据
显然这里只能破坏第三个条件
解决方法:把多条语句操作共享数据的代码锁起来,让任意时刻只能有一个线程执行(也就是操作系统中讲的互斥访问)
- JAVA正好提供了同步代码块的方式来解决
- 同步代码块格式:
synchronized (任意对象) {
//相当于给代码加锁,任意对象就是一把锁
多条语句操作共享数据的代码
}
下面看优化后的代码:
这里加上了sleep()方法模拟出票时间
ticket类:
package 卖票;
//定义一个类SellTicket实现Runnable接口,
//里面定义一个成员变量: private int tickets= 100;
public class ticket implements Runnable{
private int tickets=100;
private Object obj=new Object();
@Override
public void run() {
//A:判断票数大于0,就卖票,并告知是哪个窗口卖的
//B:卖了票之后,总票数要减1
//C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
while(true) {
synchronized (obj) {
if(tickets>0) {
//通过sleep()方法模拟出票时间
try {
Thread.sleep(100);
} catch (Exception e) {
e.printStackTrace();
}
int temp=101-tickets;
System.out.println(Thread.currentThread().getName()+"正在出售第"+temp+"张票");
tickets--;
}
}
}
}
}
main类:
package 卖票;
/*
* 需求:某电影院目前正在上映国产大片,共有100张票,
* 而它有3个窗口卖票,请设计一个程序模拟该电影院卖票
思路:
1. 定义一个类SellTicket实现Runnable接口,
里面定义一个成员变量: private int tickets= 100;
2. 在ellTicket类中重写run()方法实现卖票, 代码步骤如下
A:判断票数大于0,就卖票,并告知是哪个窗口卖的
B:卖了票之后,总票数要减1
C:票没有了,也可能有人来问,所以这里用死循环让卖票的动作一直执行
3. 定义一个测试类, 里面有main方法,代码步骤如下
A:创建SellTicket类的对象
B:创建三个Thread类的对象,把SelITicke对象作为构造方法的参数,
并给出对应的窗口名称
C:启动线程
*/
public class ticketDemo {
public static void main(String[] args) {
ticket t=new ticket();
Thread t1=new Thread(t,"窗口一");
Thread t2=new Thread(t,"窗口二");
Thread t3=new Thread(t,"窗口三");
t1.start();
t2.start();
t3.start();
}
}
由此便实现了三个窗口卖票,票不重复且不为负值,最主要的就是要对卖票这一过程加锁,实现互斥访问。