同步方法解决数据安全问题
一 案例:卖票
- 需求:某电影院目前正在上映国产大片,共有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();
}
}