同步方法解决数据安全问题(案例:卖票)
同步方法解决数据安全问题
- 一 案例:卖票
-
- 1 卖票出现的问题:
- 2 问题出现的原因:
- 二 卖票案例的数据安全问题的解决:
-
- 1. 为什么出现问题?
- 2. 如何解决数据安全问题呢?
- 3. 怎么实现?
- 3. 利用同步代码块实现:
- 5. 同步的好处和弊端?
- 三 用代码写同步代码块
- 四 "同步代码锁"的使用
-
- 1 什么是同步方法锁
- 2 利用代码分析"动态同步方法锁"
-
- "SellTiket类"注意事项:
- 3 利用代码分析"静态同步方法锁"
-
- "SellTiket类"注意事项:
一 案例:卖票
- 需求:某电影院目前正在上映国产大片,共有100张票,而他有3个窗口卖票,请设计一个程序模拟该电影卖票
- 思路:
- (1). 定义一个SellTicket类实现Runnable接口,里面定义一个成员变量:private int tickets=100;
- (2). 在SellTicket类中重写run()方法实现卖票,代码步骤如下:
判断票数大于0,就卖票,并告知是哪个窗口卖的卖了票之后,总票数减1,票没有了,也可能有人来问,所以这里用死循环让卖票动作一直执行
- 步骤:定义一个测试类SellTicketDemo,里面有main方法,代码步骤如下:
- 创建SellTicket类的对象
- 创建3个Thread类的对象,把SellTicket对象作为构造方法的参数,并给对应的窗口名称
- 启动线程
1 卖票出现的问题:
- 相同的票出现了多次
- 出现了负数的票
2 问题出现的原因:
线程的随机性导致的
二 卖票案例的数据安全问题的解决:
1. 为什么出现问题?
这也是我们判断多线程程序是否有会有数据安全问题的标准
-
是否是多线程环境
本代码为主:
-
是否有共享数据
本代码为主:
-
是否有多条语句操作共享数据
2. 如何解决数据安全问题呢?
基本思想:让程序没有安全问题的环境
3. 怎么实现?
- 把多条语句操作共享数据的代码锁起来,让任意时刻只能有一个线程执行即可
- Java提供了同步代码块的方式解决
3. 利用同步代码块实现:
锁多条语句操作共享数据,可以使用同步代码块实现,格式:
synchronize(任意对象){多条语句操作共享数据的代码}
synchronize(任意对象):就相当于给代码加锁了,任意对象都可以看成一把锁
5. 同步的好处和弊端?
好处:解决了多线程的数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率
三 用代码写同步代码块
- SellTicket
package Demo;public class SellTicket implements Runnable { private int tickets = 100; private Object obj=new Object(); @Override public void run() { while (true) { //tickets=100; //t1,t2,t3 //假设t1抢到了CPU的执行权 //假设t2抢到了CPU的执行权,但是发现被obj锁上了,所以只能等待,即使t1休息也只能等 synchronized (obj) { //t1进来后,就会把这段代码给锁起来 if (tickets > 0) { try { Thread.sleep(100); //t1就要休息100毫秒 } catch (InterruptedException e) { e.printStackTrace(); } //t1休息好之后,窗口1正在出售第100张票 System.out.println("在" + Thread.currentThread().getName() + "号卖的票第" + tickets + "票"); tickets--;//tickets=99; } } //t1出来了,这段代码锁就被释放了 //然后就是t2或者t3开始抢,抢到后和t1一样的执行过程 } }}
图文解释:
- SellTicketDemo
package Demo;public class SellTicketDemo { public static void main(String[] args) { Runnable st = new SellTicket(); Thread tr = new Thread(st,"窗口1"); Thread tr2 = new Thread(st,"窗口2"); Thread tr3 = new Thread(st,"窗口3"); tr.start(); tr2.start(); tr3.start(); }}
四 "同步代码锁"的使用
1 什么是同步方法锁
就是把synchronized关键字加到方法上
- 格式:
修饰符synchronized返回值类型 方法名(方法参数){ }
- 同步方法锁的对象是什么:
this
2 利用代码分析"动态同步方法锁"
SellTiket类
动态同步方法所的对象是"this"关键字
package Demo;public class SellTicket implements Runnable { private int tickets = 100;// private Object obj = new Object(); private int x = 0; @Override public void run() { while (true) { if (x % 2 == 0) {// synchronized (obj) { synchronized (this) { if (tickets > 0) { try {Thread.sleep(100); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println("在" + Thread.currentThread().getName() + "号卖的票第" + tickets + "票"); tickets--; } } }else{ sellTicket(); } x++; } } private synchronized void sellTicket() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("在" + Thread.currentThread().getName() + "号卖的票第" + tickets + "票"); tickets--; } } }
"SellTiket类"注意事项:
SellTiketDemo
package Demo;public class SellTicketDemo { public static void main(String[] args) { Runnable st = new SellTicket(); Thread tr = new Thread(st,"窗口1"); Thread tr2 = new Thread(st,"窗口2"); Thread tr3 = new Thread(st,"窗口3"); tr.start(); tr2.start(); tr3.start(); }}
3 利用代码分析"静态同步方法锁"
- 就是把synchronized关键字加到静态方法上
- 格式:
修饰符 static synchronized返回值类型 方法名(方法参数){ }
- 同步静态方法的锁的对象是什么呢?
类名.class
SellTiket
静态同步方法所的对象是"该类名.class"关键字
package Demo;public class SellTicket implements Runnable { private static int tickets = 100;// private Object obj = new Object(); private int x = 0; @Override public void run() { while (true) { if (x % 2 == 0) {// synchronized (obj) { synchronized (SellTicket.class) { if (tickets > 0) { try {Thread.sleep(100); } catch (InterruptedException e) {e.printStackTrace(); } System.out.println("在" + Thread.currentThread().getName() + "号卖的票第" + tickets + "票"); tickets--; } } }else{ sellTicket(); } x++; } } private static synchronized void sellTicket() { if (tickets > 0) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("在" + Thread.currentThread().getName() + "号卖的票第" + tickets + "票"); tickets--; } } }
"SellTiket类"注意事项:
SellTiketDemo
package Demo;public class SellTicketDemo { public static void main(String[] args) { Runnable st = new SellTicket(); Thread tr = new Thread(st,"窗口1"); Thread tr2 = new Thread(st,"窗口2"); Thread tr3 = new Thread(st,"窗口3"); tr.start(); tr2.start(); tr3.start(); }}