首页 文章 精选 留言 我的

精选列表

搜索[AI技能],共10000篇文章
优秀的个人博客,低调大师

java程序员应该具备的技能

1、基本语法 这包括static、final、transient等关键字的作用,foreach循环的原理等等。今天面试我问你static关键字有哪些作 用,如果你答出static修饰变量、修饰方法我会认为你合格,答出静态块,我会认为你不错,答出静态内部类我会认为你很好,答出静态导包我会对你很满 意,因为能看出你非常热衷研究技术。 最深入的一次,我记得面试官直接问到了我Volatile关 键字的底层实现原理(顺便插一句,面试和被面试本身就是相对的,面试官能问这个问题同时也让面试者感觉到面试官也是一个喜爱研究技术的人,增加了面试者对 公司的好感,我最终选择的就是问了这个问题的公司),不要觉得这太吹毛求疵了—-越简单的问题越能看出一个人的水平,别人对你技术的考量绝大多数都是以深度优先、广度次之为标准的,切记。 2、集合 非常重要,也是必问的内容。基本上就是List、Map、Set,问的是各种实现类的底层实现原理,实现类的优缺点。 集合要掌握的是ArrayList、LinkedList、Hashtable、HashMap、ConcurrentHashMap、 HashSet的实现原理,能流利作答,当然能掌握CopyOnWrite容器和Queue是再好不过的了。另外多说一 句,ConcurrentHashMap的问题在面试中问得特别多,大概是因为这个类可以衍生出非常多的问题,关于ConcurrentHashMap, 我给网友朋友们提供三点回答或者是研究方向: (1)ConcurrentHashMap的锁分段技术。 (2)ConcurrentHashMap的读是否要加锁,为什么。 (3)ConcurrentHashMap的迭代器是强一致性的迭代器还是弱一致性的迭代器。 我刚整理了一套2018最新的0基础入门和进阶教程,无私分享,加Java学习q-u-n :六七八,二四一,五六三 即可获取,内附:开发工具和安装包,以及系统学习路线图 3、框架 老生常谈,面试必问的东西。一般来说会问你一下你们项目中使用的框架,然后给你一些场景问你用框架怎么做,比如我想要在Spring初始化bean 的时候做一些事情该怎么做、想要在bean销毁的时候做一些事情该怎么做、MyBatis中$和#的区别等等,这些都比较实际了,平时积累得好、有多学习 框架的使用细节自然都不成问题。 如果上面你的问题答得好,面试官往往会深入地问一些框架的实现原理。问得最多的就是Spring AOP的实现原理,当然这个很简单啦,两句话就搞定的的事儿,即使你不会准备一下就好了。我遇到的最变态的是让我画一下Spring的Bean工厂实 现的UML图,当然面对这样一个有深度的问题,我是绝对答不出来的/(ㄒoㄒ)/~~ 4、数据库 数据库十有八九也都会问到。一些基本的像和 all的区别、left join、几种索引及其区别就不谈了,比较重要的就是数据库性能的优化,如果对于数据库的性能优化一窍不通,那么有时间,还是建议你在面试前花一两天专门 把SQL基础和SQL优化的内容准备一下。 不过数据库倒是不用担心,一家公司往往有很多部门,如果你对数据库不熟悉而基本技术又非常好,九成都是会要你的,估计会先把你放到对数据库使用不是要求非常高的部门锻炼一下。 5、Web方面的一些问题 Java主要面向Web端,因此Web的一些问题也是必问的。 我碰到过问得最多的两个问题是: 谈谈分布式Session的几种实现方式。 常用的四种能答出来自然是让面试官非常满意的。 另外一个常问的问题是:讲一下Session和Cookie的区别和联系以及Session的实现原理。这两个问题之外,web.xml里面的内容是重点,Filter、Servlet、Listener,不说对它们的实现原理一清二楚吧,至少能对它们的使用知根知底。另外,一些细节的方面比如get/post的区别、forward/重定向的区别、HTTPS的实现原理也都可能会被考察到。 6、数据结构和算法分析 数据结构和算法分析,对于一名程序员来说,会比不会好而且在工作中绝对能派上用场。数组、链表是基础,栈和队列深入一些但也不难,树挺重要的,比较 重要的树AVL树、红黑树,可以不了解它们的具体实现,但是要知道什么是二叉查找树、什么是平衡树,AVL树和红黑树的区别。记得某次面试,某个面试官和 我聊到了数据库的索引,他问我:你知道索引使用的是哪种数据结构实现吗? 我答到用的Hash表吧,答错。他又问,你知道为什么要使用树吗?我答到因为Hash表可能会出现比较多的冲突,在千万甚至是上亿级别的数据面 前,会大大增加查找的时间复杂度。而树比较稳定,基本保证最多二三十次就能找到想要的数据,对方说不完全对,最后我们还是交流了一下这个问题,我也明白了 为什么要使用树,这里不说,网友朋友们觉得索引为什么要使用树来实现呢? 至于算法分析,不会、不想研究就算了,记得某次面试对方问我,Collections.sort方法使用的是哪种排序方法,额,吐血三升。当然为了 显示我的博学,对算法分析也有一定的研究(⊙﹏⊙)b,我还是硬着头皮说了一句可能是冒泡排序吧。当然答案肯定不是,有兴趣的网友朋友们可以去看一下 Collections.sort方法的源代码,用的是一种叫做TimSort的排序法,也就是增强型的归并排序法。 7、Java虚拟机 出乎我的意料,Java虚拟机应该是很重要的一块内容,结果在这几家公司中被问到的概率几乎为0。要知道,我去年可是花了大量的时间去研究Java虚拟机的,光周志明老师的《深入理解Java虚拟机:JVM高级特性与最佳实践》,我就读了不下五遍。 言归正传,虽然Java虚拟机没问到,但我觉得还是有必要研究的,我就简单地列一个提纲吧,谈谈Java虚拟机中比较重要的内容: (1)Java虚拟机的内存布局 (2)GC算法及几种垃圾收集器 (3)类加载机制,也就是双亲委派模型 (4)Java内存模型 (5)happens-before规则 (6)volatile关键字使用规则 也许面试无用,但在走向大牛的路上,不可不会。 8、设计模式 本来以为蛮重要的一块内容,结果只在阿里巴巴B2B事业部面试的时候被问了一次,当时问的是装饰器模式。 当然咱们不能这么功利,为了面试而学习,设计模式在工作中还是非常重要、非常有用的,23种设计模式中重点研究常用的十来种就可以了,面试中关于设计模式的问答主要是三个方向: (1)你的项目中用到了哪些设计模式,如何使用。 (2)知道常用设计模式的优缺点。 (3)能画出常用设计模式的UML图。 9、多线程 这也是必问的一块了。因为三年工作经验,所以基本上不会再问你怎么实现多线程了,会问得深入一些比如说Thread和Runnable的区别和联 系、多次start一个线程会怎么样、线程有哪些状态。当然这只是最基本的,出乎意料地,几次面试几乎都被同时问到了一个问题,问法不尽相同。 总结起来是 这么一个意思: 假如有Thread1、Thread2、ThreaD3、Thread4四条线程分别统计C、D、E、F四个盘的大小,所有线程都统计完毕交给Thread5线程去做汇总,应当如何实现? 聪明的网友们对这个问题是否有答案呢?不难,java.util.concurrent下就有现成的类可以使用。 另外,线程池也是比较常问的一块,常用的线程池有几种?这几种线程池之间有什么区别和联系?线程池的实现原理是怎么样的?实际一些的,会给你一些具体的场景,让你回答这种场景该使用什么样的线程池比较合适。 最后,虽然这次面试问得不多,但是多线程同步、锁这块也是重点。synchronized和ReentrantLock的区别、 synchronized锁普通方法和锁静态方法、死锁的原理及排查方法等等。 10、JDK源码 要想拿高工资,JDK源码不可不读。上面的内容可能还和具体场景联系起来,JDK源码就是实打实地看你平时是不是爱钻研了。我面试过程中被问了不 少JDK源码的问题,其中最刁钻的一个问了我,String的hashCode()方法是怎么实现的,幸好我平时String源代码看得多,答了个大 概。 JDK源码其实没什么好总结的,纯粹看个人,总结一下比较重要的源码: (1)List、Map、Set实现类的源代码 (2)ReentrantLock、AQS的源代码 (3)AtomicInteger的实现原理,主要能说清楚CAS机制并且AtomicInteger是如何利用CAS机制实现的 (4)线程池的实现原理 (5)Object类中的方法以及每个方法的作用

