求1600字的读书文章不能全抄袭中间可以抄点开头不能抄袭。不要和我说百度啊,本人不会写

       &&计算机其实就是:『接受用户输叺指令与数据 经由中央处理器的数学与逻辑单元运算处理后,产生或储存有用的信息』包括简易型计算器、手机、卫星定位系统 (GPS)、ATM、PC、可携带电脑、pad 、psp等等。

输入单元:键盘、鼠标、阅读机、扫描仪、手写板、触控屏、usb接口等

主机:主板、内存、硬盘、电源、cpu

输出单元:屏幕、音箱、打印机

       cpu的主要单元:算数逻辑单元和控制单元算数逻辑单元负责程序运算和逻辑判断;控制单元负责协调各组件与各单え间的工作。

       cpu处理的数据来源:主存储器(内存)主存储器的数据来源:输入单元或硬盘。cpu处理后的数据必须先写回主存储器中最后传到輸出单元或写入硬盘。硬盘即是输入单元也是输出单元

      由上面的图示能知道,所有的单元都是由 CPU 内部的控制单元来负责协调的因此 CPU 是整个计算机系统的最重要部分。

      在应用方面 SPARC 架构的计算机常用于学术领域的大型工作站中,包括银行金融体系的主服务器也都有这类的計算机架构; 至的 PowerPC 架构的应用上例如新Sony公司出产的 Play Station

3(PS3)就是使用 PowerPC 架构的 Cell 处理器; 常使用的手机、 PDA、导航系统、网络设备(交换器、路由器等)等,几乎都是使用 ARM 架构的 CPU 喔!  目前世界上使用

      与 RISC 不同 CISC 在微指令集的每个小指令可以执行一些较低阶的硬件操作,指令数目多而且复杂 每條指令的长度并不相同。因为指令执行较为复杂所以每条指令花费的时间较长 但每条个别指

单有 CPU 是无法运作计算机的,所以计算机还需偠其他的接口设备才能够进行运作 除了前面稍微提到的输入/输出设备,以及 CPU 与主存之外还有什么接口设备呢? 其实最重要的接口设备昰主

板!因为主板负责将所有的设备通通连接在一起让所有的设备能够进行协调与沟通。 而主板上面最

重要的组件就是主板芯片组!这個芯片组可以将所有的设备汇集在一起!

? 储存装置:储存装置包括硬盘、软盘、光盘、磁带等等;

? 显示设备:显示适配器(显卡)对与玩 3D 遊戏来说是非常重要的的一环与显示器的分辨率有关;

         具有数个高速的 CPU,功能上虽不及超级计算机但也可用来处理大量资料和复杂的運算; 例如大型企业主机、全国性证券交易所等或者是大型企业的数据库服务器等,需要空调设备

         迷你计算机仍保有大型计算机同时支歭多用户的特性,但是主机可以放在一般作业场所 不必像前两个大型计算机需要特殊的空调场所。通常用来作为科学研究、工程分析与笁厂的流程管理等

         工作站的价格又比迷你计算机便宜许多,是针对特殊用途而设计的计算机在学术研究与工程分析方面相当常见。

        又稱为个人计算机体积最小,价格最低但功能还是五脏俱全的! 大致又可分为桌上型、笔记型等等。

        目前的个人计算机效能已经够快了甚至已经比工作站等级以上的计算机指令还快! 但是工作站计算机强调稳定性,并且运算过程要完全正确因此工作站以上等级的计算機在设计时考虑与pc是不同的(pc对稳定性和运行准确的要求没要其他计算机那么严格)! 这也是工作站等级以上计算机售价较贵的原因。

        计算机嘚运算能力是由速度来决定的而存放在计算机储存设备当中的数据容量也是有单位的。计算机依靠有没有通电来记录信息理论上它叧認识 0 不 1 而已。 0/1 称为 bit但 bit实在太小了, 在储存数据时每个简单数据都会使用到 8 个

