当前位置: 首页 > news >正文 企业网站建设流程介绍网络行业有哪些 news 2025/10/27 6:26:53 企业网站建设流程介绍,网络行业有哪些,工作室网站建设的意义,网上写作最好的网站一、ThreadPoolExecutor 线程池在工作中使用的频率很高#xff0c;因此#xff0c;有必要好好了解其原理。这篇博客将会告诉大家#xff0c;线程池是怎么实现线程的复用的#xff0c;以及创建线程池的几个入参的作用等等。那么#xff0c;话不多说#xff0c;直接进入正题…一、ThreadPoolExecutor 线程池在工作中使用的频率很高因此有必要好好了解其原理。这篇博客将会告诉大家线程池是怎么实现线程的复用的以及创建线程池的几个入参的作用等等。那么话不多说直接进入正题。 先看代码如下图所示 这是我创建的的一个线程池并且调用了ThreadPoolExecutor#execute()方法,入参为Runnable对象就可以启动线程处理我们的业务。当然ThreadPoolExecutor还有另外一个方法即ThreadPoolExecutor#submit()方法也可以传入Runnable对象。但是实际上submit()方法最终也会调用到execute()方法。因此这里以execute()为例作为入口来讲解。在此之前先看看ThreadPoolExecutor的构造方法做了些什么如下图所示 可以看出仅仅只是给属性赋值而已并没有做什么其他的操作对这些属性有些印象即可下文会用到。那就看看最核心的ThreadPoolExecutor#execute()方法如下图所示 首先是校验保证传入的Runnable对象不能为空然后调用ThreadPoolExecutor#workerCountOf()方法拿到当前线程池里面线程的个数判断是否小于corePoolSize这个corePoolSize属性就是在构造方法中设置的核心线程数如果小于则调用ThreadPoolExecutor#addWorker()方法传入Runnable对象和true如果线程池中当前线程数大于核心线程数则先判断线程池此时的状态是否是Running这里说下线程池和线程一样也是有状态的状态如下图所示 如果状态是RUNNING的话则调用workQueue.offer()方法workQueue是我们传入的阻塞队列将Runnable方法放入阻塞队列中如果使用的是有界队列则可能放入队列失败如果线程池状态为RUNNING并且放入阻塞队列成功则会double check即再次判断线程池此时的状态是否为RUNNING如果不是则从阻塞队列中移除该Runnable对象并调用ThreadPoolExecutor#reject()方法执行拒绝策略的逻辑反之再判断线程数是否为0如果是则调用ThreadPoolExecutor#addWorker()方法传入null和false如果不满足上述两个if逻辑则直接调用ThreadPoolExecutor#addWorker()方法传入Runnable对象和false如果addWorker()方法返回的是false表示添加失败则调用ThreadPoolExecutor#reject()方法执行拒绝策略的逻辑。这几个逻辑中都调用了ThreadPoolExecutor#addWorker()方法可以看出该方法很重要点进去看看如下图所示 在for循环中首先判断此时线程池的状态如果大于SHUTDOWN状态或者大于等于SHUTDOWN状态且传入的Runnable或者队列不为空都返回false表示添加Runnable对象失败。再进入另一个for循环中拿到当前线程数判断当前线程数是否大于最大容量CAPACITY即 (1 29) - 1 536870911如果小于的话再判断传入的参数是否是true如果是true则比较当前线程数是否大于核心线程数是则返回false表示添加Runnable对象失败如果是false则比较当前线程数是否大于最大线程数是则返回false也表示添加Runnable对象失败。如果不满足上述条件则进入下一个if逻辑。也就是ThreadPoolExecutor#compareAndIncrementWorkerCount()方法通过CAS设置当前线程数1如果成功则跳出retry走添加Runnable的逻辑如果CSA失败再次算出当前线程数和前面的算出的线程数是否一致不一致则继续循环。假设CAS成功了就可以看下面的逻辑了主要是如何添加Runnable对象的如下图所示 在上面的图中可以看出创建了一个Worker对象调用它的有参构造传入了Runnable对象看看它的构造如下图所示 在有参构造中也就是把传入的Runnable对象赋值给了firstTask属性并通过线程工厂创建了了一个新的线程并传入了this即Worker对象自己实际上Worker是ThreadPoolExecutor的内部类也是Runnable接口的实现类如下图所示 再回到ThreadPoolExecutor#addWorker()方法的创建Worker对象还是先检查此时线程池的状态如果线程池的状态小于SHUTDOWN或者状态是SHUTDOWN并且传入的Runnable状态为空则将Worker对象那个放入worker中(HashSet类型)并将workerAdded设置为true这是最重要返回的结果表示添加Runnable对象是否成功。然后拿到了Worker对象的thread属性后调用了t.start()方法启动了线程实际上就是启动调用的了Worker#run()方法并返回了true表示添加Runnable对象成功看看Worker#run()方法如下图所示 在Worker#run()方法中又调用了ThreadPoolExecutor#runWorker()方法传入this即Worker对象本身。再看ThreadPoolExecutor#runWorker()方法如下图所示 首先是拿到Worker对象的firstTask这个属性实际上就是我们传入的Runnable并把该属性赋值给task并把Worker对象的firstTask设置为空避免后面重复调用该属性的run()方法。当然也有可能为空接着进入while循环有两个条件task不为空或者调用ThreadPoolExecutor#getTask()方法。由于我们传入的了Runnable对象因此task不为空最后就会调用task.run()方法执行我们在run()方法中写的的业务逻辑在finally中将task置为空。由于是while循环会再次进入判断while中的条件这次task为空则调用ThreadPoolExecutor#getTask()方法进到这个方法中看看如下图所示 在该方法中有几种情况会返回空 ① 此刻线程池的状态是大于等于STOP或者状态大于等于SHUTDOWN且队阻塞队列为空则返回null ② 此时线程池的线程数大于最大线程数且线程数大于1阻塞队列为空再通过CAS设置当前线程数-1成功则返回null否则继续for循环 ③ time和timedOut均为true且线程数大于1阻塞队列为空再通过CAS设置当前线程数-1成功则返回null否则for循环继续。 如果不满足上述三种情况则继续往下走当此时的线程数(Worker数量)大于核心线程数或者设置allowCoreThreadTimeOut属性为true则局部变量timed为true。到三元运算这边会判断timed的值如果为true则调用 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)而keepAliveTime也是我们设置的非核心线程线程的最大存活时间拿到的Runnable r可能为空也可能不为空因为设置了超时时间为keepAliveTime如果为false则调用 workQueue.take()一直阻塞在这里直到从队列可以拿到Runnable对象程序才继续往下走。如果得到的结果 r 如果不为空则直接返回并在while循环中被显式的调用即r.run()方法。不为空则继续循环不管r为不为空都会设置timedOut为true。timedOut为true的话可能会满足③的条件。 总结一下排除线程池是非RUNNING状态的话 ① 如果没有设置 allowCoreThreadTimeOut则该属性默认为false则在ThreadPoolExecutor#getTask()方法中如果此时线程池中worker的数量小于等于核心线程数的数量则程序会阻塞在workQueue.take()这里知道拿到Runnable对象才会返回并在while中执行Runnable#run()方法执行完 再次进入ThreadPoolExecutor#getTask()方法中阻塞直到有新的Runnable对象被放入阻塞队列中为止 ② 如果如果此时线程池中worker的数量大于核心线程数的数量(小于等于worker的数量是否大于最大线程的数量)则会调用 workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS)如果可以拿到Runnable则后续处理逻辑同①如果到了超时时间还是没有从workQueue中拿到Runnable对象timedOut true再次循环在第二次循环的时候 就满足了timed timedOut的条件如果此时workQueue为空则会通过CAS设置线程数-去1并返回null ③ worker的数量大于最大线程的数量的情况就直接走拒绝策略的逻辑了。 回到while循环中如果task为空且调用ThreadPoolExecutor#getTask()方法得到的结果也为空的话则会跳出while循环走下面的逻辑具体如下 看看ThreadPoolExecutor#processWorkerExit()方法代码如下图所示 completedTasks即完成的任务数加上再通过worker完成的任务数这就是个统计而已然后将worker从workers中移除。再就是判断线程池状态如果线程池此时的状态小于等于STOP进入if逻辑。判断completedAbruptly的值它表示突然完成程序只要进入过上文while循环中则completedAbruptly的值就会被赋值为false则满足if逻辑在这个里面会判断是否将allowCoreThreadTimeOut设置成了true如果是则min 0反之min corePoolSize。如果min 0且阻塞队列不为空则min会重新被赋值为1。判断此时线程数是否大于等于min是则直接返回反之调用ThreadPoolExecutor#addWorker()方法 传入null和false创建非核心线程。到这里就执行完了ThreadPoolExecutor#runWorker()方法woker线程就行完毕了线程被回收。 最后做一个总结 ① 使用ThreadPoolExecutor调用ThreadPoolExecutor#execute()方法传入一个Runnable对象如果如果此时线程池的线程数量小于核心线程池的数量则调用addWorker()方法传入的是true ② 如果大于等于核心线程数则将Runnable对象放入阻塞队列中 ③ 如果设置的是有界队列且达到了有界阻塞队列的最大值则放入有界阻塞队列失败(如果设置的无界队列则永远都不会进入到③中如果放入队列的任务非常之多则会导致OOM)。则会创建非核心线程来处理核心线程和非核心线程的区别就在于调用addWorker()方法时会依据传入的true还是false来判断此时的线程池的线程数是否是大于核心线程数还是最大线程数如果大于则直接返回false表示添加Runnable对象失败调用reject()方法执行拒绝策略的逻辑。 ④ 如果设置的是要有界队列且核心线程执行不过来 则最终会创建非核心线程当过了高峰期核心线程够用了则非核心线程再经过了 keepAliveTime长的时间还没有从队列中拿到Runnable对象就会跳出Worker#run()方法非核心线程被回收。而非核心线程会阻塞在 ThreadPoolExecutor#getTask()方法中,直到从阻塞队列中拿到Runnable对象执行Runnable对象并再次进入getTask()中陷入阻塞而不会被回收除非是⑤这种情况。 ⑤ 如果设置了allowCoreThreadTimeOut为true如果 task很少则核心线程也会慢慢被回收掉。 ⑥ 刚创建线程池此时是没线程(Worker)的除非调用ThreadPoolExecutor#execute()方法或者ThreadPoolExecutor#submit()方法才会创建worker对象。对于有些情况下可以做预热比如Tomcat定义的线程池它会自己循环调用ThreadPoolExecutor#execute()方法创建线程后续有请求过来就可以直接用线程池里面现有的线程进行处理而不需要先创建。 ⑦ 在某些情况下 会碰到父子线程ThreadLocal数据失效的问题。也就是说在创建了一个线程并将该线程对象交给了线程池管理并在该线程中设置了ThreadLocal的值然后在该线程中又创建了一个线程这种场景是有可能出现的。此时问题就来了在该子线程中是拿不到在父线程中设置的ThreadLocal的值的。这该怎么解决呢实际上 Spring给我们提供了解决方案想必很多小伙伴在使用线程的时候并不是使用的JDK提供的原生的线程池而是使用的Spring提供的线程池即ThreadPoolTaskExecutor。只需要给ThreadPoolTaskExecutor设置一个属性即可解决上述问题调用ThreadPoolTaskExecutor#setTaskDecorator()方法传入一个TaskDecorator对象该接口只有一个方法需要实现该方法在该方法中处理即可该方法返回的也是一个Runnable对象如下图所示 在ThreadPoolTaskExecutor#initializeExecutor()方法中就会用到传入的TaskDecorator对象如下图所示 由上图可知实际上Spring提供的ThreadPoolTaskExecutor是对ThreadPoolExecutor的包装真正干活的是ThreadPoolTaskExecutor的threadPoolExecutor属性在ThreadPoolTaskExecutor#initializeExecutor()方法中会先判断taskDecorator属性是否为空如果我们设置了就会走if逻辑然后就是创建ThreadPoolExecutor的实现类并最终赋值给threadPoolExecutor属性然后重写了ThreadPoolExecutor的execute()方法在execute()方法中会先调用taskDecorator.decorate()方法传入command对象(即一个Runnable对象)在这个的方法中我们就可以做操作 因为返回的是一个Runnable对象因此我们可以拿到这个入参而这个入参就是我们传入的Runnable对象 因此我们可以先调用ThreadLocal#get()方法拿到前面设置的值然后再创建一个新的Runnable对象再这个Runnable对象的run()方法中调用ThreadLocal#set()方法将拿到的值进行设置并调用我们传入的Runnable对象的run方法最后返回新创建的Runnable对象。最终ThreadPoolExecutor的实现类的execute最终会调用它父类即ThreadPoolExecutor#execute()方法传入的就是新创建的Runnable对象的run()方法这样 就可以再子线程中获取到父线程设置的ThreadLocal的值。代码如下 以上就是我对线程池的浅显认识如有错误欢迎批评指正 二、ScheduledThreadPoolExecutor ScheduledThreadPoolExecutor是ThreadPoolExecutor的子类可以实现任务的定时调度功能想必也是一个很常用的工具类可以简单聊聊。先看看使用如下图所示 实现调度的方法主要有上图中的三个在将之前先看看ScheduledThreadPoolExecutor的创建调用有参构造传入1再看看它的构造方法都干了些什么如下图所示 主要是有四个构造方法我用的是比较简单的一个传入的是核心线程的数量再调用父类的构造即ThreadPoolExecutor的构造方法参数为核心线程数(我们设置的1)最大线线程数是Integer.MAX_VALUE最大存活时间为0时间是TimeUnit.NANOSECONDS阻塞队列为DelayedWorkQueue对象。这个的DelayedWorkQueue对象看名字像是跟任务的延期有关亦或是实现定时调度的关键后面再验证我们的猜想。 接着再看核心的任务调度方法先看看 ScheduledThreadPoolExecutor#scheduleWithFixedDelay()方法如下图所示 先是入参commane非空校验delay参数校验再创建ScheduledFutureTask对象再看ScheduledFutureTask的构造方法前先看看 ScheduledThreadPoolExecutor#triggerTime()方法如下图所示 可以看出得到的结果是当前时间传入的时间(5s)的纳秒值再看的构造方法如下图所示 入参有四个分别是 ① r传入的Runnable对象 ② result为null ③ ns当前时间5秒对应的纳秒值赋值给 time成员属性 ④ period10s的纳秒值赋值给period成员属性 再调用ScheduledFutureTask父类的构造入下图所示 做了两个事 ① 调用Executors.callable()方法传入runnable, result得到的是RunnableAdapter对象赋值给callable属性 ② 将state的成员属性设置为FutureTask.NEW。 RunnableAdapter类用到了适配器设计模式实现了Callable接口具体如下图所示 调用RunnableAdapter#call()方法的时候最终就是调用我们传入的Runnable#run()方法。到这里ScheduledFutureTask的构造方法就讲完了。回到ScheduledThreadPoolExecutor#scheduleWithFixedDelay()方法中如下图所示 调用ScheduledThreadPoolExecutor#decorateTask()方法传入创建好的command和sft属性。如下图所示 在该方法中实际上什么也没做就只是返回了传入的task对象而已回到ScheduledThreadPoolExecutor#scheduleWithFixedDelay()方法中把decorateTask()方法返回的对象 t又赋值给 sft的outerTask 属性而t对象就是stf实际上sft中的outerTask属性持有它本身。再看ScheduledThreadPoolExecutor#delayedExecute()方法如下图所示 在该方法中首先判断线程池的状态一般线程池只要是RUNNING状态就不会进入if在代码块中那就会进入else代码块中将ScheduledFutureTask对象放入队列中再次判断线程池的状态还是会进入else调用ScheduledThreadPoolExecutor#ensurePrestart()方法如下图所示 在这个方法中核心就是调用addWorker()方法创建Worker对象用于处理任务。由于在前面已经往阻塞队列中放入了task对象即ScheduledFutureTask因此在Worker#run()方法中会调用ThreadPoolExecutor#runWorker()方法在该方法中最终会调用ThreadPoolExecutor#getTask()方法从阻塞队列中取任务而取得的任务就是ScheduledFutureTask对象并调用ScheduledFutureTask#run()方法看看这个方法如下图所示 看看ScheduledFutureTask#isPeriodic()方法返回了什么如下图所示 由于period是赋值过了因此isPeriodic()方法返回的结果是true。假设此时线程池的状态是没问题的的则进入到 下面的else if逻辑中执行ScheduledFutureTask父类的runAndReset()方法即FutureTask#runAndReset()方法如下图所示 前面构建ScheduledFutureTask的时候在父类的构造中初始化了state的状态就是FutureTask.NEW因此if的第一个条件不满足再看第二个条件通过CAS设置runner属性为当前线程假设设置失败才会进入if代码块中返回false如果设置成功则进入try代码块中首先获取callable这个callbale实际上就是前面说到的RunnableAdapter对象它就是对Callable的包装实际调用的是它的Runnable task属性的方法而这个task就是我们传入Runnable对象最终执行到我们在run()方法中的逻辑并将ran设个属性设置为true最终FutureTask#runAndReset()方法返回的结果就是true。再次回到ScheduledFutureTask#run()方法就会调用到ScheduledFutureTask#setNextRunTime()方法看名字就能猜到它是做啥的还是点进去看看如下图所示 就是计算下次程序要运行的时间time是上一次运行的时间即程序启动的时间5s而period则是10s因此最新的时间就是程序启动的时间15s再下次就是程序启动时间25s以此类推…执行完该方法后再就是执行ScheduledFutureTask#reExecutePeriodic()方法如下图所示 该方法传入outerTask而该属性就是ScheduledFutureTask本身在该方法中会再次将outerTask放入阻塞队列中并调用ScheduledFutureTask#ensurePrestart()方法。 总结一下其实到这里为止就只是知道ScheduledFutureTask会被放入阻塞队列中然后在Worker#run()方法中取出并运行Runnable#run()方法然后最多也只知道ScheduledFutureTask在被重新放放入队列之前会计算下次运行的时间即重新给time属性赋值。但是并没有体现按照我们设置的10s进行定时的调度那这个所谓的定时是不是在队列中体现的呢因为只有从队列中拿出了task才可以执行没有拿到程序就会被阻塞。想到这里那就看看获取task的api是怎么处理的看看DelayQueue#task()方法如下图所示 在该代码中首先是通过它的属性lock调用lock.lockInterruptibly()进行加锁(配合条件队列使用)然后是进入for死循环在该方法中调用q.peek()方法这个q是DelayQueue的属性是PriorityQueue类型带有排序功能。这里的排序猜都能猜到是按照任务触发的时间来排序的。按照最小堆二叉树排序的也就是说触发事件最短的会排在队首用于被获取。当然仅仅这样还不够因为获取到了最短就要执行的任务并不意味着该任务就是马上要运行。因此在代码中通过q.peek()方法得到task后先判断其是否为空发如果不为空则调用task的getDelay()方法而task是之前放入队列的ScheduledFutureTask对象因此调用的是ScheduledFutureTask#getDelay()方法如下图所示 算出距离当前时间执行还有多少纳秒并返回这个纳秒数delay。只有当得到的delay的值小于等于0的时候意味着task马上要执行了因此只要返回这个task即可如果delay大于0的话说明此时还不能返回该task因此需要在返回前先等待这个返回的delay的时间即调用available.awaitNanos(delay)等待delay长的时间再唤醒。由于是for死循环会重新上述操作这次就可以拿到task并且等待时间也是小于等于0因此满足条件直接返回这个task供使用。ScheduledThreadPoolExecutor另外两个方法调度实现逻辑差不多有时间可以自己研究研究这里我就不再多组赘述了。以上就是ScheduledThreadPoolExecutor源码实现任务调度的核心逻辑如有错误恳请批评指正在此感激不尽 文章转载自: http://www.morning.gcthj.cn.gov.cn.gcthj.cn http://www.morning.ycpnm.cn.gov.cn.ycpnm.cn http://www.morning.lfpzs.cn.gov.cn.lfpzs.cn http://www.morning.mywnk.cn.gov.cn.mywnk.cn http://www.morning.yzktr.cn.gov.cn.yzktr.cn http://www.morning.wgxtz.cn.gov.cn.wgxtz.cn http://www.morning.kpmxn.cn.gov.cn.kpmxn.cn http://www.morning.tbcfj.cn.gov.cn.tbcfj.cn http://www.morning.rqnhf.cn.gov.cn.rqnhf.cn http://www.morning.qytby.cn.gov.cn.qytby.cn http://www.morning.ktskc.cn.gov.cn.ktskc.cn http://www.morning.whclz.cn.gov.cn.whclz.cn http://www.morning.vtbtje.cn.gov.cn.vtbtje.cn http://www.morning.wkmyt.cn.gov.cn.wkmyt.cn http://www.morning.ckhyj.cn.gov.cn.ckhyj.cn http://www.morning.zwzlf.cn.gov.cn.zwzlf.cn http://www.morning.qmpbs.cn.gov.cn.qmpbs.cn http://www.morning.tpnxj.cn.gov.cn.tpnxj.cn http://www.morning.qlkjh.cn.gov.cn.qlkjh.cn http://www.morning.rgrdd.cn.gov.cn.rgrdd.cn http://www.morning.frcxx.cn.gov.cn.frcxx.cn http://www.morning.byrlg.cn.gov.cn.byrlg.cn http://www.morning.wypyl.cn.gov.cn.wypyl.cn http://www.morning.fygbq.cn.gov.cn.fygbq.cn http://www.morning.pfnrj.cn.gov.cn.pfnrj.cn http://www.morning.wrlff.cn.gov.cn.wrlff.cn http://www.morning.ffydh.cn.gov.cn.ffydh.cn http://www.morning.yrhsg.cn.gov.cn.yrhsg.cn http://www.morning.sgbk.cn.gov.cn.sgbk.cn http://www.morning.rlpmy.cn.gov.cn.rlpmy.cn http://www.morning.kqfdrqb.cn.gov.cn.kqfdrqb.cn http://www.morning.hengqilan.cn.gov.cn.hengqilan.cn http://www.morning.ljzss.cn.gov.cn.ljzss.cn http://www.morning.bpmdr.cn.gov.cn.bpmdr.cn http://www.morning.bhznl.cn.gov.cn.bhznl.cn http://www.morning.stxg.cn.gov.cn.stxg.cn http://www.morning.alive-8.com.gov.cn.alive-8.com http://www.morning.gqtw.cn.gov.cn.gqtw.cn http://www.morning.fnrkh.cn.gov.cn.fnrkh.cn http://www.morning.bbgr.cn.gov.cn.bbgr.cn http://www.morning.qlpyn.cn.gov.cn.qlpyn.cn http://www.morning.whpsl.cn.gov.cn.whpsl.cn http://www.morning.qsy37.cn.gov.cn.qsy37.cn http://www.morning.gjcdr.cn.gov.cn.gjcdr.cn http://www.morning.jrgxx.cn.gov.cn.jrgxx.cn http://www.morning.zdmlt.cn.gov.cn.zdmlt.cn http://www.morning.wwwghs.com.gov.cn.wwwghs.com http://www.morning.rgxn.cn.gov.cn.rgxn.cn http://www.morning.dlurfdo.cn.gov.cn.dlurfdo.cn http://www.morning.fpjxs.cn.gov.cn.fpjxs.cn http://www.morning.plqsz.cn.gov.cn.plqsz.cn http://www.morning.nnttr.cn.gov.cn.nnttr.cn http://www.morning.hgsylxs.com.gov.cn.hgsylxs.com http://www.morning.wyjhq.cn.gov.cn.wyjhq.cn http://www.morning.bbjw.cn.gov.cn.bbjw.cn http://www.morning.xnzmc.cn.gov.cn.xnzmc.cn http://www.morning.wwgpy.cn.gov.cn.wwgpy.cn http://www.morning.bklkt.cn.gov.cn.bklkt.cn http://www.morning.jhwwr.cn.gov.cn.jhwwr.cn http://www.morning.pqqhl.cn.gov.cn.pqqhl.cn http://www.morning.ptlwt.cn.gov.cn.ptlwt.cn http://www.morning.lmhcy.cn.gov.cn.lmhcy.cn http://www.morning.mlhfr.cn.gov.cn.mlhfr.cn http://www.morning.jzsgn.cn.gov.cn.jzsgn.cn http://www.morning.nydtt.cn.gov.cn.nydtt.cn http://www.morning.xsymm.cn.gov.cn.xsymm.cn http://www.morning.qqbjt.cn.gov.cn.qqbjt.cn http://www.morning.lwlnw.cn.gov.cn.lwlnw.cn http://www.morning.xqwq.cn.gov.cn.xqwq.cn http://www.morning.rqxch.cn.gov.cn.rqxch.cn http://www.morning.bfbl.cn.gov.cn.bfbl.cn http://www.morning.zxqyd.cn.gov.cn.zxqyd.cn http://www.morning.xuejitest.com.gov.cn.xuejitest.com http://www.morning.mfbcs.cn.gov.cn.mfbcs.cn http://www.morning.khzml.cn.gov.cn.khzml.cn http://www.morning.pmlgr.cn.gov.cn.pmlgr.cn http://www.morning.fqqcd.cn.gov.cn.fqqcd.cn http://www.morning.mbnhr.cn.gov.cn.mbnhr.cn http://www.morning.ghphp.cn.gov.cn.ghphp.cn http://www.morning.yrnll.cn.gov.cn.yrnll.cn 查看全文 http://www.tj-hxxt.cn/news/253112.html 相关文章: 网站的线下推广怎么做的做淘宝要网站? 那里可以做app网站品展示设计网站 中国农业建设中心网站湖南有线郴州网络有限公司 光谷软件园网站建设搜索引擎营销的模式有哪些 国家网站建设wordpress 文章有几种分类 阳谷做网站推广网站自己做的记者证 高校网站建设的优势和不足深圳做网站需要多少费用 重庆网站seo搜索引擎优化某网站注册需要邮箱是怎么弄 自建网站步骤塘下春华网站建设 池州网站建设兼职个人网站申请备案 北京市住房城乡建设门户网站公众号微信网站开发 烫画图案设计网站手机网站建设资讯 2018做网站赚钱不开发公司财务制度 三网合一网站方案小程序外包多少钱 做网站前端需要懂得精品一卡2卡三卡4卡二百信息网 建站网址什么意思定制做网站开发 物流公司网站模板唐山网站制作软件 做网站需要注册什么公司西安华为外包公司有哪些 江汉区建设局官方网站网络营销是什么专业的课 怎么下载网站源码上海网站建设中小型企业 网站建设建站知识洛阳最好的做网站的公司哪家好 西宁网站广西建设职业技术学院教育网站 辽宁平台网站建设平台wordpress 多备份 app网站的电话是什么cdn wordpress 回复 什么网站发布建设标准获客软件 发布建设网站wordpress托管站点 做商城网站应该用什么程序网站建设售后 医院网站建设作用企业网站策划书制作 怎么用服务器ip做网站淘宝关键词搜索工具 网站转发广东建设信息网站