优秀的个人博客,低调大师

设计模式(通往高手之路的必备技能)

‘从大处着眼,从小处着手’,以前对这句话一知半解,自从踏出校门走入社会,开始工作以来,有了越来越深的理解,偶有发现这句话用在程序开发中也有用,所以,近段时间开始尝试着分析jQuery源码,分析angularjs源码,学习设计模式。 设计模式的由来 看过GOF的总结的23种设计模式的人,都或多或少的有种似曾相识的感觉,事实确实如此,这些设计模式原来就有,是前人优秀的工作成果,只不过是GOF他们给这些原本就有的东西重新定义了一下,给予这些东西名称和原理,使之更容易被人理解和接受,这本身就体现了GOF的伟大,让好的东西更容易传播。 设计模式的定义 在面向对象软件设计过程中针对特定问题的简洁而优雅的解决方案。 如何学习设计模式 设计模式也并不是什么洪水猛兽,高不可攀,一个有一定经验的软件开发者都会在不知不觉中使用它,这可能中间经历了很多的尝试,当他看过设计模式后,会发现原来已经有人对它进行过总结。 使用设计模式实现的代码,使用一般的方法都能实现,所以使用设计模式,会无形中增加代码的量,尤其是不正确的使用,更会带来毁灭性的灾难,所以,一般的开发人员唯恐避之不及。 理解‘可复用的面向对象软件基础’ 设计模式的实现都遵循一条原则‘找出程序中变化的地方,并将它封装起来’。在程序设计中总分为可变的地方和不可变的地方,可变的地方我们往往将他封装起来,不可变的地方也即是代码稳定和不可变的部分的,往往这部分代码是可复用的。这也是标题《可复用的面向对象软件基础》的由来。 好了,废话不多说,下面进入常见的设计模式学习。 如何分辨设计模式 有时侯我们经常会遇到这样的问题,许多设计模式的实现看起来几乎一模一样,比如:代理模式和装饰者模式,策略模式和状态模式。 你不仅会大声问,他们有什么区别呢? 其实,从代码结构上看并没什么区别,就像一把手枪,你拿它来杀人,它就是凶器,你拿它来救人它就是武器。跟你的用途有关系,其实最根本的是你的意图。所以,在学习设计模式的时候,不要在在意代码的结构形式,要多留意模式的使用场景,在这种场景下解决了什么问题,多进行对比(使用前,使用后有何差别)。 重新审视JavaScript设计模式 JavaScript从开始被人当成为一种玩具语言,到后来发展为一门流行的可靠的语言。人们从开发做一些简单的交互,到后来Google做的第一个邮件系统,再到后来Google推出的angular框架的出现,js的威力在被人们认识的同时,伴随着浏览器支持js做更多的东西,与此同时它也变得痈肿起来,框架间各种复杂的依赖甚至能让你崩溃。随着ES6, TypeScript, CoffeScript 等各种转编译语言的兴起,无疑给前端开发者带来了不小的学习压力。像React,vue,angular这些当下流行的框架,大家都知道一些,但是要说有深入研究,不见得有多少人。17年年底我发现各个公司的在招人时,对开发人员的要求越来越高,要知道这些框架的原理,要知道某些具体的功能如何实现,同时对于设计模式的考察也越来越突出。所以,基于开发语言的使用环境,以及工作面试需要,我们不得不认真对待JavaScript常见的设计模式。 从前由于使用的局限性,和做的应用相对简单,js不被重视,js就更谈不上设计模式的问题。虽然,现在JavaScript被开发人员越来越重视,但是JavaScript设计模式的讨论还不是那么活跃,有研究和见地的还是少数人,但是研究过源码的同学就会知道,在vue,angular种设计模式已经相当普遍。 作为一个励志成为前端小牛的我,现在也甚是心痒。 目录 ‘大处着眼,小处着手’——设计模式系列 注:这是我个人对设计模式的认识和理解,仅代表个人观点个看法,不足之处欢迎大家指正,随着认识的加深这篇文章(包括设计模式其他系列文章)会不断地进行更新。 javascript 设计模式 面向对象的JavaScript 构造器 设计模式 外观 设计模式 工厂 设计模式 观察者 设计模式 单例 设计模式 持续更新中... 参考资料 学习JavaScript设计模式 腾讯全端 AlloyTeam 设计模式系列文章 JavaScript设计模式与开发实践 其它设计模式相关文章请转‘大处着眼,小处着手’——设计模式系列

优秀的个人博客,低调大师

系统架构技能之设计模式-组合模式