磁盘上的每个磁道被等分为若干个弧段这些弧段便是磁盘嘚扇区。

        当磁盘旋转时磁头若保持在一个位置上,则每个磁头都会在磁盘表面划出一个圆形轨迹这些圆形轨迹就叫做磁道。

       相邻磁道の间并不是紧挨着的这是因为磁化单元相隔太近时磁性会产生相互影响,同时也为磁头的读写带来困难硬盘的一面通常有成千上万个磁道。在一些硬盘的参数描述如373~746这表示最外圈的磁道有746个扇区,而最里面的磁道有373个扇区

       在硬盘中无法被正常访问或不能被正确读寫的扇区都称为Badsector。一个扇区能存储512Bytes的数据如果在某个扇区中有任何一个字节不能被正确读写,则这个扇区为Badsector每个扇区自身带有数十个Bytes信息数据,包括标识(ID)、校验值和其它信息


      整个主板上面最重要的就是芯片组了!而芯片组通常又分为两个网桥来控制各组件的沟通, 分别是: (1)北桥:负责链接速度快的 CPU、主存储器、显卡等组件; (2)南桥:负责连接速度慢的硬盘、 USB、网卡等等

        与Intel不同的地方在于主存储器昰直接与 CPU 沟通而不通过北桥! CPU的数据都是来自于主存储器,因此 AMD 为了加速这两者的沟通所以将内存控制组件整合到 CPU当中, 理论上这样可鉯加速 CPU 不主存储器的传输速度

        多核心则是在一颗 CPU 外壳中嵌入了两个以上的运算单元;不同的微指令集会导致 CPU 工作效率的优劣。CPU频率也是效能的重要指标频率就是 CPU 每秒钟可以进行的工作次数。 所以频率越高表示这颗 CPU 单位时间内可以作更多的事情不同的 CPU 之间不能单纯的以頻率来判断运算效能,因为每颗 CPU的微指令集不相同架构不同,频率目前仅能用来比较同款 CPU 的速度!

cpu外频、倍频、超频

        外频是cpu与外部组件進行数据传输时的速度倍频是cpu内部用来加速工作效能的一个倍数,两者相乘是cpu的频率速度,

        内存的容量也是很重要的如果内存容量不够夶的话将会导致某些大容量数据无法被完整的加载, 此时内存中暂时没有使用的数据必须先释放使可用内存大于该数据,新数据才能够被加载所以,通常内存越大代表系统越快这是因为系统不用常常释放内存。 以服务器来说内存容量有时比 CPU 速度还要重要。

}

String这个类是Java中使用得最频繁的类之┅并且又是各大公司面试喜欢问到的地方,今天就来和大家一起学习一下String、StringBuilder和StringBuffer这几个类分析它们的异同点以及了解各个类适用的场景。下面是本文的目录大纲:

  一.你了解String类吗

  三.不同场景下三个类的性能测试

  四.常见的关于String、StringBuffer的面试题(辟谣网上流传的一些曲解String类的说法)

  若有不正之处,请多多谅解和指正不胜感激。

  请尊重作者劳动成果转载请标明转载地址:

  想要了解一个類,最好的办法就是看这个类的实现源代码String类的实现在

  打开这个类文件就会发现String类是被final修饰的:

  从上面可以看出几点:

  1)String類是final类,也即意味着String类不能被继承并且它的成员方法都默认为final方法。在Java中被final修饰的类是不允许被继承的,并且该类中的成员方法都默認为final方法在早期的JVM实现版本中,被final修饰的方法会被转为内嵌调用以提升执行效率而从Java SE5/6开始,就渐渐摈弃这种方式了因此在现在的Java SE版夲中,不需要考虑用final去提升方法调用效率只有在确定不想让该方法被覆盖时,才将方法设置为final

  2)上面列举出了String类中所有的成员属性,从上面可以看出String类其实是通过char数组来保存字符串的

  下面再继续看String类的一些方法实现:

  从上面的三个方法可以看出,无论是sub操、concat还是replace操作都不是在原有的字符串上进行的而是重新生成了一个新的字符串对象。也就是说进行这些操作后最原始的字符串并没有被改变。

  在这里要永远记住一点:

  “对String对象的任何改变都不影响到原对象相关的任何change操作都会生成新的对象”。

  在了解了於String类基础的知识后下面来看一些在平常使用中容易忽略和混淆的地方。

  想必大家对上面2个语句都不陌生在平时写代码的过程中也經常遇到,那么它们到底有什么区别和联系呢下面先看几个例子:

  这段代码的输出结果为

  为什么会出现这样的结果?下面解释┅下原因:

  在前面一篇讲解关于JVM内存机制的一篇博文中提到 在class文件中有一部分 来存储编译期间生成的 字面常量以及符号引用,这部汾叫做class文件常量池在运行期间对应着方法区的运行时常量池。

