Spark源码分析 -- SchedulableBuilder

SchedulableBuilder就是对Scheduleable tree的封装,
在Pool层面(中间节点), 完成对TaskSet的调度(FIFO, FAIR)
在TaskSetManager 层面(叶子节点), 完成对TaskSet中task的调度(locality)以及track(retry)

TaskSetManager

用于封装TaskSet, 主要提供对单个TaskSet内部的tasks的track和schedule
所以主要的接口,
resourceOffer, 对于一个resource offer, 如何schedule一个task来执行
statusUpdate, 对于task状态的track

/*** Tracks and schedules the tasks within a single TaskSet. This class keeps track of the status of* each task and is responsible for retries on failure and locality. The main interfaces to it* are resourceOffer, which asks the TaskSet whether it wants to run a task on one node, and* statusUpdate, which tells it that one of its tasks changed state (e.g. finished).** THREADING: This class is designed to only be called from code with a lock on the TaskScheduler* (e.g. its event handlers). It should not be called from other threads.*/
private[spark] trait TaskSetManager extends Schedulable {def schedulableQueue = null  def schedulingMode = SchedulingMode.NONEdef taskSet: TaskSetdef resourceOffer(execId: String,host: String,availableCpus: Int,maxLocality: TaskLocality.TaskLocality): Option[TaskDescription]def statusUpdate(tid: Long, state: TaskState, serializedData: ByteBuffer)def error(message: String)
}

 

ClusterTaskSetManager

ClusterScheduler上对于TaskSetManager的实现

1 addPendingTask
locality, 在schedule时候需要考虑, 应该优先执行尽可能近的task
所有未被执行的tasks, 都是pending task, 并且是安装不同locality粒度存储在hashmap中的
pendingTasksForExecutor, hashmap, 每个executor被指定的task
pendingTasksForHost,  hashmap, 每个instance被指定的task
pendingTasksForRack, hashmap, 每个机架被指定的task
pendingTasksWithNoPrefs, ArrayBuffer, 没有locality preferences的tasks, 随便在那边执行
allPendingTasks, ArrayBuffer, 所有的pending task
speculatableTasks, 重复的task, 熟悉hadoop的应该容易理解
可以继续看下addPendingTask, 如何把task加到各个list上去

addPendingTask(index: Int, readding: Boolean = false)
两个参数,
index, task的index, 用于从taskset中取得task
readding, 表示是否新的task, 因为当executor失败的时候, 也需要把task重新再加到各个list中, list中有重复的task是没有关系的, 因为选取task的时候会自动忽略已经run的task

 

2 resourceOffer
解决如何在taskset内部schedule一个task, 主要需要考虑的是locality, 直接看注释
其中比较意思的是, 对currentLocalityIndex的维护
初始时为0, PROCESS_LOCAL, 只能选择PendingTasksForExecutor
每次调用resourceOffer, 都会计算和前一次task launch之间的时间间隔, 如果超时(各个locality的超时时间不同), currentLocalityIndex会加1, 即不断的放宽
而代表前一次的lastLaunchTime, 只有在resourceOffer中成功的findTask时会被更新, 所以逻辑就是优先选择更local的task, 但当findTask总失败时, 说明需要放宽
但是放宽后, 当有比较local的task被选中时, 这个currentLocalityIndex还会缩小, 因为每次都会把tasklocality赋值给currentLocality

 

3 statusUpdate
应对statusUpdate, 主要是通过在clusterScheduler中注册的listener通知DAGScheduler
当然对于失败的task, 还要再加到pending list里面去

