需求:
某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,请设计一个程序模拟该电影院售票。
两种方式实现
继承Thread类
实现Runnable接口
//1:继承方式
class SellTicket extends Thread {
SellTicket(){super();}
SellTicket(String name){super(name);}
private static int tickets=100;
public void run() {
while(true){
if(tickets>0){
System.out.println(getName()+"正在售出第"+tickets--+"张票");
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st1=new SellTicket("窗口1");
SellTicket st2=new SellTicket("窗口2");
SellTicket st3=new SellTicket("窗口3");
st1.start();
st2.start();
st3.start();
}
}
//实现Runnable接口方式
class SellTicket implements Runnable {
private int tickets=100;
public void run() {
while(true){
if(tickets>0){
System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
}
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st=new SellTicket();
Thread st1 =new Thread(st,"窗口1");
Thread st2 =new Thread(st,"窗口2");
Thread st3 =new Thread(st,"窗口3");
st1.start();
st2.start();
st3.start();
}
}
在这里出现一个问题,在真正的买票过程中,我们的数据不可能实时传输,这样我们就需要让其售票后停顿一下
很显然,结果出现了重复的情况,这是为什么呢?
问题:
相同的票出现多次
CPU的一次操作必须是原子性的(简单的操作)
还出现了负数的票
随机性和延迟导致的
注意
•线程安全问题在理想状态下,不容易出现,但一旦出现对软件的影响是非常大的。
解决线程安全问题的基本思想
首先想为什么出现问题?(也是我们判断是否有问题的标准)
•是否是多线程环境
•是否有共享数据
•是否有多条语句操作共享数据
如何解决多线程安全问题呢?
•基本思想:让程序没有安全问题的环境。
•怎么实现呢?
•把多个语句操作共享数据的代码给锁起来,让任意时刻只能有一个线程执行即可。
同步代码块
•格式:
synchronized(对象){需要同步的代码;}
•同步可以解决安全问题的根本原因就在那个对象上。该对象如同锁的功能。
同步代码块的对象可以是哪些呢?
锁:synchronized
A:同步代码块
synchronized(对象) {
需要被同步的代码;
}
这里的锁对象可以是任意对象。
public class SellTicket implements Runnable {
private int tickets=100;
//线程安全 锁是关键
Object obj=new Object();
public void run() {
while(true){
synchronized(obj){
if(tickets>0){
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
}
}
}
}
}
同步的前提:
1:多个线程
2:多个线程使用的是同一个锁对象
同步的好处:
同步的出现解决多线程的安全问题
同步的弊端:
当线程很多的时候,每一个线程都会去判断同步上的锁,这是很浪费资源的,无形中会降低程序的运行效率
package Test_07;
class Demo{}//自己随意建的类
public class SellTicket implements Runnable {
private static int tickets=100;
Object obj=new Object();
Demo d=new Demo();
使用同步代码块 obj做锁
// public void run() {
// while(true){
// //synchronized(obj){
// synchronized(d){
// if(tickets>0){
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
// }
// }
// 同步代码块 d做锁
// public void run() {
// while(true){
// synchronized(d){
// if(tickets>0){
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
// }
// }
// 3现在我们将第二个同步块打包成方法,试试看可不可以运行
// private void FunctionSynchronized() {
// synchronized(d){
// if(tickets>0){
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
// }
// }
// }
//如果一个方法一进去就看到了代码被同步了,那么我就再想能不能把这个同步加在方法上呢?
// private synchronized void sellTicket() {
// if (tickets > 0) {
// try {
// Thread.sleep(100);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()
// + "正在出售第" + (tickets--) + "张票 ");
// }
// }
public void run() {
while(true){
int x=0;
if(x%2==0){
//synchronized(obj){
synchronized(d){
if(tickets>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
}
}
}else{
// synchronized(d){
// if(tickets>0){
// try {
// Thread.sleep(1000);
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
// }
// }
// }
FunctionSynchronized();
}
x++;
}
}
private static synchronized void FunctionSynchronized() {
if(tickets>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
}
}
}
public class SellTicketDemo {
public static void main(String[] args) {
SellTicket st=new SellTicket();
Thread st1 =new Thread(st,"窗口1");
Thread st2 =new Thread(st,"窗口2");
Thread st3 =new Thread(st,"窗口3");
st1.start();
st2.start();
st3.start();
}
}
B:同步方法
把同步加在方法上。这里的锁对象是this
private synchronized void FunctionSynchronized() {
if(tickets>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
}
}
C:静态同步方法
把同步加在方法上。
这里的锁对象是当前类的字节码文件对象(反射再讲字节码文件对象) SellTicket.class
private static synchronized void FunctionSynchronized() {
if(tickets>0){
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"正在售出第"+tickets--+"张票");
}
}