world"被存储在运行时常量池(当然只保存了一份)通过这种方式来将String对象跟引用绑定的话,JVM执行引擎会先在运行时常量池查找是否存在相同的字面常量如果存在,则直接将引用指向已经存在的字面常量;否则在運行时常量池开辟一个空间来存储该字面常量并将引用指向该字面常量。

  总所周知通过new关键字来生成对象是在堆区进行的,而在堆区进行对象生成的过程是不会去检测该对象是否已经存在的因此通过new来创建对象,创建出的一定是不同的对象即使字符串的内容是楿同的。

  那么看下面这段代码:

  这句 string += "hello";的过程相当于将原有的string变量指向的对象内容取出与"hello"作字符串相加操作再存进另一个新的String对象當中再让string变量指向新生成的对象。如果大家还有疑问可以反编译其字节码文件便清楚了:

  从这段反编译出的字节码文件可以很清楚哋看出:从第8行开始到第35行是整个循环的执行过程并且每次循环会new出一个StringBuilder对象,然后进行append操作最后通过toString方法返回String对象。也就是说这个循环执行完毕new出了10000个对象试想一下,如果这些对象没有被回收会造成多大的内存资源浪费。从上面还可以看出:string+="hello"的操作事实上会自动被JVM优化成:

  再看下面这段代码:

  反编译字节码文件得到:

  从这里可以明显看出这段代码的for循环式从13行开始到27行结束,并且new操作只进行了一次也就是说只生成了一个对象,append操作是在原有对象的基础上进行的因此在循环了10000次之后,这段代码所占的资源要比上媔小得多

  那么有人会问既然有了StringBuilder类,为什么还需要StringBuffer类查看源代码便一目了然,事实上StringBuilder和StringBuffer类拥有的成员属性以及成员方法基本相哃,区别是StringBuffer类的成员方法前面多了一个关键字:synchronized不用多说,这个关键字是在多线程访问时起到安全保护作用的,也就是说StringBuffer是线程安全的

彡.不同场景下三个类的性能测试

  从第二节我们已经看出了三个类的区别,这一小节我们来做个小测试来测试一下三个类的性能区别:

  上面提到string+="hello"的操作事实上会自动被JVM优化,看下面这段代码:

  下面对上面的执行结果进行一般性的解释:

  1)对于直接相加字符串效率很高,因为在编译器便确定了它的值也就是说形如"I"+"love"+"java"; 的字符串相加,在编译期间便被优化成了"Ilovejava"这个可以用javap -c命令反编译生成的class文件进行验证。

  对于间接相加(即包含字符串引用)形如s1+s2+s3; 效率要比直接相加低,因为在编译器不会对引用变量进行优化

  当然这個是相对的,不一定在所有情况下都是这样

  因此,这三个类是各有利弊应当根据不同的情况来进行选择使用:

  当字符串相加操作或者改动较少的情况下,建议使用 String str="hello"这种形式;

  当字符串相加操作较多的情况下建议使用StringBuilder,如果采用了多线程则使用StringBuffer。

  下媔是一些常见的关于String、StringBuffer的一些面试笔试题若有不正之处,请谅解和批评指正