/*** Schedules the tasks within a single TaskSet in the ClusterScheduler. This class keeps track of* the status of each task, retries tasks if they fail (up to a limited number of times), and* handles locality-aware scheduling for this TaskSet via delay scheduling. The main interfaces* to it are resourceOffer, which asks the TaskSet whether it wants to run a task on one node,* and statusUpdate, which tells it that one of its tasks changed state (e.g. finished).** THREADING: This class is designed to only be called from code with a lock on the* ClusterScheduler (e.g. its event handlers). It should not be called from other threads.*/
private[spark] class ClusterTaskSetManager(sched: ClusterScheduler,val taskSet: TaskSet,clock: Clock = SystemClock)extends TaskSetManagerwith Logging
{
  val tasks = taskSet.tasksval numTasks = tasks.length

  // Set of pending tasks for each executor. These collections are actually// treated as stacks, in which new tasks are added to the end of the// ArrayBuffer and removed from the end. This makes it faster to detect// tasks that repeatedly fail because whenever a task failed, it is put// back at the head of the stack. They are also only cleaned up lazily;// when a task is launched, it remains in all the pending lists except// the one that it was launched from, but gets removed from them later.private val pendingTasksForExecutor = new HashMap[String, ArrayBuffer[Int]]// Set of pending tasks for each host. Similar to pendingTasksForExecutor,// but at host level.private val pendingTasksForHost = new HashMap[String, ArrayBuffer[Int]]// Set of pending tasks for each rack -- similar to the above.private val pendingTasksForRack = new HashMap[String, ArrayBuffer[Int]]// Set containing pending tasks with no locality preferences.val pendingTasksWithNoPrefs = new ArrayBuffer[Int]// Set containing all pending tasks (also used as a stack, as above).val allPendingTasks = new ArrayBuffer[Int]// Tasks that can be speculated. Since these will be a small fraction of total// tasks, we'll just hold them in a HashSet.val speculatableTasks = new HashSet[Int]
 
  // Figure out which locality levels we have in our TaskSet, so we can do delay schedulingval myLocalityLevels = computeValidLocalityLevels() // 当前TaskSet里面的task locality有哪些val localityWaits = myLocalityLevels.map(getLocalityWait) // 每个locality level默认的等待时间(从配置读)// Delay scheduling variables: we keep track of our current locality level and the time we// last launched a task at that level, and move up a level when localityWaits[curLevel] expires.// We then move down if we manage to launch a "more local" task.var currentLocalityIndex = 0    // 当前myLocalityLevels中的index, 从0开始, 从最小的开始schedulevar lastLaunchTime = clock.getTime()  // 记录最后launch task的时间, 用于后面会算超时, 如果发生超时, currentLocalityIndex+1 
 
  /*** Add a task to all the pending-task lists that it should be on. If readding is set, we are* re-adding the task so only include it in each list if it's not already there.*/private def addPendingTask(index: Int, readding: Boolean = false) {// Utility method that adds `index` to a list only if readding=false or it's not already theredef addTo(list: ArrayBuffer[Int]) {if (!readding || !list.contains(index)) { // 新的的task或在该list里面没有list += index}}var hadAliveLocations = falsefor (loc <- tasks(index).preferredLocations) {for (execId <- loc.executorId) {if (sched.isExecutorAlive(execId)) {addTo(pendingTasksForExecutor.getOrElseUpdate(execId, new ArrayBuffer)) // 首先加到相应的executor列表中hadAliveLocations = true}}if (sched.hasExecutorsAliveOnHost(loc.host)) {addTo(pendingTasksForHost.getOrElseUpdate(loc.host, new ArrayBuffer)) // 加到host的列表中 for (rack <- sched.getRackForHost(loc.host)) {addTo(pendingTasksForRack.getOrElseUpdate(rack, new ArrayBuffer)) // 加到Rack的列表中}hadAliveLocations = true}}if (!hadAliveLocations) { // 如果上面的选择都失败了, 或本来就没有preferred locations, 那就加到pendingTasksWithNoPrefs中// Even though the task might've had preferred locations, all of those hosts or executors// are dead; put it in the no-prefs list so we can schedule it elsewhere right away.addTo(pendingTasksWithNoPrefs)}if (!readding) { // 对于新的task, 需要加到allPendingTasks中allPendingTasks += index  // No point scanning this whole list to find the old task there}}
 
  /*** Dequeue a pending task for a given node and return its index and locality level.* Only search for tasks matching the given locality constraint.*/private def findTask(execId: String, host: String, locality: TaskLocality.Value): Option[(Int, TaskLocality.Value)] ={// 先从Executor
    for (index <- findTaskFromList(getPendingTasksForExecutor(execId))) { // findTaskFromList, Dequeue a pending task from the given list and return its index.return Some((index, TaskLocality.PROCESS_LOCAL))}// Node, 需要先check localityif (TaskLocality.isAllowed(locality, TaskLocality.NODE_LOCAL)) { // locality >= TaskLocality.NODE_LOCAL for (index <- findTaskFromList(getPendingTasksForHost(host))) {return Some((index, TaskLocality.NODE_LOCAL))}}// Rack, 需要先check locality if (TaskLocality.isAllowed(locality, TaskLocality.RACK_LOCAL)) {for {rack <- sched.getRackForHost(host)index <- findTaskFromList(getPendingTasksForRack(rack))} {return Some((index, TaskLocality.RACK_LOCAL))}}// Look for no-pref tasks after rack-local tasks since they can run anywhere.for (index <- findTaskFromList(pendingTasksWithNoPrefs)) {return Some((index, TaskLocality.PROCESS_LOCAL))}if (TaskLocality.isAllowed(locality, TaskLocality.ANY)) {for (index <- findTaskFromList(allPendingTasks)) {return Some((index, TaskLocality.ANY))}}// Finally, if all else has failed, find a speculative taskreturn findSpeculativeTask(execId, host, locality)}

 

  /*** Respond to an offer of a single executor from the scheduler by finding a task*/override def resourceOffer(execId: String,host: String,availableCpus: Int,maxLocality: TaskLocality.TaskLocality): Option[TaskDescription] ={if (tasksFinished < numTasks && availableCpus >= CPUS_PER_TASK) { // 前提是task没有执行完和有足够的available cores(>1)val curTime = clock.getTime()var allowedLocality = getAllowedLocalityLevel(curTime) // 取到当前allowed LocalityLevelif (allowedLocality > maxLocality) {  //  不能超出作为参数传入的maxLocality, 调用者限定allowedLocality = maxLocality   // We're not allowed to search for farther-away tasks}findTask(execId, host, allowedLocality) match { // 调用findTask, 并对返回值进行case, findTask逻辑很简单就是依次从不同的locality中取taskcase Some((index, taskLocality)) => {// Found a task; do some bookkeeping and return a task descriptionval task = tasks(index)val taskId = sched.newTaskId()// Figure out whether this should count as a preferred launchlogInfo("Starting task %s:%d as TID %s on executor %s: %s (%s)".format(taskSet.id, index, taskId, execId, host, taskLocality))// Do various bookkeepingcopiesRunning(index) += 1val info = new TaskInfo(taskId, index, curTime, execId, host, taskLocality)taskInfos(taskId) = infotaskAttempts(index) = info :: taskAttempts(index)// Update our locality level for delay schedulingcurrentLocalityIndex = getLocalityIndex(taskLocality) // 用当前Task的locality来更新currentLocalityIndex, 这里index有可能会减少, 因为taskLocality <= currentLocality lastLaunchTime = curTime      // 更新lastLaunchTime // Serialize and return the taskval startTime = clock.getTime()// We rely on the DAGScheduler to catch non-serializable closures and RDDs, so in here// we assume the task can be serialized without exceptions.val serializedTask = Task.serializeWithDependencies(task, sched.sc.addedFiles, sched.sc.addedJars, ser)val timeTaken = clock.getTime() - startTimeincreaseRunningTasks(1)logInfo("Serialized task %s:%d as %d bytes in %d ms".format(taskSet.id, index, serializedTask.limit, timeTaken))val taskName = "task %s:%d".format(taskSet.id, index)if (taskAttempts(index).size == 1)taskStarted(task,info)return Some(new TaskDescription(taskId, execId, taskName, index, serializedTask)) // 最终返回schedule得到的那个task}case _ =>}}return None}
 
  /*** Get the level we can launch tasks according to delay scheduling, based on current wait time.*/private def getAllowedLocalityLevel(curTime: Long): TaskLocality.TaskLocality = {while (curTime - lastLaunchTime >= localityWaits(currentLocalityIndex) &&  // 发生超时currentLocalityIndex < myLocalityLevels.length - 1){// Jump to the next locality level, and remove our waiting time for the current one since// we don't want to count it again on the next onelastLaunchTime += localityWaits(currentLocalityIndex)currentLocalityIndex += 1   // currentLocalityIndex 加 1}myLocalityLevels(currentLocalityIndex)}
 
  /*** Find the index in myLocalityLevels for a given locality. This is also designed to work with* localities that are not in myLocalityLevels (in case we somehow get those) by returning the* next-biggest level we have. Uses the fact that the last value in myLocalityLevels is ANY.*/def getLocalityIndex(locality: TaskLocality.TaskLocality): Int = { // 查询locality在myLocalityLevels中的indexvar index = 0while (locality > myLocalityLevels(index)) {index += 1}index}
  
  /*** Compute the locality levels used in this TaskSet. Assumes that all tasks have already been* added to queues using addPendingTask.*/
  // 仅仅从各个pending list中看看当前的taskset中的task有哪些preference locality, 从小到大 private def computeValidLocalityLevels(): Array[TaskLocality.TaskLocality] = {import TaskLocality.{PROCESS_LOCAL, NODE_LOCAL, RACK_LOCAL, ANY}val levels = new ArrayBuffer[TaskLocality.TaskLocality]if (!pendingTasksForExecutor.isEmpty && getLocalityWait(PROCESS_LOCAL) != 0) {levels += PROCESS_LOCAL}if (!pendingTasksForHost.isEmpty && getLocalityWait(NODE_LOCAL) != 0) {levels += NODE_LOCAL}if (!pendingTasksForRack.isEmpty && getLocalityWait(RACK_LOCAL) != 0) {levels += RACK_LOCAL}levels += ANYlogDebug("Valid locality levels for " + taskSet + ": " + levels.mkString(", "))levels.toArray}
  
  /** Called by cluster scheduler when one of our tasks changes state */override def statusUpdate(tid: Long, state: TaskState, serializedData: ByteBuffer) {SparkEnv.set(env)state match {case TaskState.FINISHED =>taskFinished(tid, state, serializedData)case TaskState.LOST =>taskLost(tid, state, serializedData)case TaskState.FAILED =>taskLost(tid, state, serializedData)case TaskState.KILLED =>taskLost(tid, state, serializedData)case _ =>}}
  def taskStarted(task: Task[_], info: TaskInfo) {sched.listener.taskStarted(task, info) }
}

 

Pool

一种对schedulableQueue的抽象, 什么是schedulable?
注释说的, 包含Pools and TaskSetManagers, 这里设计有问题, 你会发现Pools和TaskSetManagers的核心接口完全不同, 虽然TaskSetManagers里面也实现了这些接口, 但都是meanless的
简单理解成, 作者想要统一对待, 泛化Pools和TaskSetManagers, 所以这样做了

所以对于Pool, 可以理解为TaskSetManagers的容器, 当然由于Pool本身也是Schedulable, 所以容器里面也可以放Pool
核心接口getSortedTaskSetQueue, 通过配置不同的SchedulingAlgorithm来调度TaskSetManagers(或pool)

所以注意那些FIFO或FAIR都是用来调度TaskSet的, 所以Spark调度的基础是stage

/*** An interface for schedulable entities.* there are two type of Schedulable entities(Pools and TaskSetManagers)*/
private[spark] trait Schedulable {var parent: Schedulable// child queuesdef schedulableQueue: ArrayBuffer[Schedulable]def schedulingMode: SchedulingModedef weight: Intdef minShare: Intdef runningTasks: Intdef priority: Intdef stageId: Intdef name: Stringdef increaseRunningTasks(taskNum: Int): Unitdef decreaseRunningTasks(taskNum: Int): Unitdef addSchedulable(schedulable: Schedulable): Unitdef removeSchedulable(schedulable: Schedulable): Unitdef getSchedulableByName(name: String): Schedulabledef executorLost(executorId: String, host: String): Unitdef checkSpeculatableTasks(): Booleandef getSortedTaskSetQueue(): ArrayBuffer[TaskSetManager]def hasPendingTasks(): Boolean
}

 

package org.apache.spark.scheduler.cluster
//An Schedulable entity that represent collection of Pools or TaskSetManagers
private[spark] class Pool(val poolName: String,val schedulingMode: SchedulingMode,initMinShare: Int,initWeight: Int)extends Schedulablewith Logging {var schedulableQueue = new ArrayBuffer[Schedulable] // 用于buffer Schedulable, TaskSetManagervar schedulableNameToSchedulable = new HashMap[String, Schedulable]var priority = 0var stageId = 0var name = poolNamevar parent:Schedulable = nullvar taskSetSchedulingAlgorithm: SchedulingAlgorithm = { // SchedulingAlgorithm其实就是定义comparator,后面好将TaskSet排序schedulingMode match {case SchedulingMode.FAIR => new FairSchedulingAlgorithm() // Faircase SchedulingMode.FIFO =>new FIFOSchedulingAlgorithm() // FIFO}}override def addSchedulable(schedulable: Schedulable) { // 增加一个TaskSetManagerschedulableQueue += schedulableschedulableNameToSchedulable(schedulable.name) = schedulableschedulable.parent= this}override def removeSchedulable(schedulable: Schedulable) { // 删除一个TaskSetManager schedulableQueue -= schedulableschedulableNameToSchedulable -= schedulable.name}override def getSortedTaskSetQueue(): ArrayBuffer[TaskSetManager] = { // 返回排过序的TaskSetManager列表var sortedTaskSetQueue = new ArrayBuffer[TaskSetManager]val sortedSchedulableQueue = schedulableQueue.sortWith(taskSetSchedulingAlgorithm.comparator) // sortWith for (schedulable <- sortedSchedulableQueue) {sortedTaskSetQueue ++= schedulable.getSortedTaskSetQueue() // 这里的schedulable有可能也是pool, 所以需要递归调用}return sortedTaskSetQueue}
}

 

SchedulableBuilder

上面说了Pool里面可以是TaskSetManagers也可以是pool, 这样是不是可以形成tree
SchedulableBuilder就是对Schedulable Tree的封装, 通过TaskSetManagers(叶节点)和pools(中间节点), 来生成Schedulable Tree
这里只列出最简单的FIFO, 看不出tree的感觉
对于FIFO很简单, 直接使用一个Pool就可以, 把所有的TaskSet使用addSchedulable加进去, 然后排序读出来即可

这里没有列出Fair的实现, 比较复杂, 后面再分析吧

/*** An interface to build Schedulable tree* buildPools: build the tree nodes(pools)* addTaskSetManager: build the leaf nodes(TaskSetManagers)*/
private[spark] trait SchedulableBuilder {def buildPools()def addTaskSetManager(manager: Schedulable, properties: Properties)
}private[spark] class FIFOSchedulableBuilder(val rootPool: Pool)extends SchedulableBuilder with Logging {override def buildPools() {// nothing}override def addTaskSetManager(manager: Schedulable, properties: Properties) {rootPool.addSchedulable(manager)}
}

转载于:https://www.cnblogs.com/fxjwind/p/3507307.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/575625.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html设置照片模糊效果,CSS如何实现照片模糊?

在开发网页时&#xff0c;照片模糊处理会经常被使用&#xff0c;比如当我们背景图的模糊&#xff0c;当我们不想背景图片过于突出影响美观时&#xff0c;就可以设置将照片模糊处理&#xff0c;突出文字部分。那么 CSS 如何实现照片模糊呢&#xff1f;这篇文章 w3cschool 小编告…

rhel6用centos163 yum源

cd /etc/yum.repos.d/wget wget http://mirrors.163.com/.help/CentOS6-Base-163.reposed -i "s/\$releasever/6/" CentOS6-Base-163.repo 转载于:https://www.cnblogs.com/yanghuahui/p/3507313.html

女生学计算机未来出路,计算机真的已经烂大街了吗,女生学计算机没出路吗?...

假的。先说第一个问题&#xff1a;情况是&#xff0c;现在程序员是很多&#xff0c;但多半是中低端程序员&#xff0c;高端程序员供不应求&#xff0c;薪资一涨再涨。现在的程序员门槛一高再高&#xff0c;就是为了淘汰掉那些半吊子的程序员。如果你是靠从网上复制粘贴代码的“…

简明易懂的call apply

在iteye看到一篇对call解释得相当简明易懂&#xff0c;觉得得宣传一下 &#xff1a; http://uule.iteye.com/blog/1158829 一、方法的定义 call方法: 语法&#xff1a;call([thisObj[,arg1[, arg2[, [,.argN]]]]]) 定义&#xff1a;调用一个对象的一个方法&#xff0c;以另一…

计算机网络有哪些技能知识,网络基础知识及操作技能.ppt

网络基础知识及操作技能 主讲人 李春报 一、计算机网络概述 1、计算机网络的概念 计算机网络是把分布在不同地理位置上的计算机、终端&#xff0c;用通信设备和通信线路连结起来&#xff0c;再配以相应的网络软件&#xff0c;从而使众多计算机可以方便地互相传递信息&#xff0…

这些快捷键要学会使用啊

史上最全苹果键盘及Xcode快捷键整理 它们分别是command、option、control、shift以及esc除了command键上有标志之外其余四个可以通过这四个键对比比较出来。 谢谢原文作者提供较详细的快捷键说明。 常用的Xcode快捷键&#xff0c;工欲善其事&#xff0c;必先利其器。mac的键盘和…

目前计算机应用最广泛的区域是,自考《计算机应用基础》试题练习(一)

以下是关于2019年4月上海自考《计算机应用基础》串讲复习&#xff0c;由上海自考通整理发布&#xff0c;希望对即将参加自考的考生们能有帮助&#xff0c;更多的复习资料可点击查看历年真题。第一章一、选择1. 世界第一台电子计算机生产日期是( A )。A. 1946 B. 1945 C. 1947 D…

CSS元素选择器

1、在css元素选择器中飞&#xff0c;最重要的是HTML页面中的元素的定位&#xff1b; p { color: red; } h1{ color:blue; font-family:sans-serif; } 2、css选择器分组: 将 h2 和 p 选择器放在规则左边&#xff0c;然后用逗号分隔&#xff0c;就定义了一个规则。其右边的样式&a…

计算机基础知识二进制转换,计算机基础知识数制转换

二、数制转换1.十进制数到二进制数的转换(1)、整数部分 除2取余法(余数为0为止)&#xff0c;最后将所取余数按逆序排列。实例:将十进制数23转换为二进制数2| 232| 11 余数 12| 5 余数 12| 2余数 12|1 余数 00 余数 1结果为 (23)10 (10111)2(2)、小数部分 乘2取整法…

iOS基础 - 控制器

一、当两个控制器互为父子关系的时候&#xff0c;它们的view一般也是互为父子关系 比如想添加A控制器的view到B控制器的view上&#xff0c;就应该让A控制器成为B控制器的子控制器&#xff0c;而B控制器就称为A控制器的父控制器 //[B addChildViewController:A]; // A就会存在于…

计算机专业英语第07章,计算机专业英语电子教案第07章.ppt

计算机专业英语电子教案第07章Computer English Chapter 7 Programming Languages 复杂定语(从句)的翻译技巧之二 四、句子结构调整法 有时&#xff0c;原文句中一个中心词带有若干修饰成分&#xff0c;但它们既不是纯“并列”头系&#xff0c;也不是规则的“连环”关系&#x…

关机计算机专业,电脑关机后自动重启怎么回事

日常生活中&#xff0c;大部分的网友朋友们都碰到过电脑在正常使用时&#xff0c;突然电脑自动重启了&#xff0c;并且多次尝试强制关机都无效&#xff0c;令人费神。下面小编针对此问题的原因给出了几种方法&#xff0c;希望对大家有所帮助&#xff0c;快来看看吧&#xff01;…

PHP验证码常用的函数记录

1、绘制真彩画布&#xff0c;返回资源类型的图像标识符 resource imagecreatetruecolor ( int $width , int $height ) 例&#xff1a;$image imagecreatetruecolor( 30, 20 ); 2、为创建的画布分配背景颜色&#xff0c;参数中的颜色&#xff08;分别是红绿蓝&#xff09;&…

计算机无法打开打印机ip端口,讲述Win10电脑上无法选择打印机端口的解决方法...

我们在Win10电脑上使用打印机&#xff0c;进行打印材料是很平常的一件事&#xff0c;但是有用户在Win10电脑上&#xff0c;操作打印机时&#xff0c;出现了打印机端口无法选择的问题&#xff0c;不知道该怎么办&#xff1f;如果打印机端口无法正常选择的话&#xff0c;就代表我…

简单自定义标签步骤

自定义标签主要用于移除Jsp页面中的java代码。 使用自定义标签移除jsp页面中的java代码&#xff0c;只需要完成以下两个步骤&#xff1a;编写一个实现Tag接口的Java类(标签处理器类)。编写标签库描述符&#xff08;tld&#xff09;文件&#xff0c;在tld文件中对标签处理器类进…

计算机相关科幻小说,科幻小说家和计算机科学家总是用人工智能来迷惑我们,计算机可以...

科幻小说家和计算机科学家总是用人工智能来迷惑我们&#xff0c;计算机可以自我思考。相关句子1、从更广泛的意义上看&#xff0c;借助计算机科学&#xff0c;我们可以了解人类思想的本质和理性的意义&#xff0c;学会回答如何度过一生这个最古老的问题。把认知视为一种解决周围…

Android 4 学习(20):ActionBar

参考《Pro Android 4.0》 ActionBar 11.0之后&#xff0c;ActionBar在Activity中默认存在&#xff0c;可以在代码中设置其显示与否&#xff1a; ActionBar actionBar getActionBar(); // Hide the Action Bar actionBar.hide(); // Show the Action Bar actionBar.show(); …

计算机WIN7动态硬盘分区,win7硬盘分区教程

win7硬盘分区教程硬盘分区其实是对硬盘的一种格式化&#xff0c;进行了此步骤后才可以使用硬盘保存各种数据&#xff0c;下面是小编为大家详细介绍win7硬盘分区教程&#xff0c;欢迎大家阅读&#xff01;一、进入磁盘管理工具我们点击要对硬盘进行分区&#xff0c;我们首先得知…

一个经典的对象级别插件的开发

先新建一个js&#xff0c;名为jquery.lifocuscolor.js&#xff0c;编写一下代码&#xff1a; (function($) { $.fn.extend({ "focusColor": function(li_col) { var def_col "#ccc"; //默认获取焦点的色值 var lst_col "#fff"; //默认丢失焦点…

计算机的alu的作用,算术逻辑运算单元(ALU)的基本功能是什么 – 手机爱问

2004-12-28解释定点运算器的功能和组成部件解释定点算器包括ALU\阵列乘除器\寄存器\多路开关\三态缓冲器\数据总线等逻辑部件。运算器的设计,主要是围绕ALU和寄存器同数据总线之间如何传送操作数和运算结果进行的。在决定方案时,需要考虑数据传送的方便性和操作速度,在微型机和…