一、上篇回顾 我们上篇主要讲述了结构型模式中的外观模式,外观模式作为结构型模式中的一个简单又实用的模式,外观模式通过封装细节来提供大粒度的调用, 直接的好处就是,封装细节,提供了应用写程序的可维护性和易用性。外观模式一般应用在系统架构的服务层中,当我们是多个不同类型的客户端应用程序 时,比如一个系统既可以在通过Web的形式访问,也可以通过客户端应用程序的形式时,可能通过外观模式来提供远程服务,让应用程序进行远程调用, 这样通过外观形式提供服务,那么不管是什么样的客户端都访问一致的外观服务,那么以后就算是我们的应用服务发生变化,那么我们不需要修改没一个客 户端应用的调用,只需要修改相应的外观应用即可。 我们主要是讲述了以下的几种情况,使用外观模式可能更适合: 1、我们在使用第三方类库或者API的时候,我们通过本地的API接口的封装,来完成对第三方API接口的粗粒度外观对象,通过这个外观对象可以 很容易的完成服务的调用。 2、我们在架构设计的过程中,一次的功能访问可能需要同时的调用很多个对象,那么如果我们在服务调用的时候,能够在应用程序调用中一次就 能完成所有要同时调用的对象那该多好啊,外观模式无疑是最好的原则,特别是在分布式应用中,通过远程调用服务,通过外观模式降低应用程序与服务的 交互次数,同时可以降低应用程序的复杂性。 二、摘要 本文将会讲述结构性模式中的另外一个常用的模式-组合模式,我们平时在面向对象的设计中,我想有一个原则经常被提及就是,我们在设计的时候, 对象组合>类的继承,本篇将会将结合简单的实例来说明这方面的优势,并且完成对组合模式的主题思想的掌握。我们这样来简单的理解组合模式,组合模 式就是把一些现有的对象或者元素,经过组合后组成新的对象,新的对象提供内部方法,可以让我们很方便的完成这些元素或者内部对象的访问和操作。我 们也可以把组合对象理解成一个容器,容器提供各种访问其内部对象或者元素的API,我们只需要使用这些方法就可以操作它了。那么对象组合相比继承的 优势有哪些呢?可能具体的优势,也不是一句二句就能表述清楚的,还是我们来看看图形的可视化的描述吧。 我们这里设计的是持久化服务写到一个基类中, 然后继承自该基类的子类都会拥有内置的持久化方法,可能后续我们又要添加其他的针对某个具体的对象类,有一些个性化的服务,我们通过扩展这个类, 进行继承,这样多重继承后,会有一个很大的问题。子类膨胀的问题,而且一般来说继承的重数达到5层左右的时候,性能上可能就会有一定的瓶颈,。也 不是好的设计的思路。这时候对象组合可能为我们提供了更好的解决方案。基于组合方式的话,可能我们可以这样来做,换个思路:就像我们的一个负责的 对象,可以通过简单的对象来组成的道理差不多,其实。 通过上图,我们知道了,组合对象可以 看作是一系列简单的对象组合成负责的对象的一个模式,复杂对象可以看作是简单对象的一个容器。这个复杂对象完成了简单对象的封装,通过这个容器完 成对象内部简单对象的访问。 三、本文大纲 a、上篇回顾。 b、摘要。 c、本文大纲。 d、组合模式的特点及使用场景。 e、组合模式的经典实现。 f、组合模式的其他方案。 g、原型模式使用总结。 h、系列进度。 i、下篇预告。 四、组合模式的特点及使用场景 组合模式是将一系列对象组合成树形结构用来表示整体和部分之间的关系,组合模式的主要目的是达到,访问组合对象和访问单个对象具有一致性。这里的组合对象比较特殊,本身他可以是由其他的对象组合而成,同时,这个组合对象又可以是组成更复杂对象的一个部分。我们来举个例子来说明吧,可能更直观。 这里我们可以理解为一个简单的查询组件可能有要满足 上述的几类查询条件的输入类型,将这个组件作为一个容器,那么同事,这个容器又可以作为另外一个容器的组件来运行,那么我们又可以在分页控件中, 将这个查询组件包含到其中。以为分页提供相应的查询元素的集成。 我们一般在如下场景中使用组合模式比较方便: 1、我们有的时候想用户使用一个复杂对象像使用简单对象一样的方式去访问,并且用户同意使用对象内部的所有的对象时,我们可以考虑使用该模 式。这个怎么理解呢?我们使用复杂对象像使用简单对象一样的方式去访问的话,那么我们可以使用组合对象,我们把这些简单的对象进行组合,用组合对 象进行包装,并且提供相应的操作组合对象内部的方法。结合上图中的例子,我们可以这样理解,我们把查询组件封装成一个用户控件,然后可以在其他的 页面中进行调用。这个时候,我们可能考虑如何分部页面,如何能够动态的维护界面上的控件,是否显示,控件里面显示的文本和值是什么?等等,这些都 是我们可以考虑的元素,那么我们如何来做呢?提供下面的几个方法。我还是给出图来说话吧: 包含上面的这些元素,那么我们可以通过一些相应的方法来进行访 问内部的元素。我们给出简单的示例代码: public QueryControl() { InitializeComponent(); this.InitControlList(); } public event EventHandler handler; private Dictionary<string,Control> _controlList = null; /// <summary> /// 初始化控件信息 /// </summary> private void InitControlList() { this._controlList = new Dictionary<string,Control>(); } /// <summary> /// 返回界面控件中所有的查询条件控件列表 /// </summary> /// <returns></returns> public Dictionary<string,Control> GetControls() { return this._controlList; } /// <summary> /// 添加查询控件到界面中 /// </summary> /// <param name="control"></param> /// <returns></returns> public bool AddControl(string key,Control control) { if(this._controlList.ContainsKey(key)) return false; this._controlList.Add(key,control); return true; } /// <summary> /// 移除指定键值的对象 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool RemoveControl(string key) { if (this._controlList.ContainsKey(key)) return false; this._controlList.Remove(key); return true; } public virtual void OnQuery() { if (this.handler != null) handler(this, new EventArgs()); } public void Query(object sender, EventArgs e) { this.CreateSQL(); this.OnQuery(); } /// <summary> /// 根据选中的条件生成SQL语句 /// </summary> private void CreateSQL() { throw new NotImplementedException(); } 上面的代码很简单就是给出了基本的思路,并不是完整的功能。我们来看其他的可能会用到组合模式的情况 2、如果有的时候,我们希望用户不了解自己使用的对象有多复杂,并且组合对象可以的内部可以自由的变化和组合,但是不会影响到客户应用程序使用这 个组合对象,如果项目中需要新增一个组合对象的时候,客户调用的程序还是一样,不会因为这个组合对象发生变更而发生变化。组合模式在解决整体和部 分之间的问题应用很广泛,也可以降低系统的复杂度,因为我们可以把复杂的组件看作另一个组件的组成部分来处理。就像上面的查询组件,那么我们可以 把查询组件作为分页组件的一个部分来处理。我这里就不给出示例代码了通过上面的情况,我们可以大概的知道,组合模式的使用场景。下面我们就要结合 实例说明组合模式的用处了。 五、组合模式的经典实现 我们先来看看使用经典的组合模式来实现我们上面说的查询控件吧,看看和其他的方案有什么样的不同,不过大体的思路都是一致的,把其他的对象 进行组合成复杂的对象,然后提供操作内部对象的方法,具体的方式可以很多。我们这里还是以上面的查询组件和分页组件为例来说明具体的实现思路,当 然我这里只是给出核心代码,但不是完整的代码: 我们先给出控件内部的完整定义: public QueryPanel() { InitializeComponent(); this.InitControlList(); } public event EventHandler handler; private Dictionary<string,Control> _controlList = null; /// <summary> /// 初始化控件信息 /// </summary> private void InitControlList() { this._controlList = new Dictionary<string,Control>(); } /// <summary> /// 返回界面控件中所有的查询条件控件列表 /// </summary> /// <returns></returns> public Dictionary<string,Control> GetControls() { return this._controlList; } /// <summary> /// 添加查询控件到界面中 /// </summary> /// <param name="control"></param> /// <returns></returns> public bool AddControl(string key,Control control) { if(this._controlList.ContainsKey(key)) return false; this._controlList.Add(key,control); return true; } /// <summary> /// 移除指定键值的对象 /// </summary> /// <param name="key"></param> /// <returns></returns> public bool RemoveControl(string key) { if (this._controlList.ContainsKey(key)) return false; this._controlList.Remove(key); return true; } public virtual void OnQuery() { if (this.handler != null) handler(this, new EventArgs()); } public void Query(object sender, EventArgs e) { this.CreateSQL(); this.OnQuery(); } /// <summary> /// 根据选中的条件生成SQL语句 /// </summary> private void CreateSQL() { throw new NotImplementedException(); } public virtual Control this[string key] { get { return this._controlList[key]; } set { this._controlList[key] = value; } } public virtual IEnumerable<Control> GetControlList() { if (this._controlList.Count > 0) { foreach (Control control in this._controlList.Values) { foreach (Control item in control.Controls) { yield return control; } } } } 这时候我们如果想要重写上面的方案,可能有时候我们的查询控件需要基于上面的几个基本的操作方法进行重写,完成自定义的组合对象的操作: public partial class QueryPanelText : QueryPanel { public QueryPanelText() { InitializeComponent(); } public override Control this[string key] { get { return base.GetControls()[key]; } set { base.GetControls()[key] = value; } } /// <summary> /// 获取集合中的所有控件集合 /// </summary> /// <returns></returns> public override IEnumerable<Control> GetControlList() { return base.GetControlList(); } } 当然我上面并没有改变对象的任何行为,这里定义了公共的行为,有的时候我们需要限制一些对象内部对象的某些行为,这时候我们通过继承公共的 对象来重写对象的行为来完成。例如从组合对象衍生出来的某些组合对象,可能不具有父类的某些行为时,我们可以通过重写父类的行为来完成自定义的操 作。 六、组合模式的其他方案 6.1、通过过滤器来改进上述方案。 有的时候,我们可能并不关心对象中的所有的元素,我们只是操作其中的具有同类特征的对象,比如对于上面的查询控件中的对象,我们可能想要获 取设置条件的控件项,或者说我们相应控制文本框或者下拉框的样式或者高度等等,这个时候,我们需要对组合对象内的元素就行筛选。这个时候,我们可 能可以采用过滤器来实现这样的功能。 对于上面的结果,我们可能需要获取界面中所有的文本框,或者下拉框,或者其他类型的控件等,这时候我们可以如下方式来做,通过接口定义,根 据传入的不同的过滤器,返回过滤后的结果集合。我们先定义一个过滤器接口: public interface ISelectRule { bool IsMatch(Control control); } 我们这里定义一个文本过滤器的实现。 public class TextSelect : ISelectRule { #region ISelectRule 成员 public bool IsMatch(System.Windows.Forms.Control control) { if (control is System.Windows.Forms.TextBox) return true; return false; } #endregion } 我们来看看具体的查询控件内部的获取内部元素的方法: /// <summary> /// 获取集合中的所有控件集合 /// </summary> /// <returns></returns> public IEnumerable<Control> GetControlList(ISelectRule rule) { foreach (Control control in base.GetControls().Values) { if (rule.IsMatch(control)) yield return control; } } 通过上面的几行代码的改进,我们就可以完成对内部的组合对象的改进,这样就可以完成根据不同的查询规则,来返回不同的内部对象集合。 通过上面的迭代器来封装我们的内部组合对象,我们通过迭代器来完成组合对象的过滤。我们还可以通过XML文件来组织我们的对象组合的结构,XML文 件先天性的优势就是结构化的语言,特别适合这样的整体与局部关系的结构,所以我们的组合对象也可以通过XML文件来组织,可以将复杂对象设置为一 个复杂的节点,该节点下包含多个对象时,我们只需要将每个对象设置为一个节点,通过XPath语言完成查询。 6.2XML文件格式的组合模式 使用XML文件来完成组合模式有如下优点: 1、我们有系统提供的类库自动完成对XML文件的操作和访问。 2、XML提供了基于XPath解析的查询语言。 也有一些点,就是操作XML文件,调试起来会比较麻烦,而且进行代码编写和测试是个不太方便的事情。 我们来看看如果我们把上面的获取迭代器的代码,还原成XML文件的格式该如何书写呢? <?xml version="1.0" encoding="utf-8" ?> <Composite> <QueryPanel> <DropDownList name="" type=""/> <DropDownList name="" type=""/> <DropDownList name="" type=""/> <TextBox name="" value="" /> <TextBox name="" value="" /> <TextBox name="" value="" /> <ComboBox name="" value="" /> <ComboBox name="" value="" /> <ComboBox name="" value="" /> <Button name="" value="" /> <Button name="" value="" /> <Button name="" value="" /> </QueryPanel> </Composite> 对于上面的XML文件,那么我们如何获取到这个XML文件中的某些类型的节点呢?比如我如何获取到TextBox呢?这时候我们可以使用Xpath语法来进行 选择: public class XMLSelect { string xPath = "/Composite/QueryPanel/TextBox"; private XmlNodeList list = null; public XmlNodeList GetNodes() { System.Xml.XmlDocument doc = new XmlDocument(); doc.LoadXml("Composite.xml"); list = doc.SelectNodes(xPath); return list; } public List<Control> GetControls() { List<Control> listControls = new List<Control>(); foreach (XmlNode node in list) { System.Windows.Forms.TextBox textBox = new TextBox(); textBox.Name = node.Attributes["name"].Value.ToString(); listControls.Add(textBox); } return listControls; } } 基于上面的形式我们也可以完成组合模式的要求,通过上面的讲解,我们应该对组合模式有了一定的了解。 七、组合模式使用总结 通过上面的简单讲解,我们知道了,组合模式意图是通过整体与局部之间的关系,通过树形结构的形式进行组织复杂对象,屏蔽对象内部的细节,对 外展现统一的方式来操作对象,是我们处理更复杂对象的一个手段和方式。本文以查询控件为例,说明了,查询控件内部的组成元素,及如何操作内部的组 成元素,包括添加元素,删除和处理相应事件的Handler,当然组合模式的作用远比这些强大,后面我们肯定会在一些实例代码中运用到组合模式的。组合 模式如果在条件允许的情况下,我们尽量使用组合模式来处理复杂对象,远比通过继承出来的对象来的有效。 由于本人水平有限,加之理解有误,错误之处还请大家批评指出,多谢大伙的支持和关注! 八、系列进度 九、下篇预告 下篇,我们将会讲述结构性模式中的适配器模式,我想大家对适配器模式应该有不少的了解,因为如果我们经常在项目中使用第三方的或者因为旧的系统与新系 统的接口类型不一致,倒置无法进行调用,这时候我们可以通过适配器模式将旧的接口,转化成可以让我们的新系统调用的接口形式,完成调用,很神奇吧!下篇我们 将会进行详细的阐述。 本文转自 hot的fans 51CTO博客,原文链接:http://blog.51cto.com/2435232/603394