1. 下面这段代码的输出结果是什么?

  输出结果为:true原洇很简单,"hello"+2在编译期间就已经被优化成"hello2"因此在运行期间,变量a和变量b指向的是同一个对象

2.下面这段代码的输出结果是什么?

  输出結果为:false由于有符号引用的存在,所以  String c = b + 2;不会在编译期间被优化不会把b+2当做字面常量来处理的,因此这种方式生成的对象事实上是保存在堆上的因此a和c指向的并不是同一个对象。javap -c得到的内容:

3.下面这段代码的输出结果是什么

  输出结果为:true。对于被final修饰的变量会在class攵件常量池中保存一个副本,也就是说不会通过连接而进行访问对final变量的访问在编译期间都会直接被替代为真实的值。那么String c = b + 2;在编译期间僦会被优化成:String c = "hello" + 2; 下图是javap -c的内容:

4.下面这段代码输出结果为:

  输出结果为false这里面虽然将b用final修饰了,但是由于其赋值是通过方法调用返囙的那么它的值只能在运行期间确定,因此a和c指向的不是同一个对象

5.下面这段代码的输出结果是什么?

  输出结果为(JDK版本 JDK6):

  這里面涉及到的是String.intern方法的使用在String类中,intern方法是一个本地方法在JAVA SE6之前,intern方法会在运行时常量池中查找是否存在内容相同的字符串如果存在则返回指向该字符串的引用,如果不存在则会将该字符串入池,并返回一个指向该字符串的引用因此,a和d指向的是同一个对象

  这个问题在很多书籍上都有说到比如《Java程序员面试宝典》,包括很多国内大公司笔试面试题都会遇到大部分网上流传的以及一些面試书籍上都说是2个对象,这种说法是片面的

  如果有不懂得地方可以参考这篇帖子:

  首先必须弄清楚创建对象的含义,创建是什麼时候创建的这段代码在运行期间会创建2个对象么?毫无疑问不可能用javap -c反编译即可得到JVM执行的字节码内容:

  很显然,new只调用了一佽也就是说只创建了一个对象。

  而这道题目让人混淆的地方就是这里这段代码在运行期间确实只创建了一个对象,即在堆上创建叻"abc"对象而为什么大家都在说是2个对象呢,这里面要澄清一个概念  该段代码执行过程和类的加载过程是有区别的在类加载的过程中,确實在运行时常量池中创建了一个"abc"对象而在代码执行过程中确实只创建了一个String对象。

  个人觉得在面试的时候如果遇到这个问题可以姠面试官询问清楚”是这段代码执行过程中创建了多少个对象还是涉及到多少个对象“再根据具体的来进行回答。

7.下面这段代码1)和2)的區别是什么

  1)的效率比2)的效率要高,1)中的"love"+"java"在编译期间会被优化成"lovejava"而2)中的不会被优化。下面是两种方式的字节码:

  可以看出在1)中只进行了一次append操作,而在2)中进行了两次append操作

}

在驱动程序中当多个线程同时訪问相同的资源时(驱动程序中的全局变量是一种典型的共享资源),可能会引发"竞态"因此我们必须对共享资源进行并发控制。Linux内核中解决并发控制的最常用方法是自旋锁与信号量(绝大多数时候作为互斥锁使用)

自旋锁与信号量"类似而不类",类似说的是它们功能上的楿似性"不类"指代它们在本质和实现机理上完全不一样,不属于一类

自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持调用者就一直循环查看是否该自旋锁的保持者已经释放了锁,"自旋"就是"在原地打转"而信号量则引起调用者睡眠,它把进程从运行队列仩拖出去除非获得锁。这就是它们的"不类"

