文山网站建设代理,贵阳模板建站定制,百度开户推广多少钱,重庆潼南网站建设公司1.火车站售票系统仿真
某火车站目前正在出售火车票#xff0c;共有50张票#xff0c;而它有3个售票窗口同时售票#xff0c;下面设计了一个程序模拟该火车站售票#xff0c;通过实现Runnable接口实现#xff08;模拟网络延迟#xff09;。 伪代码#xff1a;
Ticket类…1.火车站售票系统仿真
某火车站目前正在出售火车票共有50张票而它有3个售票窗口同时售票下面设计了一个程序模拟该火车站售票通过实现Runnable接口实现模拟网络延迟。 伪代码
Ticket类
Ticket 50//总共有张票
Run{
Whiletrue{
If ticket0;//判断是否有余票
输出“正在出售第ticket- -张票”
Else break;
Sleep1000ms;//模拟网络延迟
}
}
MyTicket类
Main{
New Thread1 窗口1//创建线程1
New Thread2 窗口2//创建线程2
New Thread3 窗口3//创建线程3
Start Thread1Thread2Thread3//开启三个线程
}运行结果仅截取一部分
上面的结果出现了相同票数的窗口,原因是ticket–不具有原子性当窗口1休眠时窗口3进入之后也休眠这时窗口1苏醒了进行到输出i的值这一步时窗口3苏醒了并抢到了执行权它也进行了输出i由于上一步还没进行到i-1的步骤因此窗口3和窗口1输出的值相同。因此需要限制修改票数的多并发。 下面一部分将介绍JVM中处理多线程并发时所用到的synchronized关键字并介绍其是如何实现线程锁。
2.Java中synchronized关键字
2.1 synchronized关键字控制进程锁的实现结果
根据上一部分在售票过程中发现相同票数的窗口为保证票数一致针对多线程将才用synchronized关键字对程序修改票数时上线程锁在程序完成修改票数后释放线程锁。 伪代码
Ticket类
Ticket 50
Run{
Whiletrue{
Synchronized{//线程锁
If ticket0;
输出“正在出售第ticket- -张票”
Else break;
}
Sleep1000ms;
}
}
MyTicket类
Main{
New Thread1 窗口1
New Thread2 窗口2
New Thread3 窗口3
Start Thread1Thread2Thread3
}运行结果仅截取一部分
根据结果发现使用synchronized关键字后售票数据正常程序运行结果无误。
2.2 synchronized关键字
2.2.1 synchronized关键字的用法
① 当synchronized作用在实例方法时监视器锁monitor便是对象实例this ② 当synchronized作用在静态方法时监视器锁monitor便是对象的Class实例因为Class数据存在于永久代因此静态方法锁相当于该类的一个全局锁 ③ 当synchronized作用在某一个对象实例时监视器锁monitor便是括号括起来的对象实例
2.2.2 synchronized的同步原理
查看上述代码反汇编后的结果
实现原理 1.monitorenter每个对象都是一个监视器锁monitor。当monitor被占用时就会处于锁定状态线程执行monitorenter指令时尝试获取monitor的所有权过程如下 ① 如果monitor的进入数为0则该线程进入monitor然后将进入数设置为1该线程即为monitor的所有者 ② 如果线程已经占有该monitor只是重新进入则进入monitor的进入数加1 ③ 如果其他线程已经占用了monitor则该线程进入阻塞状态直到monitor的进入数为0再重新尝试获取monitor的所有权 2.monitorexit执行monitorexit的线程必须是objectref所对应的monitor的所有者。指令执行时monitor的进入数减1如果减1后进入数为0那线程退出monitor不再是这个monitor的所有者。其他被这个monitor阻塞的线程可以尝试去获取这个 monitor 的所有权。monitorexit指令出现了两次第1次为同步正常退出释放锁第2次为发生异步退出释放锁
3.监视器Monitor
任何一个对象都有一个Monitor与之关联当且一个Monitor被持有后它将处于锁定状态。Synchronized在JVM里的实现都是基于进入和退出Monitor对象来实现方法同步和代码块同步都可以通过成对的MonitorEnter和MonitorExit指令来实现。 1.MonitorEnter指令插入在同步代码块的开始位置当代码执行到该指令时将会尝试获取该对象Monitor的所有权即尝试获得该对象的锁 2.MonitorExit指令插入在方法结束处和异常处JVM保证每个MonitorEnter必须有对应的MonitorExit ####3.1 Monitor实现原理 Monitor可以把它理解为一个同步工具也可以描述为一种同步机制它通常被描述为一个对象。与一切皆对象一样所有的Java对象是天生的Monitor每一个Java对象都有成为Monitor的潜质因为在Java的设计中 每一个Java对象被创建初就带了一把看不见的锁它叫做内部锁或者Monitor锁。也就是通常说Synchronized的对象锁MarkWord锁标识位为10其中指针指向的是Monitor对象的起始地址。在Java虚拟机HotSpot中Monitor是由ObjectMonitor实现的其主要数据结构如下位于HotSpot虚拟机源码ObjectMonitor.hpp文件C实现的
ObjectMonitor() {_header NULL;_count 0; // 记录个数_waiters 0,_recursions 0;_object NULL;_owner NULL;_WaitSet NULL; // 处于wait状态的线程会被加入到_WaitSet_WaitSetLock 0 ;_Responsible NULL ;_succ NULL ;_cxq NULL ;FreeNext NULL ;_EntryList NULL ; // 处于等待锁block状态的线程会被加入到该列表_SpinFreq 0 ;_SpinClock 0 ;OwnerIsThread 0 ;}ObjectMonitor中有两个队列_WaitSet 和 _EntryList用来保存ObjectWaiter对象列表 每个等待锁的线程都会被封装成ObjectWaiter对象 _owner指向持有ObjectMonitor对象的线程当多个线程同时访问一段同步代码时 1.首先会进入 _EntryList 集合当线程获取到对象的monitor后进入 _Owner区域并把monitor中的owner变量设置为当前线程同时monitor中的计数器count加1 2.若线程调用 wait() 方法将释放当前持有的monitorowner变量恢复为nullcount自减1同时该线程进入 WaitSet集合中等待被唤醒 3.若当前线程执行完毕也将释放monitor锁并复位count的值以便其他线程进入获取monitor(锁)
4.Java模拟PV原语
4.1信号量
count如果是正值则表示当前资源的个数如果是0表示有一个进程在执行临界区的代码也就是说这个进程位于临界区并且没有进程处于阻塞队列中。如果是负值这个值的绝对值abs(count)表示阻塞队列中进程的个数。 queue即为阻塞进程队列。当进程不能申请相应的资源是则使用P操作将自己插入阻塞队列中。当运行的进程执行完临界区代码时就执行V操作唤醒一个阻塞队列中的进程。
4.2功能实现
定义一个PV操作类syn。在这个类中通过构造函数设置count的值。这个类中并没有阻塞进程所在的queue是通过java的this.wait()与this.notifyAll()来实现的。
public class syn { //PV操作类int count0;//信号量syn(){}syn(int a){counta;}public synchronized void Wait(){ //关键字 synchronized 保证了此操作是一条【原语】count--;if(count0){//等于0 有一个进程进入了临界区try { //小于0abs(count)阻塞的进程数目this.wait();} catch (InterruptedException e) {e.printStackTrace();}}}public synchronized void Signal(){ //关键字 synchronized 保证了此操作是一条【原语】count;if(count0){//如果有进程阻塞this.notify();}}
}由此可以简单实现jvm中synchronized关键字的基本操作