优秀的个人博客,低调大师

企业级网络监控系统“必备技能”

网络可用性监控系统通常是应用程序出现故障时的第一道防线。IT组织的各个团队免不了在出事时互相推委责任,这时网络可用性监控系统就是一个必不可少的工具。 事实上,只要部署了正确的系统,这种推卸责任的现象甚至都不可能再出现。这种可用性监控工具有完备的警报和错误隔离功能。拥有这种工具,网络团队就可以提前处理服务问题。 网络可用性监控系统市场中有各种各样的产品,既有只支持有限功能和规模的开源工具,也有来自IBM和惠普等大型企业IT管理供应商的大型企业产品。在购买监控系统时,采购团队必须将企业需求与各种产品的网络监控特性。 在收集供应商的征询方案(RFP)时,有几点是必须考虑的:方便部署、可用性、兼容现有基础架构、可扩展性及与大量IT管理工具的集成支持。 部署模式 在考虑网络可用性监控系统的部署方式时,第一个问题是了解清楚授权模式。不同供应商的授权方式大相径庭。有一些按照不同的监控功能来销售许可证,而其他一些则按照企业需要管理的设备或逻辑对象的数量来收费。一定要仔细检查他们的授权方式,以确定系统的使用成本及维护难度。 下一个要注意的问题是实际部署模式。采购团队应该弄清楚供应商是否提供了关于系统配置的最佳实践指导。此外,有一些监控系统是独立部署的软件包,而另一些则要求单独安装各个组件,其中包括数据库、检测系统、分析引擎和前端控制台。通常,如果供应商的产品部署中包含多个组件,那么就会采用后一种部署方式,以实现最大的可扩展性和灵活性,但是这种方法也会给某些企业增加额外的复杂性。 易用性 采购团队应该确保网络可用性监控系统的现有需求能够保证所选择的系统能够高效地运行。但是现实并非总是如此。企业管理协会的研究表明,企业中存在大量中看不中用的存架软件——即一些耗费时间且成本昂贵的工具软件。 管理控制台是网络人员要评估的一个最重要的网络监控系统特性。系统上是否有面向典型用户的合理图表功能?命令是否足够简单?相关人员需要经过多少的培训才能学会使用系统?在这种情况下,通过一次概念验证过程,让网络管理人员真实地体验这个系统,能够帮助保证系统的顺利使用并为企业产生价值。 兼容现有网络基础架构 许多组织都在企业环境中混合使用多种供应商的产品。例如,他们可能会使用一两家供应商的交换机,然后再使用另一家的边界路由器。而且,可能还会使用另一家供应商的无线设备。在评估网络可用性监控系统时,风格团队需要确定该工具支持哪一些供应商和平台。 这个评估过程可能需要扩大到网络基础架构之外。许多企业还会监控这些系统连接的服务器和存储。在检查一个管理系统所支持的供应商和设备时,采购团队还必须界定它能够管理的技术类型。这其中包括虚拟化技术和云服务。 系统可扩展性 网络可用性监控系统可能会由于所能管理的风格规模不同而大相径庭。有一些供应商的产品能够监控上百个设备,而其他供应商的产品则能够监控成千上万个设备。而且,有一些系统只能够监控一个位置,但是其他产品则可以覆盖整个企业。 一定要确定网络监控系统所能支持的最大规模和分布程度,而且更重要的是要确定供应商实现可扩展性的方式。某一个供应商可能能够在一台服务器上监控一个包含成千上万台设备的大型分布式网络。而其他供应商则可能将多个服务器部署到企业各个位置,然后通过一个前端控制台将它们绑定在一起。这些差别都将影响系统的使用方式和复杂程度。 互操作性 和其他产品类似,这种网络监控系统也属于大型IT管理系统的一部分。采购团队应该评估企业是否需要集成其他的管理产品。例如,网络工程团队可能会使用可用性监控系统去监控和管理网络,而网络运营团队则可能会使用更高级的服务保证系统。这两种系统之间的集成将促进不同团队之间的协作。 其他潜在的网络监控系统特性还包括网络性能管理、日志分析、应用性能管理、网络变更与配置管理等等。事实上,许多网络可用性监控供应商都提供了大量的IT管理工具及其不同程度的集成技术。然而,对于第三方平台的集成,采购团队还应该评估供应商的技术合作伙伴生态系统、认证类型及这些集成技术附带的联合支持。 总结 前面提到的每一个网络监控系统特性构成了RFP的主体部分,它们是可用性监控系统的采购依据。市场中有大量不同形态的产品,而且许多供应商往往言过其实。因此网络团队一定要全面理解监控系统的需求,再将这些需求与所评估产品的功能相关联。采购团队应该回答所有这些问题,并且在产品评估过程中验证这些问题,然后才能够作出采购决策。 作者:Shamus McGillicuddy 翻译:曾少宁 来源:51CTO

优秀的个人博客,低调大师

大数据技能修炼的个人道场

版权声明:本文为半吊子子全栈工匠(wireless_com,同公众号)原创文章,未经允许不得转载。 https://blog.csdn.net/wireless_com/article/details/50663978 大数据技术火热而且火爆,学习大数据的课程和资料也泛滥如潮,而大数据研发环境又不是随便就可以搭建起来的,如何有一个自己随时可用的大数据修炼道场呢? 网上有很多hadoop单机版的搭建教程,但大多是基础组件,如果想窥探Hadoop 的整个生态系统,并建立一个人的大数据环境,从而修炼大数据的各种技术,我觉得非 HDP 的Sandbox 莫属了。 HDP 的Sandbox 是一个基于虚拟机的单节点hadoop集群,相当于一个伪分布式环境,学习和使用都非常的轻松便捷。虚拟机既支持VMware也支持VirtualBox,完全可以在windows和mac 上无缝执行,需要注意的是需要64位的主机多核cpu并支持虚拟化。 以virtualbox为例,三步,只需三步,就可以搭建自己修炼大数据技术的环境了。 1)先去下载安装virtualbox, https://www.virtualbox.org/wiki/Downloads,选好自己的所需的版本,下载安装。 2)下载HDP sandbox, http://hortonworks.com/products/hortonworks-sandbox/#install,选择virtual box 版本即可,文件较大,接近9个G,要有耐心。 3)启动Virtualbox,倒入HDP sandbox镜像,import 完成后如下图: 忽略那个我自己的ubantu镜像,启动HDP sandbox 即可。 从Ambri开始,就可以逐渐走进Hadoop 的ecosystem 了。 如果只是想马上实践大数据平台计算的应用,可以直接利用云服务。 AWS 的大数据云服务: https://aws.amazon.com/cn/elasticmapreduce/ 这是EMR,当时实时流处理也很强。 阿里云的大数据云服务: https://www.aliyun.com/product/emapreduce只可惜还在公测,没了ali 的名头 青云的大数据云服务: https://www.qingcloud.com/products/big_data_platform后起之秀,也不错,只是HBase 还没有商用。 其他的云PaaS 大数据计算没有过多尝试,不敢妄言了。