但是,无论是信号量还是自旋锁,在任何时刻最多只能有一个保持者,即在任何时刻最哆只能有一个执行单元获得锁这就是它们的"类似"。

鉴于自旋锁与信号量的上述特点一般而言,自旋锁适合于保持时间非常短的情况咜可以在任何上下文使用;信号量适合于保持时间较长的情况,会只能在进程上下文使用如果被保护的共享资源只在进程上下文访问,則可以以信号量来保护该共享资源如果对共享资源的访问时间非常短,自旋锁也是好的选择但是,如果被保护的共享资源需要在中断仩下文访问(包括底半部即中断处理句柄和顶半部即软中断)就必须使用自旋锁。

与信号量相关的API主要有:

该函数初始化信号量并设置信号量sem的值为val

该函数用于获得信号量sem,它会导致睡眠(这个睡眠和下面所说的不知道有什么不同既然不能被其它地方唤醒,那么这个down囿什么用呢),因此不能在中断上下文使用;

该函数功能与down类似不同之处为,down不能被信号打断但down_interruptible能被信号打断;(这个能被信号打斷,有点疑惑我现在做的项目是使用的是被中断打断,不知道它这个地方所说的是什么意思)

该函数尝试获得信号量sem如果能够立刻获嘚,它就获得该信号量并返回0否则,返回非0值它不会导致调用者睡眠,可以在中断上下文使用

该函数释放信号量sem,唤醒等待者

与洎旋锁相关的API主要有:

该宏用于动态初始化自旋锁lock

该宏用于获得自旋锁lock,如果能够立即获得锁它就马上返回,否则它将自旋在那里,矗到该自旋锁的保持者释放;

该宏尝试获得自旋锁lock如果能立即获得锁,它获得锁并返回真否则立即返回假,实际上不再"在原地打转";

除此之外还有一组自旋锁使用于中断情况下的API。

2.5.1 自旋锁和互斥体

访问共享资源的代码区域称作临界区自旋锁(spinlock)和互斥体(mutex,mutual exclusion的缩写)是保護内核临界区的两种基本机制我们逐个分析。

自旋锁可以确保在同时只有一个线程进入临界区其他想进入临界区的线程必须不停地原哋打转,直到第1个线程释放自旋锁注意:这里所说的线程不是内核线程,而是执行的线程

下面的例子演示了自旋锁的基本用法:

与自旋锁不同的是,互斥体在进入一个被占用的临界区之前不会原地打转而是使当前线程进入睡眠状态。如果要等待的时间较长互斥体比洎旋锁更合适,因为自旋锁会消耗CPU资源在使用互斥体的场合,多于2次进程切换时间都可被认为是长时间因此一个互斥体会引起本线程睡眠,而当其被唤醒时它需要被切换回来。

因此在很多情况下,决定使用自旋锁还是互斥体相对来说很容易:

(1) 如果临界区需要睡眠呮能使用互斥体,因为在获得自旋锁后进行调度、抢占以及在等待队列上睡眠都是非法的;

(2) 由于互斥体会在面临竞争的情况下将当前线程置於睡眠状态因此,在中断处理函数中只能使用自旋锁。(第4章将介绍更多的关于中断上下文的限制)

下面的例子演示了互斥体使用的基夲方法:

为了论证并发保护的用法,我们首先从一个仅存在于进程上下文的临界区开始并以下面的顺序逐步增加复杂性:

(1) 非抢占内核,單CPU情况下存在于进程上下文的临界区;

(2) 非抢占内核单CPU情况下存在于进程和中断上下文的临界区;

(3) 可抢占内核,单CPU情况下存在于进程和中断上丅文的临界区;

(4) 可抢占内核SMP情况下存在于进程和中断上下文的临界区。

互斥体接口代替了旧的信号量接口(semaphore)互斥体接口是从-rt树演化而来的,在2.6.16内核中被融入主线内核

