需求:某车站出售舟山至宁波的车票,共一百张,有三个窗口卖票,请设计一个程序模拟窗口卖票
分析:
1.定义一个SellTicket类实现Runnable接口,并重写run方法,并定义车票总张数
2.在run方法中判断票是否大于0;
是:卖票,并告知哪个窗口卖出的,卖票后总票数减一
否:提示没票了
3.定义一个测试类,创建SellTicket类对象,并作为参数创建三个Thread类对象,赋予线程名
4.启动线程
SellTicket类
public class SellTicket implements Runnable{
//定义总张数
private int tickets=100;
@Override
public void run() {
while (true) {
//判断车票是否大于0
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + tickets + "张车票");
tickets--;
}
}
}
}
SellTicketDemo类
public class SellTicketDemo {
public static void main(String[] args) {
//创建SellTicket类对象
SellTicket st=new SellTicket();
//作为参数创建三个Thread类对象,赋予线程名
Thread t=new Thread(st,"窗口一");
Thread t1=new Thread(st,"窗口二");
Thread t2=new Thread(st,"窗口三");
//启动线程
t.start();
t1.start();
t2.start();
}
}
经过运行发现会出现两个问题:
1.三个线程卖出了相同号码的车票
2.卖出了负数的车票
导致两个问题出现的原因是线程的随机性导致的数据安全问题。
一、那么如何判断多线程程序是否有数据安全问题?
1.判断程序是否是多线程环境
2.判断程序是否有共享数据
3.判断程序是否有多条语句操作共享数据
二、如何解决多线程安全问题?
1.基本思想:让程序没有安全问题的环境
2.把多条语句操作共享数据的代码块锁起来,让任意时刻只能有一个线程执行
这里使用同步代码块的方式来解决
三、同步代码块
格式:
synchronized(任意对象){
多条语句操作共享数据的代码;
}
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看作是一把锁
修改后SellTicket类
import static java.lang.Thread.sleep;
public class SellTicket implements Runnable{
//定义总张数
private int tickets=100;
//定义一把锁
private Object obj=new Object();
@Override
public void run() {
synchronized (obj){
while (true) {
//判断车票是否大于0
if (tickets > 0) {
System.out.println(Thread.currentThread().getName() + "卖出第" + tickets + "张车票");
tickets--;
//模仿出票
try {
sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
}
这样就解决了以上两个问题。
四、同步代码块的优缺点
1.优点:解决了多线程的数据安全问题
2.缺点:当线程很多时,因为每个线程都会去判断同步代码块中的锁,很耗费资源,会降低程序的运行效率
五、同步方法
1.同步方法就是把synchronized关键字加在方法上
格式:
修饰符synchronized 返回值类型 方法名(方法参数){}
public synchronized void sellTiket(){}
同步方法的锁对象是 this
synchronized (this)
2.同步静态方法就是把synchronized关键字加在静态方法上
格式:
修饰符 static synchronized 返回值类型 方法名(方法参数){}
public static synchronized void sellTiket(){}
同步方法的锁对象是类名.class
synchronized (SellTicket.class)
浙公网安备 33010602011771号