卖票
- 案例需求
某电影院目前正在上映国产大片,共有100张票,而它有3个窗口卖票,请设计一个程序模拟该电影院卖票 - 实现步骤
- 定义一个类SellTicket实现Runnable接口,里面定义一个成员变量:private int tickets = 100;
- 在SellTicket类中重写run()方法实现卖票,代码步骤如下
- 判断票数大于0,就卖票,并告知是哪个窗口卖的
- 卖了票之后,总票数要减1
- 票卖没了,线程停止
- 定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下
- 创建SellTicket类的对象
- 创建三个Thread类的对象,把SellTicket对象作为构造方法的参数,并给出对应的窗口名称
- 启动线程
- 代码演示
private int tickets = 100;
@Override
public void run() {
while (true) {
if (tickets > 0) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
tickets--;
System.out.println(Thread.currentThread().getName() + "在卖票,还剩下" + tickets + "张票");
} else {
break;
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
Thread t3 = new Thread(st);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
卖票案例的问题
- 买票出现了问题
- 相同的票出现了多次
- 出现了负数的票
- 相同的票出现了多次
- 问题产生原因
线程执行的随机性导致的,可能在卖票过程中丢失CPU的执行权,导致出现问题
同步代码块解决数据安全问题
- 安全问题出现的条件
- 是多线程数据
- 是共享数据
- 有多条语句操作共享数据
- 如何解决多线程安全问题
- 基本思想:让程序没有安全问题的环境
- 如何实现
- 把多条语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可
- Java提供了同步代码块的方式来解决
- 同步代码块格式
synchronized(任意对象) {
多条语句操作共享数据的代码
}
-
synchronized(任意对象):就相当于给代码加锁了,任意对象就可以看成是一把锁
-
同步的好处和弊端
- 好处:解决了多线程的数据安全问题
- 弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
-
代码演示
public class SellTicket implements Runnable{
private int tickets = 100;
private Object obj = new Object();
@Override
public void run() {
while (true){
synchronized (obj){
if (tickets > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
tickets--;
}else {
break;
}
}
}
}
}
public class Test {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
Thread t3 = new Thread(st);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
同步方法解决数据安全问题
- 同步方法的格式
同步方法:就是把synchronized关键字加到方法上
修饰符 synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步方法的锁对象是什么呢?
this
- 静态同步方法
同步静态方法:就是把synchronized关键字加到静态方法上
修饰符 static synchronized 返回值类型 方法名(方法参数) {
方法体;
}
同步静态方法的锁对象是什么呢?
类名.class
public class SellTicket implements Runnable{
private int tickets = 100;
@Override
public void run() {
while (true){
pick();
}
}
public synchronized void pick(){
if (tickets > 0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println(Thread.currentThread().getName() + "正在出售第" + tickets + "张票");
tickets--;
}else {
System.exit(0);
}
}
}
public class Test {
public static void main(String[] args) {
SellTicket st = new SellTicket();
Thread t1 = new Thread(st);
Thread t2 = new Thread(st);
Thread t3 = new Thread(st);
t1.setName("窗口1");
t2.setName("窗口2");
t3.setName("窗口3");
t1.start();
t2.start();
t3.start();
}
}
死锁
- 概述
线程思索是由于两个或者多个线程相互持有对方所需要的资源,导致这些线程处于等待状态而无法前往执行 - 什么情况下会产生死锁
- 资源有限
- 同步嵌套
- 代码演示
public class Demo {
public static void main(String[] args) {
Object objA = new Object();
Object objB = new Object();
new Thread(() -> {
while (true){
synchronized (objA){
synchronized (objB){
System.out.println("小明同学正在走路");
}
}
}
}).start();
new Thread(() -> {
while (true){
synchronized (objB){
synchronized (objA){
System.out.println("小红同学正在走路");
}
}
}
}).start();
}
}