尽管如此,但是旧的信号量仍然在内核和驱动程序中广泛使用信号量接口的基本用法如下:

1. 案例1:进程上丅文,单CPU非抢占内核

这种情况最为简单,不需要加锁因此不再赘述。

2. 案例2:进程和中断上下文单CPU,非抢占内核

在这种情况下为了保护临界区,仅仅需要禁止中断如图2-4所示,假定进程上下文的执行单元A、B以及中断上下文的执行单元C都企图进入相同的临界区

图2-4 进程和中断上下文进入临界区

由于执行单元C总是在中断上下文执行,它会优先于执行单元A和B因此,它不用担心保护的问题执行单元A和B也鈈必关心彼此会被互相打断,因为内核是非抢占的因此,执行单元A和B仅仅需要担心C会在它们进入临界区的时候强行进入为了实现此目嘚,它们会在进入临界区之前禁止中断:

但是如果当执行到Point A的时候已经被禁止,local_irq_enable()将产生副作用它会重新使能中断,而不是恢复之前的Φ断状态可以这样修复它:

不论Point A的中断处于什么状态,上述代码都将正确执行

3. 案例3:进程和中断上下文,单CPU抢占内核

如果内核使能叻抢占,仅仅禁止中断将无法确保对临界区的保护因为另一个处于进程上下文的执行单元可能会进入临界区。重新回到图2-4现在,除了C鉯外执行单元A和B必须提防彼此。显而易见解决该问题的方法是在进入临界区之前禁止内核抢占、中断,并在退出临界区的时候恢复内核抢占和中断因此,执行单元A和B使用了自旋锁API的irq变体:

我们不需要在最后显示地恢复Point A的抢占状态因为内核自身会通过一个名叫抢占计數器的变量维护它。在抢占被禁止时(通过调用preempt_disable())计数器值会增加;在抢占被使能时(通过调用preempt_enable()),计数器值会减少只有在计数器值为0的时候,搶占才发挥作用

4. 案例4:进程和中断上下文,SMP机器抢占内核

到目前为止讨论的场景中,自旋锁原语发挥的作用仅限于使能和禁止抢占和Φ断时间的锁功能并未被完全编译进来。在SMP机器内锁逻辑被编译进来,而且自旋锁原语确保了SMP安全性SMP使能的含义如下:

在SMP系统上,獲取自旋锁时仅仅本CPU上的中断被禁止。因此一个进程上下文的执行单元(图2-4中的执行单元A)在一个CPU上运行的同时,一个中断处理函数(图2-4中嘚执行单元C)可能运行在另一个CPU上非本CPU上的中断处理函数必须自旋等待本CPU上的进程上下文代码退出临界区。中断上下文需要调用spin_lock()/spin_unlock():

除了有irq變体以外自旋锁也有底半部(BH)变体。在锁被获取的时候spin_lock_bh()会禁止底半部,而spin_unlock_bh()则会在锁被释放时重新使能底半部我们将在第4章讨论底半部。

实时(-rt)树也被称作CONFIG_PREEMPT_RT补丁集,实现了内核中一些针对低延时的修改该补丁集可以从www.kernel.org/pub/linux/kernel/projects/rt下载,它允许内核的大部分位置可被抢占但是用自旋锁代替了一些互斥体。它也合并了一些高精度的定时器数个-rt功能已经被融入了主线内核。详细的文档见http://rt.wiki.kernel.org/

为了提高性能,内核也定义叻一些针对特定环境的特定的锁原语使能适用于代码执行场景的互斥机制将使代码更高效。下面来看一下这些特定的互斥机制

原子操莋用于执行轻量级的、仅执行一次的操作,例如修改计数器、有条件的增加值、设置位等原子操作可以确保操作的串行化,不再需要锁進行并发访问保护原子操作的具体实现取决于体系架构。

}

我要回帖

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信