版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u013317445/article/details/82628961
某电影院目前正在上映贺岁大片,共有100张票,而它有3个售票窗口售票,设计一个程序模拟该电影院售票。
//SaleTickets.java
public class SaleTickets implements Runnable {
public SaleTickets(){}
private int ticketsNumbers=100;
private int personTimes=0;
@Override
public void run() {
//while(true)模拟一直有票
while(true){
if(ticketsNumbers>0) {
System.out.println(++personTimes);
ticketsNumbers--;
System.out.println(Thread.currentThread().getName() + " " + "正在出售第:" + (100 - ticketsNumbers) + "张票 " + "余票:" + ticketsNumbers);
}
}
}
}
public class SaleTicketsDemo {
public static void main(String[] args){
//这个只有一个对象 所以自然是共享100张票的
//It's 资源对象
SaleTickets saletic= new SaleTickets();
//创建3个线程对象 3个线程去抢(java使用的线程调度模型是抢占式调度)
Thread window1= new Thread(saletic, "1号窗口");
Thread window2= new Thread(saletic, "2号窗口");
Thread window3= new Thread(saletic, "3号窗口");
window1.start();
window2.start();
window3.start();
}
}
//代码测试结果:运行正常
考虑了延迟:
在run()方法加入了sleep:
public void run() {
//while(true)模拟一直有票
while(true){
if(ticketsNumbers>0) {
System.out.println(++personTimes);
//模拟更真实的场景。正常是有延迟的
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketsNumbers--;
System.out.println(Thread.currentThread().getName() + " " + "正在出售第:" + (100 - ticketsNumbers) + "张票 " + "余票:" + ticketsNumbers);
}
}
出现了问题:
1.相同的一张票卖了多次:cpu的一次操作必须是原子性的
2.出现了负数票:随机性和延迟导致的
##如何解决线程安全问题
####哪些原因会导致出问题
多线程环境;
有共享数据;
有多条语句操作共享数据。
解决——同步代码块
思想:把操作共享数据的多条语句包成一个整体,当有线程在执行它时,不允许其他的线程再执行它。
synchronized(对象){
需要同步的代码(即,多条语句操作共享数据的代码);
}
这个对象就如同锁的功能,所以要多个线程过来遇到的是同一把锁 ——>共享同一把锁。
同步代码块的对象是任意对象。
public class SaleTickets implements Runnable {
private int ticketsNumbers = 100;
//定义同一把锁
private Object lock = new Object();//Note:定义了一个private的对象
@Override
public void run() {
while (true) {
//三个线程都可以走到这里
//假设window1此时抢到cpu的执行权,window1就要进来。门没锁,它可以进来。进来后就锁上了
//假设window2抢到了cpu的执行权,window2要进来,但是门是锁着的,就要等着
synchronized (lock) {
if (ticketsNumbers > 0)
try {
Thread.sleep(100);//延迟
} catch (InterruptedException e) {
e.printStackTrace();
}
ticketsNumbers--;
System.out.println(Thread.currentThread().getName()
+ " " + "正在出售第" + (100 - ticketsNumbers) + "张票");
//窗口1正在售出第1张票
}//window1出来了。开锁。
}
}
}
同步
同步的特点
前提:多个线程
多个线程使用的是同一个锁对象
同步的好处
同步的出现解决了多线程的安全问题。
####同步的弊端
当线程相当多时,因为每个线程都会去判断同步上的锁,这很耗费资源,无形中会降低程序的运行效率。
同步的方式
两种方式:同步代码块、同步方法
同步代码块
同步方法
可以把同步关键字synchronized加在方法上(加在修饰符之后)。
同步方法的锁对象是:this
如果是静态方法,同步方法的锁对象是:类的字节码文件。
到底用谁呢?如果锁对象是this,就可以考虑使用同步方法。否则能使用同步代码块的尽量使用同步代码块。