优秀的个人博客,低调大师

第一章 Java多线程技能

进程和多线程的概念及其优点 进程是操作系统结构的基础,是一次程序的执行;是一个程序及其数据在处理机上顺序执行时所发生的活动;是程序在一个数据集合上运行的过程,它是系统进行资源分配和调度的一个独立单位。进程是受操作系统管理的基本运行单元。 线程可以理解成是在进程中独立运行的子任务。比如QQ.exe运行时就有很多的子任务在同时运行。再如,好友视频线程、下载文件线程、传输数据线程、发送表情线程等,这些不同的任务或者说功能都可以同时运行,其中每一项任务完全可以理解成是“线程”在工作,传文件、听音乐、发送图片表情等功能都有对应的线程在后台默默地运行。 多线程的优点:可以最大限度的利用CPU的空闲时间来处理其他的任务,而CPU在这些任务之间不停的切换,由于切换的速度非常快,给使用者的感受就是这些任务似乎在同时运行。所以使用多线程技术后,可以在同一时间内运行更多不同种类的任务。 单任务的特点就是排队执行,也就是同步,就像在cmd中输入一条命令后,必须等待这条命令执行完才可以执行下一条命令一样。这就是单任务环境的缺点,即CPU利用率大幅降低。而多线程情况下,CPU完全可以在任务1和任务2之间来回切换,使任务2不必等到10秒再运行,系统的运行效率大大得到提升,这就是多线程技术的优点,使用多线程也就是在使用异步,千万不要把开发环境里代码的顺序当成线程执行的顺序,线程被调用的时机是随机的。 使用多线程 继承Thread 类 在Java 的JDK 开发包中,已经自带了对多线程技术的支持,可以很方便地进行多线程编程。实现多线程编程的方式主要有两种,一种是继承Thread 类,另一种是实现Runnable 接口。 package com.mythread.www; public class MyThread extends Thread { @Override public void run() { super.run(); System.out.println("MyThread"); } } package test; import com.mythread.www.MyThread; public class Run { public static void main(String[] args) { MyThread mythread = new MyThread(); mythread.start(); System.out.println(" 运行结束! "); } } 输出结果 运行结束! MyThread MyThread.java 类中的run 方法执行的时间比较晚,这也说明在使用多线程技术时,代码的运行结果与代码执行顺序或调用顺序是无关的。 实现Runnable 接口 如果欲创建的线程类已经有一个父类了,这时就不能再继承自Thread 类了,因为Java不支持多继承,所以就需要实现Runnable 接口来应对这样的情况。 package myrunnable; public class MyRunnable implements Runnable { @Override public void run() { System.out.println(" 运行中!"); } public static void main(String[] args) { Runnable runnable=new MyRunnable(); Thread thread=new Thread(runnable); thread.start(); System.out.println(" 运行结束!"); } } 输出结果 运行结束! 运行中! 那也就意味着构造函数Thread(Runnable target) 不光可以传入Runnable 接口的对象,还可以传入一个Thread 类的对象,这样做完全可以将一个Thread 对象中的run() 方法交由其他的线程进行调用。 实例变量与线程安全 自定义线程类中的实例变量针对其他线程可以有共享与不共享之分,这在多个线程之间进行交互时是很重要的一个技术点。 不共享数据的情况 public class MyThread extends Thread { private int count = 5; public MyThread(String name) { super(); this.setName(name);// 设置线程名称 } @Override public void run() { super.run(); while (count > 0) { count--; System.out.println(" 由 " + this.currentThread().getName() + " 计算,count=" + count); } } } public class Run { public static void main(String[] args) { MyThread a=new MyThread("A"); MyThread b=new MyThread("B"); MyThread c=new MyThread("C"); a.start(); b.start(); c.start(); } } 一共创建了3 个线程,每个线程都有各自的count 变量,自己减少自己的count 变量的值。这样的情况就是变量不共享,此示例并不存在多个线程访问同一个实例变量的情况。 共享数据的情况就是多个线程可以访问同一个变量,比如在实现投票功能的软件时,多个线程可以同时处理同一个人的票数。 public class MyThread extends Thread { private int count=5; @Override public void run() { super.run(); count--; System.out.println(" 由 "+Thread.currentThread().getName()+" 计算, count="+count); } } public class Run { public static void main(String[] args) { MyThread mythread=new MyThread(); Thread a=new Thread(mythread,"A"); Thread b=new Thread(mythread,"B"); Thread c=new Thread(mythread,"C"); Thread d=new Thread(mythread,"D"); Thread e=new Thread(mythread,"E"); a.start(); b.start(); c.start(); d.start(); e.start(); } } 输出结果 由 B 计算, count=3 由 A 计算, count=3 由 C 计算, count=2 由 D 计算, count=1 由 E 计算, count=0 线程A 和B 打印出的count 值都是3,说明A 和B 同时对count 进行处理,产生了“非线程安全”问题。在某些JVM 中,i-- 的操作要分成如下3 步: 1 取得原有i 值。 2 计算i-1。 3 对i 进行赋值。 在这3 个步骤中,如果有多个线程同时访问,那么一定会出现非线程安全问题。 在这里占不解决同步问题,读者可以自行思考。 留意i-- 与System.out.println() 的异常 本节将通过程序案例细化一下println() 方法与i++ 联合使用时“有可能”出现的另外一种异常情况,并说明其中的原因。 package extthread; public class MyThread extends Thread { private int i = 5; @Override public void run() { System.out.println("i=" + (i--) + " threadName=" + Thread.currentThread().getName()); // 注意:代码i-- 由前面项目中单独一行运行改成在当前项目中在println() 方法中直接进行打印 } } package test; import extthread.MyThread; public class Run { public static void main(String[] args) { MyThread run = new MyThread(); Thread t1 = new Thread(run); Thread t2 = new Thread(run); Thread t3 = new Thread(run); Thread t4 = new Thread(run); Thread t5 = new Thread(run); t1.start(); t2.start(); t3.start(); t4.start(); t5.start(); } } 输出结果 i=5 threadName=B i=4 threadName=C i=5 threadName=A i=2 threadName=D i=3 threadName=E 虽然println() 方法在内部是同步的,但i-- 的操作却是在进入println() 之前发生的,所以有发生非线程安全问题的概率。 /** * Prints a String and then terminate the line. This method behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x The <code>String</code> to be printed. */ public void println(String x) { synchronized (this) { print(x); newLine(); } } currentThread() currentThread() 方法可返回代码段正在被哪个线程调用的信息。 /** * Created by Lee on 2018/7/25. */ public class MyThread extends Thread { public MyThread () { System.out.println("构造方法打印的是:" + Thread.currentThread().getName()); } @Override public void run() { System.out.println("run方法打印的是" + Thread.currentThread().getName()); } public static void main(String[] args) { MyThread thread = new MyThread(); thread.start(); //thread.start(); } } 输出结果 构造方法打印的是:main run方法打印的是Thread-0 //run方法打印的是main 再来一个比较复杂的 package mythread; public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("this.getName()=" + this.getName()); System.out.println("CountOperate---end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("this.getName()=" + this.getName()); System.out.println("run---end"); } public static void main(String[] args) { CountOperate c = new CountOperate(); Thread t1 = new Thread(c); t1.setName("A"); t1.start(); } } 输出结果 CountOperate---begin Thread.currentThread().getName()=main this.getName()=Thread-0 CountOperate---end run---begin Thread.currentThread().getName()=A this.getName()=Thread-0 run---end 因为Thread.currentThread()是当前代码段正在那个线程调用,CountOperate的构造函数是有main主线程调用的,run是由Thread线程调用。同时线程的默认名称是Thread-(No),这点可以有Thread类的构造函数看出。其中一个构造函数如下: public Thread(Runnable target) { init(null, target, "Thread-" + nextThreadNum(), 0); } /* For autonumbering anonymous threads. */ private static int threadInitNumber; private static synchronized int nextThreadNum() { return threadInitNumber++; } 重点也就是最后一个this.getName() 为什么是Thread-0?由上面的Thread构造函数可以看出当使用一个Thread对象作为参数去实例化一个Thread对象时,实现Thread的线程类被缓存进了target对象,而当调用run()方法时,Thread类是这样实现的 /** * If this thread was constructed using a separate * <code>Runnable</code> run object, then that * <code>Runnable</code> object's <code>run</code> method is called; * otherwise, this method does nothing and returns. * <p> * Subclasses of <code>Thread</code> should override this method. * * @see #start() * @see #stop() * @see #Thread(ThreadGroup, Runnable, String) */ @Override public void run() { if (target != null) { target.run(); } } isAlive() 方法 方法isAlive() 的功能是判断当前的线程是否处于活动状态。 /** * @program: Demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread{ @Override public void run() { System.out.println("run=" + this.isAlive()); } public static void main(String[] args) { Demo mythread = new Demo(); System.out.println("begin ==" + mythread.isAlive()); mythread.start(); System.out.println("end ==" + mythread.isAlive()); } } 输出结果 begin ==false end ==true run=true 方法isAlive() 的作用是测试线程是否处于活动状态。什么是活动状态呢?活动状态就是线程已经启动且尚未终止。线程处于正在运行或准备开始运行的状态,就认为线程是“存活”的。 需要说明一下,如以下代码: System.out.println("end ==" + mythread.isAlive());虽然在上面的示例中打印的值是true,但此值是不确定的。打印true 值是因为mythread线程还未执行完毕,所以输出true。如果代码更改如下: public static void main(String[] args) throws InterruptedException { MyThread mythread = new MyThread(); System.out.println("begin ==" + mythread.isAlive()); mythread.start(); Thread.sleep(1000); System.out.println("end ==" + mythread.isAlive()); } 输出结果 begin ==false run=true end ==false 另外,在使用isAlive() 方法时,如果将线程对象以构造参数的方式传递给Thread 对象进行start() 启动时,运行的结果和前面示例是有差异的。造成这样的差异的原因还是来自于Thread.currentThread() 和this 的差异。 /** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */ public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("CountOperate---end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("run---end"); } public static void main(String[] args) { CountOperate c = new CountOperate(); Thread t1 = new Thread(c); t1.setName("A"); t1.start(); } } 输出结果 CountOperate---begin Thread.currentThread().getName()=main Thread.currentThread().isAlive()=true this.getName()=Thread-0 this.isAlive()=false CountOperate---end run---begin Thread.currentThread().getName()=A Thread.currentThread().isAlive()=true this.getName()=Thread-0 this.isAlive()=false run---end 另一种方式 /** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */ public class CountOperate extends Thread { public CountOperate() { System.out.println("CountOperate---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("CountOperate---end"); } @Override public void run() { System.out.println("run---begin"); System.out.println("Thread.currentThread().getName()=" + Thread.currentThread().getName()); System.out.println("Thread.currentThread().isAlive()=" + Thread.currentThread().isAlive()); System.out.println("this.getName()=" + this.getName()); System.out.println("this.isAlive()=" + this.isAlive()); System.out.println("run---end"); } public static void main(String[] args) { CountOperate c = new CountOperate(); c.start(); } } 输出结果 CountOperate---begin Thread.currentThread().getName()=main Thread.currentThread().isAlive()=true this.getName()=Thread-0 this.isAlive()=false CountOperate---end run---begin Thread.currentThread().getName()=Thread-0 Thread.currentThread().isAlive()=true this.getName()=Thread-0 this.isAlive()=true run---end sleep() 方法 方法sleep() 的作用是在指定的毫秒数内让当前“正在执行的线程”休眠(暂停执行)。这个“正在执行的线程”是指this.currentThread() 返回的线程。 /** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */ public class CountOperate extends Thread { @Override public void run() { try { System.out.println("run threadName=" + Thread.currentThread().getName() + " begin"); Thread.sleep(2000); System.out.println("run threadName=" + Thread.currentThread().getName() + " end"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } public static void main(String[] args) { CountOperate c = new CountOperate(); c.start(); } } getId() 方法 getId() 方法的作用是取得线程的唯一标识 /** * @program: CountOperate * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */ public class CountOperate extends Thread { public static void main(String[] args) { Thread runThread = Thread.currentThread(); System.out.println(runThread.getName() + " " + runThread.getId()); } } # 输出结果 main 1 停止线程 停止一个线程可以使用Thread.stop() 方法,但最好不用它。虽然它确实可以停止一个正在运行的线程,但是这个方法是不安全的(unsafe),而且是已被弃用作废的(deprecated),在将来的Java 版本中,这个方法将不可用或不被支持。 大多数停止一个线程的操作使用Thread.interrupt() 方法,尽管方法的名称是“停止,中止”的意思,但这个方法不会终止一个正在运行的线程,还需要加入一个判断才可以完成线程的停止。 在Java 中有以下3 种方法可以终止正在运行的线程: 使用退出标志,使线程正常退出,也就是当run 方法完成后线程终止。 使用stop 方法强行终止线程,但是不推荐使用这个方法,因为stop 和suspend 及resume 一样,都是作废过期的方法,使用它们可能产生不可预料的结果。 使用interrupt 方法中断线程。 停止不了的线程 本示例将调用interrupt() 方法来停止线程, 但interrupt() 方法的使用效果并不像for+break 语句那样,马上就停止循环。调用interrupt() 方法仅仅是在当前线程中打了一个停止的标记,并不是真的停止线程。 /** * @program: MyThread * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 */ public class MyThread extends Thread { @Override public void run() { for (int i = 0; i < 500000; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); thread.interrupt(); } catch (Exception e) { System.out.println("main catch"); e.printStackTrace(); } } } 输出结果 ... ... i=499995 i=499996 i=499997 i=499998 i=499999 i=500000 从运行的结果来看,调用interrupt 方法并没有停止线程。如何停止线程呢,请继续向下读。 判断线程是否是停止状态 在介绍如何停止线程的知识点前,先来看一下如何判断线程的状态是不是停止的。在Java 的SDK 中,Thread.java 类里提供了两种方法。 this.interrupted():测试当前线程是否已经中断。 this.isInterrupted():测试线程是否已经中断。 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); thread.interrupt(); //Thread.currentThread().interrupt(); System.out.println(" 是否停止1 ? ="+thread.isInterrupted()); System.out.println(" 是否停止2 ? ="+thread.interrupted()); } catch (Exception e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } } 输出结果 是否停止1 ? =true 是否停止2 ? =false end! i=1 ... i=45 i=46 i=47 i=48 i=49 i=50 判断thread 对象所代表的线程是否停止,但从控制台打印的结果来看,线程并未停止,这也就证明了interrupted() 方法的解释:测试当前线程是否已经中断。这个“当前线程”是main,它从未中断过,所以打印的结果是两个false。 第二种情况 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); Thread.sleep(1000); thread.interrupt(); System.out.println(" 是否停止1 ? ="+thread.isInterrupted()); System.out.println(" 是否停止2 ? ="+thread.interrupted()); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } } 输出结果 i=1 ... i=47 i=48 i=49 i=50 是否停止1 ? =false 是否停止2 ? =false end! 为什么两种结果不一样呢,因为休息一秒后,线程早已经结束了,所以不能中断了,interrupted() 方法的解释:测试当前线程是否已经中断。等价于 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread{ @Override public void run() { for (int i = 0; i < 50; i++) { System.out.println("i=" + (i + 1)); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); Thread.sleep(1000); thread.interrupt(); System.out.println(" 是否停止1 ? ="+thread.isInterrupted()); System.out.println(" 是否停止2 ? ="+Thread.interrupted()); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } } 最后,再来看一下这两个方法的解释。 this.interrupted() :测试当前线程是否已经是中断状态,执行后具有将状态标志置清除为false 的功能。 this.isInterrupted() :测试线程Thread 对象是否已经是中断状态,但不清除状态标志。 能停止的线程——异常法 直接上代码 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread { @Override public void run() { super.run(); for (int i = 0; i < 5000; i++) { if (Thread.interrupted()) { System.out.println(" 已经是停止状态了! 我要退出了!"); break; } System.out.println("i=" + (i + 1)); } //System.out.println(" 我被输出,如果此代码是for 又继续运行,线程并未停止! "); } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); thread.interrupt(); System.out.println(" 是否停止1 ? =" + thread.isInterrupted()); System.out.println(" 是否停止2 ? =" + Thread.interrupted()); System.out.println("end!"); } } 输出结果 是否停止1 ? =true 是否停止2 ? =false end! 已经是停止状态了! 我要退出了! 上面的示例虽然停止了线程,但如果for 语句下面还有语句,还是会继续运行的。 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread { @Override public void run() { try { for (int i = 0; i < 500000; i++) { if (Thread.interrupted()) { System.out.println(" 已经是停止状态了! 我要退出了!"); throw new InterruptedException(); } System.out.println("i=" + (i + 1)); } System.out.println(" 我在for 下面"); } catch (InterruptedException e) { System.out.println(" 进MyThread.java 类run 方法中的catch 了! "); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); thread.interrupt(); System.out.println(" 是否停止1 ? =" + thread.isInterrupted()); System.out.println(" 是否停止2 ? =" + Thread.interrupted()); System.out.println("end!"); } } 输出结果 是否停止1 ? =true 是否停止2 ? =false end! 已经是停止状态了! 我要退出了! 进MyThread.java 类run 方法中的catch 了! java.lang.InterruptedException at Demo.run(Demo.java:14) 在沉睡中停止 如果在sleep状态下停止某一线程,会进入catch语句,并且清除停止状态值,使之变成false. /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread { @Override public void run() { try { System.out.println("run begin"); Thread.sleep(200000); System.out.println("run end"); } catch (InterruptedException e) { System.out.println("在沉睡中被停止! 进入catch!"+this.isInterrupted()); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { try { Demo thread = new Demo(); thread.start(); Thread.sleep(200); thread.interrupt(); } catch (InterruptedException e) { System.out.println("main catch"); e.printStackTrace(); } System.out.println("end!"); } } 输出结果 run begin end! 在沉睡中被停止! 进入catch!false java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at Demo.run(Demo.java:12) 另一种情况 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread { @Override public void run() { try { for(int i=0;i<100000;i++){ System.out.println("i="+(i+1)); } System.out.println("run begin"); Thread.sleep(200000); System.out.println("run end"); } catch (InterruptedException e) { System.out.println(" 先停止,再遇到了sleep! 进入catch!"); e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); thread.interrupt(); System.out.println("end!"); } } 输出结果 ... i=99998 i=99999 i=100000 run begin 先停止,再遇到了sleep! 进入catch! 能停止的线程——暴力停止 使用stop() 方法停止线程则是非常暴力的,调用stop() 方法时会抛出java.lang.ThreadDeath 异常,但在通常的情况下,此异常不需要显式地捕捉,方法stop() 已经被作废,因为如果强制让线程停止则有可能使一些清理性的工作得不到完成。另外一个情况就是对锁定的对象进行了“解锁”,导致数据得不到同步的处理,出现数据不一致的问题。 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread { @Override public void run() { try { for(int i=0;i<100000;i++){ System.out.println("i="+(i+1)); Thread.sleep(2000); } } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); Thread.sleep(10000); thread.stop(); System.out.println("end!"); } } 输出结果 i=1 i=2 i=3 i=4 i=5 end! stop停止导致数据不一致 /** * Created by Lee on 2018/7/28. */ public class StopSynchronizedObject { private String username = "a"; private String password = "aa"; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } synchronized public void printString(String username, String password) { try { this.username = username; Thread.sleep(8000); this.password = password; } catch (InterruptedException ex) { ex.printStackTrace(); } } } /** * Created by Lee on 2018/7/28. */ public class StopSynchronizedThread extends Thread { private StopSynchronizedObject object; public StopSynchronizedThread(StopSynchronizedObject object) { this.object=object; } @Override public void run() { object.printString("b","bb"); } } /** * Created by Lee on 2018/7/28. */ public class StopSynchronizedRun { public static void main(String[] args) { try { StopSynchronizedObject object = new StopSynchronizedObject(); StopSynchronizedThread thread = new StopSynchronizedThread(object); thread.start(); Thread.sleep(500); thread.stop(); System.out.println(object.getUsername()); System.out.println(object.getPassword()); } catch (InterruptedException e) { e.printStackTrace(); } } } 输出结果 b aa 再看一个例子 /** * Created by Lee on 2018/7/28. */ public class StopSynchronizedRun { public static void main(String[] args) { try { StopSynchronizedObject object = new StopSynchronizedObject(); StopSynchronizedThread thread = new StopSynchronizedThread(object); thread.start(); //Thread.sleep(500); thread.stop(); System.out.println(object.getUsername()); System.out.println(object.getPassword()); } catch (Exception e) { e.printStackTrace(); } } } 输出结果 a aa 使用return 停止线程 /** * @program: demo * @description: java基础知识测试 * @author: lee * @create: 2018-07-18 09:45 **/ public class Demo extends Thread { @Override public void run() { while (true) { if (this.isInterrupted()) { System.out.println("停止了!"); return; } System.out.println("timer=" + System.currentTimeMillis()); } } public static void main(String[] args) throws InterruptedException { Demo thread = new Demo(); thread.start(); Thread.sleep(1000); thread.interrupt(); System.out.println("end!"); } } 输出结果 timer=1532932687078 timer=1532932687078 end! 停止了! 暂停线程 暂停线程意味着此线程还可以恢复运行。在Java 多线程中,可以使用suspend() 方法暂停线程,使用resume() 方法恢复线程的执行。 /** * @program: demo * @description: demo * @author: lee * @create: 2018-07-29 11:05 **/ public class SuspendThread extends Thread { private long i = 0; public long getI() { return i; } public void setI(long i) { this.i = i; } @Override public void run(){ while (true) { i++; } } } /** * Created by Lee on 2018/7/28. */ public class SuspendThreadRun { public static void main(String[] args) { try { SuspendThread thread = new SuspendThread(); thread.start(); Thread.sleep(1000); //第一段 thread.suspend(); System.out.println("I="+thread.getI()); Thread.sleep(5000); System.out.println("I="+thread.getI()); //第二段 thread.resume(); Thread.sleep(1000); //第三段 thread.suspend(); System.out.println("I="+thread.getI()); Thread.sleep(5000); System.out.println("I="+thread.getI()); thread.stop(); } catch (InterruptedException ex) { ex.printStackTrace(); } } } 输出结果 I=483701768 I=483701768 I=1001747085 I=1001747085 suspend 与resume 方法的缺点——独占 在使用suspend 与resume 方法时,如果使用不当,极易造成公共的同步对象的独占,使得其他线程无法访问公共同步对象。 public class SynchronizedObject { synchronized public void printString() { System.out.println("begin"); if (Thread.currentThread().getName().equals("a")) { System.out.println("a 线程永远 suspend 了! "); Thread.currentThread().suspend(); } System.out.println("end"); } } public class Run { public static void main(String[] args) { try { final SynchronizedObject object = new SynchronizedObject(); Thread thread1 = new Thread() { @Override public void run() { object.printString(); } }; thread1.setName("a"); thread1.start(); Thread.sleep(1000); Thread thread2 = new Thread() { @Override public void run() { System.out.println("thread2 启动了,但进入不了printString() 方法! 只打印1 个begin"); System.out.println(" 因为printString() 方法被a 线程锁定并且永远 suspend 暂停了! "); object.printString(); } }; thread2.start(); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } 还有另外一种独占锁的情况也要格外注意,稍有不慎,就会掉进“坑”里 public class MyThread extends Thread { private long i = 0; @Override public void run() { while (true) { i++; } } } public class Run { public static void main(String[] args) { try { MyThread thread = new MyThread(); thread.start(); Thread.sleep(1000); thread.suspend(); System.out.println("main end!"); } catch (InterruptedException e) { e.printStackTrace(); } } } 输出结果 main end! 如果更改程序如下 /** * @program: demo * @description: 2 * @author: lee * @create: 2018-07-30 14:52 **/ public class test extends Thread{ private long i = 0; @Override public void run() { while (true) { i++; System.out.println(i); } } public static void main(String[] args) { try { test thread = new test(); thread.start(); Thread.sleep(1000); thread.suspend(); System.out.println("main end!"); } catch (InterruptedException e) { e.printStackTrace(); } } } 输出结果 ... 113720 113721 113722 113723 不再会有main end!输出,出现这样情况的原因是,当程序运行到println() 方法内部停止时,同步锁未被释放。 /** * Prints a String and then terminate the line. This method behaves as * though it invokes <code>{@link #print(String)}</code> and then * <code>{@link #println()}</code>. * * @param x The <code>String</code> to be printed. */ public void println(String x) { synchronized (this) { print(x); newLine(); } } suspend 与resume 方法的缺点——不同步 在使用suspend 与resume 方法时也容易出现因为线程的暂停而导致数据不同步的情况。 public class MyObject { private String username = "1"; private String password = "11"; public void setValue(String u, String p) { this.username = u; if (Thread.currentThread().getName().equals("a")) { System.out.println(" 停止a 线程! "); Thread.currentThread().suspend(); } this.password = p; } public void printUsernamePassword() { System.out.println(username + " " + password); } } public class Run { public static void main(String[] args) throws InterruptedException { final MyObject myobject = new MyObject(); Thread thread1 = new Thread() { public void run() { myobject.setValue("a", "aa"); }; }; thread1.setName("a"); thread1.start(); Thread.sleep(500); Thread thread2 = new Thread() { public void run() { myobject.printUsernamePassword(); }; }; thread2.start(); } } 输出结果 停止a线程 a 11 yield 方法 yield() 方法的作用是放弃当前的CPU 资源,将它让给其他的任务去占用CPU 执行时间。但放弃的时间不确定,有可能刚刚放弃,马上又获得CPU 时间片。 /** * @program: demo * @description: demo * @author: lee * @create: 2018-07-29 11:21 **/ public class YieldThread extends Thread { @Override public void run(){ long beginTime = System.currentTimeMillis(); int count = 0; for (int i = 0; i < 5000000; i++) { // Thread.yield(); count = count + (i+1); } long endTime=System.currentTimeMillis(); System.out.println("用时:" + (endTime-beginTime) + "毫秒!"); } public static void main(String[] args) { YieldThread thread = new YieldThread(); thread.start(); } } # 输出结果 用时:2毫秒! # 没有注释Thread.yield();输出结果 用时:1809毫秒! 线程的优先级 在操作系统中,线程可以划分优先级,优先级较高的线程得到的CPU 资源较多,也就是CPU 优先执行优先级较高的线程对象中的任务,在Java 中,线程的优先级分为1 ~ 10 这10 个等级,如果小于1 或大于10,则JDK 抛出异常throw new IllegalArgumentException()。 JDK 中使用3 个常量来预置定义优先级的值,代码如下: public final static int MIN_PRIORITY = 1; public final static int NORM_PRIORITY = 5; public final static int MAX_PRIORITY = 10; 线程优先级的继承特性 在Java 中,线程的优先级具有继承性,比如A 线程启动B 线程,则B 线程的优先级与A 是一样的。 /** * @program: demo * @description: MyThread1 * @author: lee * @create: 2018-07-30 15:38 **/ public class MyThread1 extends Thread { @Override public void run() { System.out.println("MyThread1 run priority=" + this.getPriority()); MyThread2 thread2 = new MyThread2(); thread2.start(); } } /** * @program: demo * @description: MyThread2 * @author: lee * @create: 2018-07-30 15:38 **/ public class MyThread2 extends Thread { @Override public void run() { System.out.println("MyThread2 run priority=" + this.getPriority()); } } /** * @program: demo * @description: demo * @author: lee * @create: 2018-07-30 15:39 **/ public class RunDemo { public static void main(String[] args) { MyThread1 thread = new MyThread1(); thread.start(); } } 输出结果 MyThread1 run priority=5 MyThread2 run priority=5 优先级具有规则性 虽然使用setPriority() 方法可以设置线程的优先级,但还没有看到设置优先级所带来的效果。 import java.util.Random; /** * @program: demo * @description: 线程优先级测试 * @author: lee * @create: 2018-07-29 11:37 **/ public class PropertyThread1 extends Thread{ @Override public void run() { long beginTime = System.currentTimeMillis(); long addResult = 0; for (int j = 0;j < 100; j++) { Random random = new Random(); random.nextInt(); addResult = addResult +j; long endTime = System.currentTimeMillis(); System.out.println("*************** PropertyThread1 use time= " +(endTime-beginTime)); } } } import java.util.Random; /** * @program: demo * @description: 线程优先级测试 * @author: lee * @create: 2018-07-29 11:37 **/ public class PropertyThread2 extends Thread{ @Override public void run() { long beginTime = System.currentTimeMillis(); long addResult = 0; for (int j = 0;j < 100; j++) { Random random = new Random(); random.nextInt(); addResult = addResult +j; long endTime = System.currentTimeMillis(); System.out.println("######### PropertyThread2 use time= " +(endTime-beginTime)); } } } /** * @program: demo * @description: property测试 * @author: lee * @create: 2018-07-29 11:43 **/ public class PropertyRun { public static void main(String[] args) { PropertyThread1 thread1 = new PropertyThread1(); thread1.setPriority(2); thread1.start(); PropertyThread2 thread2 = new PropertyThread2(); thread2.start(); thread2.setPriority(6); } } 输出结果 ... ######### PropertyThread2 use time= 1 *************** PropertyThread1 use time= 2 ######### PropertyThread2 use time= 2 *************** PropertyThread1 use time= 3 ######### PropertyThread2 use time= 2 ... *************** PropertyThread1 use time= 11 *************** PropertyThread1 use time= 11 *************** PropertyThread1 use time= 11 *************** PropertyThread1 use time= 11 *************** PropertyThread1 use time= 11 高优先级的线程总是大部分先执行完,但不代表高优先级的线程全部先执行完。另外,不要以为thread1 线程先被main 线程所调用就会先执行完,当线程优先级的等级差距很大时,谁先执行完和代码的调用顺序无关,线程的优先级具有一定的规则性,也就是CPU 尽量将执行资源让给优先级比较高的线程。 优先级具有随机性 线程的优先级还具有“随机性”,也就是优先级较高的线程不一定每一次都先执行完,当优先级的差距不大的时候最能体现出随机性。 看谁运行得快 线程的优先级越高,CPU分配得到的机会越大,所以执行的时间越多。 守护线程 在Java 线程中有两种线程,一种是用户线程,另一种是守护线程。 守护线程是一种特殊的线程,它的特性有“陪伴”的含义,当进程中不存在非守护线程了,则守护线程自动销毁。典型的守护线程就是垃圾回收线程,当进程中没有非守护线程了,则垃圾回收线程也就没有存在的必要了,自动销毁。 用个比较通俗的比喻来解释一下“守护线程”:任何一个守护线程都是整个JVM 中所有非守护线程的“保姆”,只要当前JVM实例中存在任何一个非守护线程没有结束,守护线程就在工作,只有当最后一个非守护线程结束时,守护线程才随着JVM 一同结束工作。注意点: thread.setDaemon(true)必须在thread.start()之前设置。 在Daemon线程中产生的新线程也是Daemon的。 不是所有的应用都可以分配给Daemon线程来进行服务,比如读写操作或者计算逻辑。因为Daemon Thread还没来得及进行操作,虚拟机可能已经退出了。 /** * @program: demo * @description: 守护线程 * @author: lee * @create: 2018-07-29 12:04 **/ public class DaemonThread extends Thread{ private int i = 0; @Override public void run() { while (true) { try { System.out.println(i++); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } } } } /** * @program: demo * @description: 守护进程 * @author: lee * @create: 2018-07-29 12:06 **/ public class DaemonRun { public static void main(String[] args) throws InterruptedException { DaemonThread thread = new DaemonThread(); thread.setDaemon(true); thread.start(); Thread.sleep(5000); System.out.println("***************************************************************再也不会打印东西了。。。。"); } } 输出结果 0 1 2 3 4 ***************************************************************再也不会打印东西了。。。。 如果多线程类改变一下 /** * @program: demo * @description: 守护线程 * @author: lee * @create: 2018-07-29 12:04 **/ public class DaemonThread extends Thread{ private int i = 0; @Override public void run() { while (true) { System.out.println(i++); } } } 输出结果 ... 106250 106251 106252 ***************************************************************再也不会打印东西了。。。。 106253 106254 106255 ... 106341 Process finished with exit code 0

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

腾讯云软件源

腾讯云软件源

为解决软件依赖安装时官方源访问速度慢的问题,腾讯云为一些软件搭建了缓存服务。您可以通过使用腾讯云软件源站来提升依赖包的安装速度。为了方便用户自由搭建服务架构,目前腾讯云软件源站支持公网访问和内网访问。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册