<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>街灯晚餐的个人博客</title>
  <subtitle>我的学习笔记</subtitle>
  <link href="/atom.xml" rel="self"/>
  
  <link href="http://yoursite.com/"/>
  <updated>2017-06-28T13:00:47.000Z</updated>
  <id>http://yoursite.com/</id>
  
  <author>
    <name>Jessie Zou</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>一次JAVA爬虫之旅</title>
    <link href="http://yoursite.com/2017/05/22/%E4%B8%80%E6%AC%A1JAVA%E7%88%AC%E8%99%AB%E4%B9%8B%E6%97%85/"/>
    <id>http://yoursite.com/2017/05/22/一次JAVA爬虫之旅/</id>
    <published>2017-05-22T12:14:05.000Z</published>
    <updated>2017-06-28T13:00:47.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="一次JAVA爬虫之旅"><a href="#一次JAVA爬虫之旅" class="headerlink" title="一次JAVA爬虫之旅"></a>一次JAVA爬虫之旅</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;好早就想做一下爬虫，最近终于完成了。爬了知乎用户信息，现在知乎确实正规了好多，各种反爬虫机制，遇到了不少坑，还好都克服了，现在来记录下这个过程，和把结果展示一下。</p>
  <a id="more"></a>
<h2 id="准备知识"><a href="#准备知识" class="headerlink" title="准备知识"></a>准备知识</h2><p>&#160; &#160; &#160; &#160;我们每天都在浏览网页，如果对其中的某些信息特别感兴趣，我们会选择将其保存下来，但当数据数量很多的时候，我们就会选择用程序来实现这样一个过程，实现自动浏览网页，自动根据我们的要求，保存我们想要的数据。于是，爬虫就应运而生了。所以，简单来说，爬虫就是实现网页自动浏览，数据自动保存，根据我们的需要进行自动化浏览器的操作或者自动化测试的这样一种程序。</p>
<p><img src="https://timgsa.baidu.com/timg?image&amp;quality=80&amp;size=b9999_10000&amp;sec=1498659629614&amp;di=5a920e203a547e3594b2c44077f570d3&amp;imgtype=0&amp;src=http%3A%2F%2Fimg.mp.itc.cn%2Fupload%2F20160429%2F9769904ae8b34174b6f67ae5d90be143_th.jpg" alt=""></p>
<h3 id="HTTP协议"><a href="#HTTP协议" class="headerlink" title="HTTP协议"></a>HTTP协议</h3><p>&#160; &#160; &#160; &#160;计算机网络中必学的协议，我们之前已经模拟过web服务器和浏览器，这也需要http基础，理解http协议，然后根据不同的语言去用不同的工具包，模拟http请求，得到响应页面。其中我们要注意的是，要模拟登陆，就要用到cookies等。下图就是一个http响应信息：</p>
<p><img src="https://timgsa.baidu.com/timg?image&amp;quality=80&amp;size=b9999_10000&amp;sec=1498659736502&amp;di=d83687259ca11abd3fa21ff6a5a7716d&amp;imgtype=0&amp;src=http%3A%2F%2Fimages.cnitblog.com%2Fblog%2F434462%2F201303%2F21132402-b67f3fea251d40e493c26c439885ffed.jpg" alt=""></p>
<h3 id="前端基础"><a href="#前端基础" class="headerlink" title="前端基础"></a>前端基础</h3><p>&#160; &#160; &#160; &#160;要有一定的前端基础，能会分析出请求地址，通过firebug等分析网页元素，我甚至看过一个github项目，通过手机访问和路由器、抓包软件找出了摩拜单车的一些业务请求地址，然后进行爬虫，单车也是最近很流行。比如我用Mac的Safari审查元素如下。需要提出的是，我在爬取网易云音乐或者qq空间的时候，除了分析请求，如果想爬取的信息在iframe中，而iframe是重定向的，这时候也许一些内嵌浏览器就要上场了，比如selenium，需要模拟浏览器行为，得到的是最后页面。</p>
<p><img src="http://opb7t58xj.bkt.clouddn.com/%E5%AE%A1%E6%9F%A5%E5%85%83%E7%B4%A0.png" alt=""></p>
<h3 id="正则表达式"><a href="#正则表达式" class="headerlink" title="正则表达式"></a>正则表达式</h3><p>&#160; &#160; &#160; &#160;当我们已经得到了html的响应页面，那么肯定需要筛选出我们想要的信息，这时候我们就需要用到正则表达式了，相关类似的还有XPath、CSS选择器等，可以根据匹配规则筛选信息，也有很多在线网站可以验证我们写的正则表达式的正确性。下图是基本的正则匹配规则。</p>
<p><img src="https://timgsa.baidu.com/timg?image&amp;quality=80&amp;size=b9999_10000&amp;sec=1498660014837&amp;di=fd3bd6f03a0e4207658f25029eb9b46d&amp;imgtype=0&amp;src=http%3A%2F%2Fwww.gaixue.com%2Fuploads%2Feditor%2Fimage%2F20130828%2F20130828121847_34621.png" alt=""></p>
<h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p>&#160; &#160; &#160; &#160;一种编程语言，这里我们使用的是java，之后都是java相关的。为了让程序更高效，我们还用到了JUC中的阻塞队列、线程池、原子类、可重入锁等技术，如果要数据持久化的话，我们还要一定的数据库基础，更深层次的可以研究比如：直接下载html网页保存，然后用搜索引擎，比如我看过一个项目使用ElasticSerch保存下载的html网页，ElasticSerch是一个搜索服务器，apache的子项目，或许你知道PageRank，知道谷歌的几大论文，想象百度和谷歌怎么检索那么多网站，并进行相关度排行，爬虫也可以分布式，比如nutch就是一个分布式爬虫的框架。最后数据保存了，还可以使用数据可视化工作，做出比较好看的直观的图表来展现爬取的数据。</p>
<h2 id="相关框架"><a href="#相关框架" class="headerlink" title="相关框架"></a>相关框架</h2><p>&#160; &#160; &#160; &#160;python可以用scrapy，java的我使用的是webmagic，还有分布式的nutch等，虽然使用框架，但是对于框架本身我们还要了解一下。这里重点说说webmagic。</p>
<h3 id="webmagic"><a href="#webmagic" class="headerlink" title="webmagic"></a>webmagic</h3><p>&#160; &#160; &#160; &#160;首先献上官方文档：<a href="http://webmagic.io/docs/zh/" target="_blank" rel="external">WebMagic中文版官方文档</a>。</p>
<p><img src="http://code4craft.github.io/images/posts/webmagic.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;如上图就是整体框架图，Downloader负责从互联网上下载页面，以便后续处理。WebMagic默认使用了Apache HttpClient作为下载工具。之后我们对比一些java中模拟http请求的工具包。PageProcessor负责解析页面，抽取有用信息，以及发现新的链接。WebMagic使用Jsoup作为HTML解析工具，并基于其开发了解析XPath的工具Xsoup，也就是我们页面信息提取部分。Pipeline负责抽取结果的处理，包括计算、持久化到文件、数据库等。WebMagic默认提供了“输出到控制台”和“保存到文件”两种结果处理方案，根据我们持久化方式也可以自定义。</p>
<p>&#160; &#160; &#160; &#160;webmagic封装了很多api给用户用，而且采用链式api方式，很多方面也许你只需要设置一下。而且如果知识简单的爬虫，自己需要书写的代码也很少。有了基础知识的准备，我们应该再去看看框架的源码，比较一下各种工具，也许更方便我们的使用。</p>
<h2 id="框架工具"><a href="#框架工具" class="headerlink" title="框架工具"></a>框架工具</h2><p>&#160; &#160; &#160; &#160;这里我们针对核心包的一些内容学习。我建议每个技术都找些demo运行试试。</p>
<p><img src="http://opb7t58xj.bkt.clouddn.com/webmagic.png" alt=""></p>
<h3 id="如何模拟请求"><a href="#如何模拟请求" class="headerlink" title="如何模拟请求"></a>如何模拟请求</h3><p>&#160; &#160; &#160; &#160;源码在downloader包中，WebMagic默认使用了Apache HttpClient作为下载工具。其他的还有URLConnection。一个是开园框架，一个是原生JDK，如果使用后者，我发现登陆功能并不能实现，爬取的网页首先是登陆页面，HttpClient提供api包装请求头。</p>
<p>&#160; &#160; &#160; &#160;首先：在 JDK 的 java.net 包中已经提供了访问 HTTP 协议的基本功能：HttpURLConnection。但是对于大部分应用程序来说，JDK 库本身提供的功能还不够丰富和灵活。 </p>
<p>&#160; &#160; &#160; &#160;其次：HttpClient是个很不错的开源框架，封装了访问http的请求头，参数，内容体，响应等等，<br>HttpURLConnection是java的标准类，什么都没封装，用起来太原始，不方便，比如重访问的自定义，以及一些高级功能等。</p>
<p>&#160; &#160; &#160; &#160;HttpClient就是一个增强版的HttpURLConnection，HttpURLConnection可以做的事情HttpClient全部可以做；HttpURLConnection没有提供的有些功能，HttpClient也提供了，但它只是关注于如何发送请求、接收响应，以及管理HTTP连接。 </p>
<h3 id="如何筛选信息"><a href="#如何筛选信息" class="headerlink" title="如何筛选信息"></a>如何筛选信息</h3><p>&#160; &#160; &#160; &#160;源码再selector包中，源码书写大都用接口，抽象类和实现类，类似JDK源码的书写风格，高扩展性。Jsoup这方面也做得很好，我在代码中也部分直接使用了Jsoup，源码在Jsoup基础上其中分别包装了CSS选择器、正则表达式、Xpath的api使用方法。可以通过相关方法获取页面的链接、但是要准备的定位信息，还是需要一些调试，比如知乎上用户信息的缺省就对程序有一定影响。</p>
<h3 id="数据持久化"><a href="#数据持久化" class="headerlink" title="数据持久化"></a>数据持久化</h3><p>&#160; &#160; &#160; &#160;源码在Pipeline何种，提供了输出栏打印、文件保存等实现类。我们采用的是mysql存储，基于JDBC，并没有使用mybatis之类的持久层框架，也没那个必要。</p>
<h2 id="代码结构"><a href="#代码结构" class="headerlink" title="代码结构"></a>代码结构</h2><p><img src="http://opb7t58xj.bkt.clouddn.com/%E9%A1%B9%E7%9B%AE%E7%BB%93%E6%9E%84.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;需要特殊说明的是ip代理我是买的，有些是提供免费的，我试过了项目不是很好，购买后，他会给一个api连接，实行更新半个小时左右，相关访问代码也是固定给出的。当然对于针对的ip反爬虫机制，我们可以降低访问频率，经过测试，知乎如果不断访问，半个小时就会down，会封2天左右。</p>
<h2 id="结果"><a href="#结果" class="headerlink" title="结果"></a>结果</h2><p>&#160; &#160; &#160; &#160;我最后爬下来的一些数据如下：</p>
<p><img src="http://opb7t58xj.bkt.clouddn.com/%E7%BB%93%E6%9E%9C.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;暂时还没有进行可视化分析，有兴趣我可以把数据给你，邮箱：wust_zoujing@foxmail.com。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;一次JAVA爬虫之旅&quot;&gt;&lt;a href=&quot;#一次JAVA爬虫之旅&quot; class=&quot;headerlink&quot; title=&quot;一次JAVA爬虫之旅&quot;&gt;&lt;/a&gt;一次JAVA爬虫之旅&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;好早就想做一下爬虫，最近终于完成了。爬了知乎用户信息，现在知乎确实正规了好多，各种反爬虫机制，遇到了不少坑，还好都克服了，现在来记录下这个过程，和把结果展示一下。&lt;/p&gt;
    
    </summary>
    
      <category term="爬虫" scheme="http://yoursite.com/categories/%E7%88%AC%E8%99%AB/"/>
    
    
      <category term="爬虫" scheme="http://yoursite.com/tags/%E7%88%AC%E8%99%AB/"/>
    
  </entry>
  
  <entry>
    <title>数据结构（一）——树</title>
    <link href="http://yoursite.com/2017/05/11/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84%EF%BC%88%E4%B8%80%EF%BC%89%E2%80%94%E2%80%94%E6%A0%91/"/>
    <id>http://yoursite.com/2017/05/11/数据结构（一）——树/</id>
    <published>2017-05-11T07:51:41.000Z</published>
    <updated>2017-05-22T12:01:06.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="数据结构（一）——树"><a href="#数据结构（一）——树" class="headerlink" title="数据结构（一）——树"></a>数据结构（一）——树</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;在数据结构的学习当中，看到后面树，图，散列的时候已经有点难度，理解的较浅，学习完后面的知识，就发现，这些数据结构和经典算法都用在了一些常用工具的实现原理上，所以需要把这些知识总结一下,今天重点说下树。</p>
 <a id="more"></a>
<p>&#160; &#160; &#160; &#160;在算法题的做题当中就已经学习了简单的数据结构，hashmap中又用到散列，为了理解 TreeMap 的底层实现，必须先介绍排序二叉树和平衡二叉树，然后继续介绍红黑树。平衡二叉树和红黑树又是一种特殊的二叉排序树。二叉排序树是一种特殊结构的二叉树，可以非常方便地对树中所有节点进行排序和检索。mysql索引也是b+，b-树的原理。这里总结一下：</p>
<h2 id="二叉排序树／二叉查找树／二叉搜索树／BST"><a href="#二叉排序树／二叉查找树／二叉搜索树／BST" class="headerlink" title="二叉排序树／二叉查找树／二叉搜索树／BST"></a>二叉排序树／二叉查找树／二叉搜索树／BST</h2><p>&#160; &#160; &#160; &#160;标题已经说明它的名称之多，我们先说下排序二叉树特性：</p>
<ol>
<li>若它的左子树不空，则左子树上所有节点的值均小于它的根节点的值</li>
</ol>
<ol>
<li>若它的右子树不空，则右子树上所有节点的值均大于它的根节点的值</li>
</ol>
<ol>
<li>它的左、右子树也分别为排序二叉树</li>
</ol>
<h3 id="插入"><a href="#插入" class="headerlink" title="插入"></a>插入</h3><p>&#160; &#160; &#160; &#160;已知一个关键字值为key的结点s，若将其插入到二叉排序树中，只要保证插入后仍符合二叉排序树的定义即可。插入可以用下面的方法进行：</p>
<ol>
<li>若二叉排序树是空树，则key成为二叉排序树的根； </li>
<li>若二叉排序树非空，则将key与二叉排序树的根进行比较。如果key的值等于根结点的值，则停止插入；如果key的值小于根结点的值，则将key插入左子树，如果key的值大于根结点的值，则将key插入右子树。</li>
<li>重复步骤2，直到找到合适的插入位置。</li>
</ol>
<h3 id="删除"><a href="#删除" class="headerlink" title="删除"></a>删除</h3><p>&#160; &#160; &#160; &#160;当程序从排序二叉树中删除一个节点之后，为了让它依然保持为排序二叉树，程序必须对该排序二叉树进行维护。维护可分为如下几种情况：</p>
<ol>
<li>被删除的节点是叶子节点，则只需将它从其父节点中删除即可。</li>
<li>如果待删除节点左子树存在右子树不存在，或者左子树不存在右子树存在。直接将其子树中存在的一边候补上来即可。</li>
<li>若被删除节点 p 的左、右子树均非空，有两种做法：一：将 pL 设为 p 的父节点 q 的左或右子节点（取决于 p 是其父节点 q 的左、右子节点），将 pR 设为 p 节点的中序前趋节点 s 的右子节点（s 是 pL 最右下的节点，也就是 pL 子树中最大的节点）。二：以 p 节点的中序前趋或后继替代 p 所指节点，然后再从原排序二叉树中删去中序前趋或后继节点即可。（也就是用大于 p 的最小节点或小于 p 的最大节点代替 p 节点即可）。</li>
</ol>
<p>&#160; &#160; &#160; &#160;下面我们结合图例来说明。当是左子树右子树只有一个存在时如下图：<br><img src="http://img.blog.csdn.net/20140210085705265?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvbWF6aGltYXpo/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""></p>
<p><img src="http://img.blog.csdn.net/20140210085800921" alt=""></p>
<p>&#160; &#160; &#160; &#160;如果左右子树都存在：</p>
<p>被删除节点左右子节点不为空的情形，采用到是第一种方式维护：<br><img src="http://img.blog.csdn.net/20140210085841468" alt=""></p>
<p>被删除节点左右子节点不为空的情形，采用到是第二种方式维护：<br><img src="http://img.blog.csdn.net/20140210085916093" alt=""></p>
<p>&#160; &#160; &#160; &#160;TreeMap 删除节点采用上图第二种方式的情形进行维护——也就是用被删除节点的右子树中最小节点与被删节点交换的方式进行维护。</p>
<p>&#160; &#160; &#160; &#160;在使用第二种方式进行维护时，如果使用前驱节点代替被删除的节点，则前驱节点可能还存在左子树（因为前驱节点是根节点左子树中最右边的节点），而如果是后继节点的话，这个后继节点可能还存在右子树。他们的处理方法相同，直接将子树移上去即可。</p>
<p>&#160; &#160; &#160; &#160;<font color="red">一定要理解，无论是前驱还是后继节点，不可能同时具有左子树或右子树，这就为删除替代节点后的操作带来了方便。</font>如下图：</p>
<p><img src="http://img.blog.csdn.net/20140414215015765" alt=""></p>
<p>tip:直接前驱后继是按中序序列化而来。</p>
<h3 id="查找"><a href="#查找" class="headerlink" title="查找"></a>查找</h3><p>&#160; &#160; &#160; &#160;从二叉排序树中进行查找时，根据树的性质，节点的左子树必定小于根节点，右子树必定大于根结点。如果查找的节点值小于根节点，则进入左子树，大于进入右子树，重复这个比较步骤直到找到这个节点或者这个节点不存在。 </p>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>&#160; &#160; &#160; &#160;二叉树以链式方式存储，保持了链接存储结构在执行插入或删除操作时不用移动元素的优点，只要找到合适的插入和删除位置后，仅需要修改链接指针节课。插入删除的时间性能比较好。而丢与二拆排序树的查找，走的就是从根节点到要查找的节点的路径，其比较次数等于给定值的节点在二叉排序树的层数。极端情况，最少为1次，即根节点就是要找的节点，最多也不会超过树的深度。也就是说，二叉排序树的查找性能取决于二叉排序树的形状。可问题就在于，二叉排序树的形状是不确定的。可能出现以下情况，而二叉平衡树就不会：</p>
<p><img src="http://img.blog.csdn.net/20140414214957765?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2FuZ3l1bnl1bjAw/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""></p>
<h2 id="二叉平衡树／AVL"><a href="#二叉平衡树／AVL" class="headerlink" title="二叉平衡树／AVL"></a>二叉平衡树／AVL</h2><p>&#160; &#160; &#160; &#160;ALV树中左右子树的高度差值最大不超过1。树在查找、插入和删除时平均和最坏的情况下都是O(logn)。在一颗二叉搜索树查找一个值的平均时间复杂度为log(n)，但是若查找树的所有的节点向一边倾斜，这时候的查找就退化为线性查找，复杂度为n。为了获得更高的查找效率，就有了AVL树的概念，对于一颗非平衡的AVL树，可以通过旋转变换为AVL树。</p>
<p>&#160; &#160; &#160; &#160;由于平衡二叉树也是排序二叉树，所以使用二叉树的插入、删除和查找操作即可。只是在操作完成后，为了下次能够保持一个好的检索效率，也为了防止这个链表退化为普通链表，则需要对树进行旋转。旋转可以再次达到平衡。主要包括：左旋转、右旋转、左右旋转、右左旋转。</p>
<h3 id="LL"><a href="#LL" class="headerlink" title="LL"></a>LL</h3><p>&#160; &#160; &#160; &#160;插入一个新节点到根节点的左子树的左子树，导致根节点的平衡因子由1变为2.需要 右 旋转来解决。</p>
<p><img src="http://img.blog.csdn.net/20140226092057593" alt=""></p>
<h3 id="LR"><a href="#LR" class="headerlink" title="LR"></a>LR</h3><p>插入一个新节点到根节点的左子树的左子树，导致根节点的平衡因子由1插入一个新节点到根节点的左子树的右子树，导致根节点的平衡因子由1变为2.需要先左旋后右旋转来解决。</p>
<p><img src="http://img.blog.csdn.net/20140226092128906" alt=""></p>
<h3 id="RR"><a href="#RR" class="headerlink" title="RR"></a>RR</h3><p>&#160; &#160; &#160; &#160;插入一个新节点到根节点的右子树的 右 子树，导致根节点的平衡因子由1变为2.需要 左 旋转来解决。</p>
<h3 id="RL"><a href="#RL" class="headerlink" title="RL"></a>RL</h3><p>&#160; &#160; &#160; &#160;插入一个新节点到根节点的 右 子树的左子树，导致根节点的平衡因子由1变为2.需要先 右 旋后 左 旋转来解决。</p>
<h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><h4 id="优点"><a href="#优点" class="headerlink" title="优点"></a>优点</h4><p>&#160; &#160; &#160; &#160;在最坏情况下仍有较好的性能。每个操作中处理每个结点的时间都不会超过一个很小的常数，且这两个操作都只会访问一条路径上的结点，所以任何查找或者插入的成本都肯定不会超过对数级别。<br>完美平衡的2-3树要平展的多。例如，含有10亿个结点的一颗2-3树的高度仅在19到30之间。我们最多只需要访问30个结点就能在10亿个键中进行任意查找和插入操作。</p>
<h4 id="缺点"><a href="#缺点" class="headerlink" title="缺点"></a>缺点</h4><p>&#160; &#160; &#160; &#160;我们需要维护两种不同类型的结点，查找和插入操作的实现需要大量的代码，而且它们所产生的额外开销可能会使算法比标准的二叉查找树更慢。平衡一棵树的初衷是为了消除最坏情况，但我们希望这种保障所需的代码能够越少越好。</p>
<h2 id="红黑树／RBT"><a href="#红黑树／RBT" class="headerlink" title="红黑树／RBT"></a>红黑树／RBT</h2><p>&#160; &#160; &#160; &#160;红黑树是一个更高效的检索二叉树，因此常常用来实现关联数组。典型地，JDK 提供的集合类 TreeMap 本身就是一个红黑树的实现。排序二叉树的深度直接影响了检索的性能，当插入节点本身就是由小到大排列时，排序二叉树将变成一个链表，这种排序二叉树的检索性能最低：N 个节点的二叉树深度就是 N-1。但是红黑树通过上面这种限制来保证它大致是平衡的——因为红黑树的高度不会无限增高，这样保证红黑树在最坏情况下都是高效的，不会出现普通排序二叉树的情况。<br>红黑树在原有的排序二叉树增加了如下几个要求：</p>
<p>性质 1：每个节点要么是红色，要么是黑色。</p>
<p>性质 2：根节点永远是黑色的。</p>
<p>性质 3：所有的叶节点都是空节点（即 nil），并且是黑色的。</p>
<p>性质 4：每个红色节点的两个子节点都是黑色。（从每个叶子到根的路径上不会有两个连续的红色节点）。</p>
<p>性质 5：从任一节点到其子树中每个叶子节点的路径都包含相同数量的黑色节点。</p>
<p><img src="http://img.my.csdn.net/uploads/201212/12/1355319681_6107.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;之前我们了解了二叉查找树的插入，接下来，咱们便来具体了解下红黑树的插入操作。红黑树的插入相当于在二叉查找树插入的基础上，为了重新恢复平衡，继续做了插入修复操作。<br>下面来看一下几个约定：</p>
<p>&#160; &#160; &#160; &#160;（1）性质 3 中指定红黑树的每个叶子节点都是空节点，而且叶子节点都是黑色 Java 实现的红黑树将使用 null 来代表，因此遍历红黑树时将看不到黑色的叶子节点，反而看到每个叶子节点都是红色的。非叶子节点，也就是非null节点称为红黑树中的儿子节点。</p>
<p>&#160; &#160; &#160; &#160;（2） 红黑树从根节点到每个叶子节点的路径都包含相同数量的黑色节点，因此从根节点到叶子节点的路径中包含的黑色节点数被称为树的“黑色高度”。</p>
<p>&#160; &#160; &#160; &#160;对于给定的黑色高度为 N 的红黑树，从根到叶子节点的最短路径长度为 N-1，最长路径长度为 2 * (N-1)。</p>
<p>&#160; &#160; &#160; &#160;假如有一棵黑色高度为 3 的红黑树：从根节点到叶节点的最短路径长度是 2，该路径上全是黑色节点（黑节点 - 黑节点 - 黑节点）。最长路径也只可能为 4，在每个黑色节点之间插入一个红色节点（黑节点 - 红节点 - 黑节点 - 红节点 - 黑节点），性质 4 保证绝不可能插入更多的红色节点。由此可见，红黑树中最长路径就是一条红黑交替的路径。</p>
<p>&#160; &#160; &#160; &#160;红黑树能够以O(log2(N))的时间复杂度进行搜索、插入、删除操作。此外,任何不平衡都会在3次旋转之内解决。这一点是AVL所不具备的。红黑树并不追求“完全平衡”——它只要求部分地达到平衡要求，降低了对旋转的要求，从而提高了性能。  </p>
<h2 id="B-Tree／平衡多路搜索树"><a href="#B-Tree／平衡多路搜索树" class="headerlink" title="B-Tree／平衡多路搜索树"></a>B-Tree／平衡多路搜索树</h2><p>&#160; &#160; &#160; &#160;性质如下，图片显示M=3时的例子：</p>
<ol>
<li>定义任意非叶子结点最多只有M个儿子；且M&gt;2；</li>
<li>根结点的儿子数为[2, M]；</li>
<li>除根结点以外的非叶子结点的儿子数为[M/2, M]；</li>
<li>每个结点存放至少M/2-1（取上整）和至多M-1个关键字；（至少2个关键字）</li>
<li>非叶子结点的关键字个数=指向儿子的指针个数-1；</li>
<li>非叶子结点的关键字：K[1], K[2], …, K[M-1]；且K[i] &lt; K[i+1]；</li>
<li>非叶子结点的指针：P[1], P[2], …, P[M]；其中P[1]指向关键字小于K[1]的子树，P[M]指向关键字大于K[M-1]的子树，其它P[i]指向关键字属于(K[i-1], K[i])的子树；</li>
<li>所有叶子结点位于同一层；</li>
</ol>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/manesking/4.JPG" alt=""></p>
<p>&#160; &#160; &#160; &#160;B-树的搜索，从根结点开始，对结点内的关键字（有序）序列进行二分查找，如果命中则结束，否则进入查询关键字所属范围的儿子结点；重复，直到所对应的儿子指针为空，或已经是叶子结点；</p>
<h3 id="B-树的特性："><a href="#B-树的特性：" class="headerlink" title="B-树的特性："></a>B-树的特性：</h3><ol>
<li>关键字集合分布在整颗树中；</li>
<li>任何一个关键字出现且只出现在一个结点中；</li>
<li>搜索有可能在非叶子结点结束；</li>
<li>其搜索性能等价于在关键字全集内做一次二分查找；</li>
<li>自动层次控制；</li>
</ol>
<p>&#160; &#160; &#160; &#160;由于限制了除根结点以外的非叶子结点，至少含有M/2个儿子，确保了结点的至少利用率，其最底搜索性能为：</p>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/manesking/0.JPG" alt=""></p>
<h2 id="B-树"><a href="#B-树" class="headerlink" title="B+树"></a>B+树</h2><p>&#160; &#160; &#160; &#160;B+树是B-树的变体，也是一种多路搜索树：</p>
<ol>
<li>其定义基本与B-树同，除了：</li>
<li>非叶子结点的子树指针与关键字个数相同；</li>
<li>非叶子结点的子树指针P[i]，指向关键字值属于[K[i], K[i+1])的子树（B-树是开区间）；</li>
<li>为所有叶子结点增加一个链指针；</li>
<li>所有关键字都在叶子结点出现；</li>
</ol>
<p><img src="http://p.blog.csdn.net/images/p_blog_csdn_net/manesking/5.JPG" alt=""></p>
<p>&#160; &#160; &#160; &#160;B+的搜索与B-树也基本相同，区别是B+树只有达到叶子结点才命中（B-树可以在非叶子结点命中），其性能也等价于在关键字全集做一次二分查找；</p>
<h3 id="B-的特性："><a href="#B-的特性：" class="headerlink" title="B+的特性："></a>B+的特性：</h3><ol>
<li>所有关键字都出现在叶子结点的链表中（稠密索引），且链表中的关键字恰好是有序的；</li>
<li>不可能在非叶子结点命中；</li>
<li>非叶子结点相当于是叶子结点的索引（稀疏索引），叶子结点相当于是存储（关键字）数据的数据层；</li>
<li>更适合文件索引系统；</li>
</ol>
<p>&#160; &#160; &#160; &#160;b+tree每层节点直接还有链表连接，增加局部和范围访问效率,尾部还有聚集和非聚集区别，这数据结构很适合存储引擎</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;数据结构（一）——树&quot;&gt;&lt;a href=&quot;#数据结构（一）——树&quot; class=&quot;headerlink&quot; title=&quot;数据结构（一）——树&quot;&gt;&lt;/a&gt;数据结构（一）——树&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;在数据结构的学习当中，看到后面树，图，散列的时候已经有点难度，理解的较浅，学习完后面的知识，就发现，这些数据结构和经典算法都用在了一些常用工具的实现原理上，所以需要把这些知识总结一下,今天重点说下树。&lt;/p&gt;
    
    </summary>
    
      <category term="数据结构" scheme="http://yoursite.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
    
      <category term="数据结构" scheme="http://yoursite.com/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
  </entry>
  
  <entry>
    <title>SpringMvc学习笔记（二）</title>
    <link href="http://yoursite.com/2017/04/10/SpringMvc%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%BA%8C%EF%BC%89/"/>
    <id>http://yoursite.com/2017/04/10/SpringMvc学习笔记（二）/</id>
    <published>2017-04-10T08:24:44.000Z</published>
    <updated>2017-07-04T10:47:23.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="SpringMvc学习笔记（二）"><a href="#SpringMvc学习笔记（二）" class="headerlink" title="SpringMvc学习笔记（二）"></a>SpringMvc学习笔记（二）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;项目最先用到了.Net MVC，其实我的Web开发经历一开始就是MVC模式，学习SpringMvc的过程中，也发现很多类似之处，学习SpringMvc过程中更注重原理，之前更注重使用。下面记录学习的一些体会。</p>
 <a id="more"></a>
<h2 id="回顾总结"><a href="#回顾总结" class="headerlink" title="回顾总结"></a>回顾总结</h2><h3 id="框架相关"><a href="#框架相关" class="headerlink" title="框架相关"></a>框架相关</h3><ol>
<li>DispatcherServlet前端控制器：接收request，进行response</li>
<li>HandlerMapping处理器映射器：根据url查找Handler。（可以通过xml配置方式，注解方式）</li>
<li>HandlerAdapter处理器适配器：根据特定规则去执行Handler，编写Handler时需要按照HandlerAdapter的要求去编写。</li>
<li>Handler处理器（后端控制器）：需要程序员去编写，常用注解开发方式。<br> Handler处理器执行后结果 是ModelAndView，具体开发时Handler返回方法值类型包括 ：ModelAndView、String（逻辑视图名）、void（通过在Handler形参中添加request和response，类似原始 servlet开发方式，注意：可以通过指定response响应的结果类型实现json数据输出）</li>
<li>View resolver视图解析器：根据逻辑视图名生成真正的视图（在springmvc中使用View对象表示）</li>
<li>View视图:jsp页面，仅是数据展示，没有业务逻辑。</li>
</ol>
<h3 id="注解开发"><a href="#注解开发" class="headerlink" title="注解开发"></a>注解开发</h3><p>&#160; &#160; &#160; &#160;使用mvc注解驱动mvc:annotation-driven来进行处理器映射器和适配器的配置。</p>
<p>&#160; &#160; &#160; &#160;@controller注解必须要加，作用标识类是一个Handler处理器。</p>
<p>&#160; &#160; &#160; &#160;@requestMapping注解必须要加，作用：</p>
<p>1、对url和Handler的方法进行映射。</p>
<p>2、可以窄化请求映射，设置Handler的根路径，url就是根路径+子路径请求方式</p>
<p>3、可以限制http请求的方法。映射成功后，springmvc框架生成一个Handler对象，对象中只包括 一个映射成功的method。</p>
<p>&#160; &#160; &#160; &#160;注解开发中的参数绑定，默认支持：</p>
<p>1、默认支持很多类型，HttpServletRequest、response、session、model/modelMap(将模型数据填充到request域)</p>
<p>2、支持简单数据类型，整型、字符串、日期</p>
<p>&#160; &#160; &#160; &#160;只要保证request请求的参数名和形参名称一致，自动绑定成功</p>
<p>&#160; &#160; &#160; &#160;如果request请求的参数名和形参名称不一致，可以使用@RequestParam（指定request请求的参数名），@RequestParam加在形参的前边。</p>
<p>3、支持pojo类型</p>
<p>&#160; &#160; &#160; &#160;只要保证request请求的参数名称和pojo中的属性名一致，自动将request请求的参数设置到pojo的属性中。</p>
<p>&#160; &#160; &#160; &#160;注意：形参中即有pojo类型又有简单类型，参数绑定互不影响。</p>
<h2 id="使用技巧"><a href="#使用技巧" class="headerlink" title="使用技巧"></a>使用技巧</h2><h3 id="数据校验"><a href="#数据校验" class="headerlink" title="数据校验"></a>数据校验</h3><p>&#160; &#160; &#160; &#160;springmvc使用的是hibernate的校验框架vaildation，但是和hibernate一点关系都没有。目的是解耦前台数据检验和错误信息返回。参考博文：<a href="http://jinnianshilongnian.iteye.com/blog/1733708" target="_blank" rel="external">SpringMVC数据验证</a>.</p>
<h3 id="数据回显"><a href="#数据回显" class="headerlink" title="数据回显"></a>数据回显</h3><p>&#160; &#160; &#160; &#160;提交数据失败之后，数据在填入表单。<a href="http://www.cnblogs.com/lcngu/p/5517877.html" target="_blank" rel="external">SpringMVC学习–数据回显</a>.</p>
<h3 id="统一异常处理"><a href="#统一异常处理" class="headerlink" title="统一异常处理"></a>统一异常处理</h3><p>&#160; &#160; &#160; &#160;Java异常类层次结构图：</p>
<p><img src="http://img.my.csdn.net/uploads/201211/27/1354020417_5176.jpg" alt=""></p>
<p>关于异常可参考博文：<a href="http://blog.csdn.net/hguisu/article/details/6155636" target="_blank" rel="external">深入理解java异常处理机制</a>.</p>
<p>springmvc中异常处理思路是：系统的dao、service、controller出现都通过throwsException向上抛出，最后由springmvc的前端控制器交由异常处理器（一个系统只有一个，由框架提供）进行异常处理。</p>
<p>处理的思路是：解析出异常的类型，如果是自定义的直接取出异常信息，在错误页面展示。如果不是系统自定义的，构造一个自定义的异常类型，为未知错误。提供HandlerExceptionResolver接口</p>
<h3 id="RESTful"><a href="#RESTful" class="headerlink" title="RESTful"></a>RESTful</h3><p>&#160; &#160; &#160; &#160;RESTful架构：是一种设计的风格，并不是标准，只是提供了一组设计原则和约束条件，也是目前比较流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便，所以正得到越来越多网站的采用。参考：<a href="http://www.cnblogs.com/huaixiaoz/p/5782954.html" target="_blank" rel="external">SPRINGMVC中的RESTFUL架构风格
</a>.</p>
<h3 id="json交互"><a href="#json交互" class="headerlink" title="json交互"></a>json交互</h3><p>&#160; &#160; &#160; &#160;json格式在接口调用中、html页面中常用，格式比较简单，解析还比较方便。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;SpringMvc学习笔记（二）&quot;&gt;&lt;a href=&quot;#SpringMvc学习笔记（二）&quot; class=&quot;headerlink&quot; title=&quot;SpringMvc学习笔记（二）&quot;&gt;&lt;/a&gt;SpringMvc学习笔记（二）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;项目最先用到了.Net MVC，其实我的Web开发经历一开始就是MVC模式，学习SpringMvc的过程中，也发现很多类似之处，学习SpringMvc过程中更注重原理，之前更注重使用。下面记录学习的一些体会。&lt;/p&gt;
    
    </summary>
    
      <category term="SSM框架" scheme="http://yoursite.com/categories/SSM%E6%A1%86%E6%9E%B6/"/>
    
    
      <category term="SpringMvc" scheme="http://yoursite.com/tags/SpringMvc/"/>
    
  </entry>
  
  <entry>
    <title>SpringMvc学习笔记（一）</title>
    <link href="http://yoursite.com/2017/04/04/SpringMvc%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%80%EF%BC%89/"/>
    <id>http://yoursite.com/2017/04/04/SpringMvc学习笔记（一）/</id>
    <published>2017-04-04T05:49:59.000Z</published>
    <updated>2017-07-04T08:11:56.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="SpringMvc学习笔记（一）"><a href="#SpringMvc学习笔记（一）" class="headerlink" title="SpringMvc学习笔记（一）"></a>SpringMvc学习笔记（一）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;项目最先用到了.Net MVC，其实我的Web开发经历一开始就是MVC模式，学习SpringMvc的过程中，也发现很多类似之处，学习SpringMvc过程中更注重原理，之前更注重使用。下面记录学习的一些体会。</p>
 <a id="more"></a>
<h2 id="SpringMvc框架原理"><a href="#SpringMvc框架原理" class="headerlink" title="SpringMvc框架原理"></a>SpringMvc框架原理</h2><p>&#160; &#160; &#160; &#160;MVC全名是Model View Controller，是模型(model)－视图(view)－控制器(controller)的缩写，一种软件设计典范，用一种业务逻辑、数据、界面显示分离的方法组织代码，将业务逻辑聚集到一个部件里面，在改进和个性化定制界面及用户交互的同时，不需要重新编写业务逻辑。MVC被独特的发展起来用于映射传统的输入、处理和输出功能在一个逻辑的图形化用户界面的结构中。</p>
<p>&#160; &#160; &#160; &#160;SpringMvc中有一些组件包括：前端控制器、处理器映射器、处理器适配器、视图解析器。</p>
<ol>
<li><p>前端控制器（DispatcherServlet）：接收请求，响应结果，相当于电脑的CPU。</p>
</li>
<li><p>处理器映射器（HandlerMapping）：根据URL去查找处理器</p>
</li>
<li><p>处理器（Handler）：（需要程序员去写代码处理逻辑的）</p>
</li>
<li><p>处理器适配器（HandlerAdapter）：会把处理器包装成适配器，这样就可以支持多种类型的处理器，类比笔记本的适配器（适配器模式的应用）</p>
</li>
<li><p>视图解析器（ViewResovler）：进行视图解析，多返回的字符串，进行处理，可以解析成对应的页面</p>
</li>
</ol>
<p>运行的框架图如下：</p>
<p><img src="http://img.my.csdn.net/uploads/201211/16/1353059506_5137.jpg" alt=""></p>
<p>运行的步骤可以概述如下：</p>
<p>第一步：发起请求到前端控制器(DispatcherServlet)</p>
<p>第二步：前端控制器请求HandlerMapping查找 Handler可以根据xml配置、注解进行查找</p>
<p>第三步：处理器映射器HandlerMapping向前端控制器返回Handler执行链。</p>
<p>第四步：前端控制器调用处理器适配器去执行Handler</p>
<p>第五步：处理器适配器去执行Handler（需人为编写的Handler得实现适配器的相关接口）</p>
<p>第六步：Handler执行完成给适配器返回ModelAndView</p>
<p>第七步：处理器适配器向前端控制器返回ModelAndViewModelAndView是springmvc框架的一个底层对象，包括 Model和view</p>
<p>第八步：前端控制器请求视图解析器去进行视图解析根据逻辑视图名解析成真正的视图(jsp)</p>
<p>第九步：视图解析器向前端控制器返回View</p>
<p>第十步：前端控制器进行视图渲染视图渲染，就是将模型数据(在ModelAndView对象中)填充到request域</p>
<p>第十一步：前端控制器向用户响应结果 </p>
<h2 id="初识源码"><a href="#初识源码" class="headerlink" title="初识源码"></a>初识源码</h2><h3 id="SpringMVC接口解释"><a href="#SpringMVC接口解释" class="headerlink" title="SpringMVC接口解释"></a>SpringMVC接口解释</h3><ol>
<li><p>DispatcherServlet接口：<br>Spring提供的前端控制器，所有的请求都有经过它来统一分发。在DispatcherServlet将请求分发给Spring Controller之前，需要借助于Spring提供的HandlerMapping定位到具体的Controller。</p>
</li>
<li><p>HandlerMapping接口：<br>能够完成客户请求到Controller映射。</p>
</li>
<li><p>Controller接口：<br>需要为并发用户处理上述请求，因此实现Controller接口时，必须保证线程安全并且可重用。<br>Controller将处理用户请求，这和Struts Action扮演的角色是一致的。一旦Controller处理完用户请求，则返回ModelAndView对象给DispatcherServlet前端控制器，ModelAndView中包含了模型（Model）和视图（View）。<br>从宏观角度考虑，DispatcherServlet是整个Web应用的控制器；从微观考虑，Controller是单个Http请求处理过程中的控制器，而ModelAndView是Http请求过程中返回的模型（Model）和视图（View）。</p>
</li>
<li><p>ViewResolver接口：<br>Spring提供的视图解析器（ViewResolver）在Web应用中查找View对象，从而将相应结果渲染给客户。</p>
</li>
</ol>
<h3 id="执行过程"><a href="#执行过程" class="headerlink" title="执行过程"></a>执行过程</h3><p>第一步：前端控制器接收请求，调用doDiapatch方法</p>
<p>第二步：前端控制器调用处理器映射器查找 Handler，getHandler方法根据url找到执行链，包括拦截器和控制器。</p>
<p>第三步：调用处理器适配器执行Handler，得到执行结果ModelAndView</p>
<p>第四步：视图渲染，view的render方法，将model数据填充到request域。</p>
<h2 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h2><ol>
<li>首先前端控制器是一个DispatcherServlet，要在web.xml中配置。这里类似Servlet的配置，可以参考博文：<a href="http://www.cnblogs.com/hxsyl/p/3435412.html" target="_blank" rel="external">JavaWeb工程中web.xml基本配置</a>。</li>
<li><p>servlet中声明的springmvc.xml中需要配置处理器映射器、处理器适配器、视图解析器。 这些都是接口抽象类高扩展性的编写方式，非注解模式一般使用simpleControllerHandlerAdapter作为适配器，BeanForNameHadnlerMapping作为映射器（根据bean的name作为url来查找，这就需要配置）。视图解析器使用InternalResourceViewSolver，能够解析jsp。现在一般使用注解模式。</p>
</li>
<li><p>编写hadler，即实现controller接口的业务逻辑处理类，返回ModelAndView。</p>
</li>
<li>前台页面编写。</li>
</ol>
<h2 id="SpringMvc和Mybatis整合"><a href="#SpringMvc和Mybatis整合" class="headerlink" title="SpringMvc和Mybatis整合"></a>SpringMvc和Mybatis整合</h2><p>&#160; &#160; &#160; &#160;整合思想如下：</p>
<p>第一步：整合dao层</p>
<p>&#160; &#160; &#160; &#160;mybatis和spring整合，通过spring管理mapper接口。</p>
<p>&#160; &#160; &#160; &#160;使用mapper的扫描器自动扫描mapper接口在spring中进行注册。</p>
<p>第二步：整合service层</p>
<p>&#160; &#160; &#160; &#160;通过spring管理 service接口。</p>
<p>&#160; &#160; &#160; &#160;使用配置方式将service接口配置在spring配置文件中。</p>
<p>&#160; &#160; &#160; &#160;实现事务控制。</p>
<p>第三步：整合springmvc</p>
<p>&#160; &#160; &#160; &#160;由于springmvc是spring的模块，不需要整合。</p>
<h2 id="tip"><a href="#tip" class="headerlink" title="tip"></a>tip</h2><ol>
<li>视图解析器可以配置前缀和后缀。</li>
<li>前台往后台传值，后台往前台传值的参数绑定和使用方式。</li>
</ol>
<h2 id="springmvc和struts2区别"><a href="#springmvc和struts2区别" class="headerlink" title="springmvc和struts2区别"></a>springmvc和struts2区别</h2><ol>
<li>Struts2是类级别的拦截， 一个类对应一个request上下文，SpringMVC是方法级别的拦截，一个方法对应一个request上下文，而方法同时又跟一个url对应,所以说从架构本身上SpringMVC就容易实现restful url,而struts2的架构实现起来要费劲，因为Struts2中Action的一个方法可以对应一个url，而其类属性却被所有方法共享，这也就无法用注解或其他方式标识其所属方法了。</li>
<li>由上边原因，SpringMVC的方法之间基本上独立的，独享request response数据，请求数据通过参数获取，处理结果通过ModelMap交回给框架，方法之间不共享变量，而Struts2搞的就比较乱，虽然方法之间也是独立的，但其所有Action变量是共享的，这不会影响程序运行，却给我们编码 读程序时带来麻烦，每次来了请求就创建一个Action，一个Action对象对应一个request上下文。</li>
<li>由于Struts2需要针对每个request进行封装，把request，session等servlet生命周期的变量封装成一个一个Map，供给每个Action使用，并保证线程安全，所以在原则上，是比较耗费内存的。</li>
<li>拦截器实现机制上，Struts2有以自己的interceptor机制，SpringMVC用的是独立的AOP方式，这样导致Struts2的配置文件量还是比SpringMVC大。</li>
<li>SpringMVC的入口是servlet，而Struts2是filter（这里要指出，filter和servlet是不同的。以前认为filter是servlet的一种特殊），这就导致了二者的机制不同，这里就牵涉到servlet和filter的区别了。</li>
<li>SpringMVC集成了Ajax，使用非常方便，只需一个注解@ResponseBody就可以实现，然后直接返回响应文本即可，而Struts2拦截器集成了Ajax，在Action中处理时一般必须安装插件或者自己写代码集成进去，使用起来也相对不方便。</li>
<li>SpringMVC验证支持JSR303，处理起来相对更加灵活方便，而Struts2验证比较繁琐，感觉太烦乱。</li>
<li>spring MVC和Spring是无缝的。从这个项目的管理和安全上也比Struts2高（当然Struts2也可以通过不同的目录结构和相关配置做到SpringMVC一样的效果，但是需要xml配置的地方不少）。</li>
<li>设计思想上，Struts2更加符合OOP的编程思想， SpringMVC就比较谨慎，在servlet上扩展。</li>
<li>SpringMVC开发效率和性能高于Struts2。</li>
<li>SpringMVC可以认为已经100%零配置。</li>
</ol>
<p>转自：<a href="http://blog.csdn.net/chenleixing/article/details/44570681" target="_blank" rel="external">SpringMVC与Struts2区别与比较总结</a>。</p>
<p>简介一些的可以看下面叙述：</p>
<p>一、pringmvc基于方法开发的，struts2基于类开发的。</p>
<p>springmvc将url和controller方法映射。映射成功后springmvc生成一个Handler对象，对象中只包括了一个method。<br>方法执行结束，形参数据销毁。<br>springmvc的controller开发类似service开发。</p>
<p>二、springmvc可以进行单例开发，并且建议使用单例开发，struts2通过类的成员变量接收参数，无法使用单例，只能使用多例。</p>
<p>三、经过实际测试，struts2速度慢，在于使用struts标签，如果使用struts建议使用jstl。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;SpringMvc学习笔记（一）&quot;&gt;&lt;a href=&quot;#SpringMvc学习笔记（一）&quot; class=&quot;headerlink&quot; title=&quot;SpringMvc学习笔记（一）&quot;&gt;&lt;/a&gt;SpringMvc学习笔记（一）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;项目最先用到了.Net MVC，其实我的Web开发经历一开始就是MVC模式，学习SpringMvc的过程中，也发现很多类似之处，学习SpringMvc过程中更注重原理，之前更注重使用。下面记录学习的一些体会。&lt;/p&gt;
    
    </summary>
    
      <category term="SSM框架" scheme="http://yoursite.com/categories/SSM%E6%A1%86%E6%9E%B6/"/>
    
    
      <category term="SpringMvc" scheme="http://yoursite.com/tags/SpringMvc/"/>
    
  </entry>
  
  <entry>
    <title>实现简易web服务器和web浏览器</title>
    <link href="http://yoursite.com/2017/04/02/web%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%92%8Cweb%E6%B5%8F%E8%A7%88%E5%99%A8%E7%9A%84%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
    <id>http://yoursite.com/2017/04/02/web服务器和web浏览器的实现原理/</id>
    <published>2017-04-02T04:31:39.000Z</published>
    <updated>2017-05-23T05:30:54.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="web服务器和web浏览器的实现原理"><a href="#web服务器和web浏览器的实现原理" class="headerlink" title="web服务器和web浏览器的实现原理"></a>web服务器和web浏览器的实现原理</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;预警：此篇篇幅很长，可通过菜单导航观看。最近在复习计算机网络的知识，因为想在Java web方面深入了解，所以在思考B／S结构模式的工作是怎样的过程呢，刚好找了好些资料和别人的总结，现在把这些都整理一下。在基础中已经学习了socket和http协议，我们也知道通过浏览器输入一个地址，访问一个网页的操作。实际对应的底层操作简单来说就是：客户端(浏览器)面向于WEB服务器的网络通信。</p>
 <a id="more"></a>
<p>&#160; &#160; &#160; &#160;那么，既然是网络通信。对应于Java当中来说，就自然离不开Socket与IO流。其实这也正是Web服务器与浏览器的基础实现原理。当然，想要开发一套完善的WEB服务器或浏览器，需要做的工作是很复杂的，要考虑多线程、并发、io流、网络等等。但我们想要了解的只是其原理。后面将对使用较多的servlet和tomcat在进行分析，达到一个比较深入的理解。本篇在此博客基础上进一步整理：<a href="http://blog.csdn.net/ghost_programmer/article/details/43446531" target="_blank" rel="external">浅析web服务器与浏览器的实现原理</a>。</p>
<h2 id="原理概述"><a href="#原理概述" class="headerlink" title="原理概述"></a>原理概述</h2><p>&#160; &#160; &#160; &#160;我们知道，将开发的web项目部署到tomcat服务器之后，就可以通过浏览器对服务器上的资源进行访问。但重要的一点是，存在多种不同厂商开发的不同浏览器。但各个类型的WEB浏览器，都可以正常的访问tomcat服务器上的资源。对此，我们可以这样理解：我开发了一个WEB服务器，并且能够保证其他人开发的客户端都能够与我的服务器正常通信。</p>
<p>&#160; &#160; &#160; &#160;能够实现这样的目的的前提自然就是，你要制定一个规范，并让想要与你开发的服务器正常进行通信的客户端都遵循这个规范来实现。这个规范，也就是所谓的协议。</p>
<p>&#160; &#160; &#160; &#160;正如在网络通信中，数据的传输可以遵循TCP/IP或UDP协议一样。WEB服务器与WEB浏览器之间，也通过一种双方都熟悉的语言进行通信。这种协议即是：超文本传输协议，也就是HTTP协议。不同的是，TCP/IP与UDP议是传输层当中的通信协议，而HTTP协议是应用层当中的协议。</p>
<p>&#160; &#160; &#160; &#160;当我们想要使用Java语言实现所谓的WEB通信，自然也应当遵循HTTP协议。Java中已经为我们提供了这样的一种实现规范，也就是广为人知的：Servlet接口。而我们开发web项目时,最常用到的HttpServlet类，就是基于此接口实现的具体子类。该类封装和提供了，针对基于Http协议通信的内容进行访问和操作的常用方法。</p>
<h2 id="Servlet实例"><a href="#Servlet实例" class="headerlink" title="Servlet实例"></a>Servlet实例</h2><p>&#160; &#160; &#160; &#160;首先，我们通过一段简单的Servlet代码来看一下，基于HTTP协议进行WEB通信的请求信息。我的环境是os x+idea+tomcat7，项目工程我放在github上，链接传送门：<a href="https://github.com/wustzoujing/MyBrowerAndMyTomcat" target="_blank" rel="external">wustzoujing/MyBrowerAndMyTomcat</a>。</p>
<p>&#160; &#160; &#160; &#160;首先要了解servlet，这个基础没有的话，可以参考这个入门教程<a href="http://www.runoob.com/servlet/servlet-tutorial.html" target="_blank" rel="external">servlet教程</a>。从helloworld开始做。这里我们基于HttpServlet+Tomcat，先建立一个web的项目，再写servlet测试类，然后配置web.xml，最后用tomcat发布。</p>
<p>&#160; &#160; &#160; &#160;这里贴上一些代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div></pre></td><td class="code"><pre><div class="line">import javax.servlet.http.*;</div><div class="line">import javax.servlet.*;</div><div class="line">import java.io.IOException;</div><div class="line">import java.util.Enumeration;</div><div class="line">/**</div><div class="line"> * Created by zoujing on 2017/5/10.</div><div class="line"> */</div><div class="line">public class ServeletTest extends HttpServlet &#123;</div><div class="line">    public void doGet(HttpServletRequest request, HttpServletResponse response)</div><div class="line">            throws ServletException, IOException &#123;</div><div class="line">        for (Enumeration e = request.getHeaderNames(); e.hasMoreElements();) &#123;</div><div class="line">            String header = (String) e.nextElement();</div><div class="line">            if (header != null)</div><div class="line">                System.out.println((new StringBuilder(String.valueOf(header)))</div><div class="line">                        .append(&quot;:&quot;).append(request.getHeader(header))</div><div class="line">                        .toString());</div><div class="line">        &#125;</div><div class="line"></div><div class="line">    &#125;</div><div class="line"></div><div class="line">    public void doPost(HttpServletRequest request, HttpServletResponse response)</div><div class="line">            throws ServletException, IOException &#123;</div><div class="line"></div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;接下来是web.xml的配置：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</div><div class="line">&lt;web-app xmlns=&quot;http://java.sun.com/xml/ns/javaee&quot;</div><div class="line">           xmlns:xsi=&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</div><div class="line">           xsi:schemaLocation=&quot;http://java.sun.com/xml/ns/javaee</div><div class="line">		  http://java.sun.com/xml/ns/javaee/web-app_3_1.xsd&quot;</div><div class="line">           version=&quot;3.0&quot;&gt;</div><div class="line"></div><div class="line"></div><div class="line">    &lt;servlet&gt;</div><div class="line">        &lt;servlet-name&gt;TomcatWeb&lt;/servlet-name&gt;</div><div class="line">        &lt;servlet-class&gt;ServeletTest&lt;/servlet-class&gt;</div><div class="line">    &lt;/servlet&gt;</div><div class="line"></div><div class="line">    &lt;servlet-mapping&gt;</div><div class="line">        &lt;servlet-name&gt;TomcatWeb&lt;/servlet-name&gt;</div><div class="line">        &lt;url-pattern&gt;/servlet/HttpServletDemo&lt;/url-pattern&gt;</div><div class="line">    &lt;/servlet-mapping&gt;</div><div class="line">&lt;/web-app&gt;</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;当我们在浏览器输入链接<a href="http://localhost:8080/servlet/HttpServletDemo的时候，会打印出下面信息：" target="_blank" rel="external">http://localhost:8080/servlet/HttpServletDemo的时候，会打印出下面信息：</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">host:localhost:8080</div><div class="line">accept:text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</div><div class="line">upgrade-insecure-requests:1</div><div class="line">cookie:JSESSIONID=92E7AE4F85499378342793886884D0C0; _ga=GA1.1.1073505442.1486722778</div><div class="line">user-agent:Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30</div><div class="line">accept-language:zh-cn</div><div class="line">accept-encoding:gzip, deflate</div><div class="line">connection:keep-alive</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;在计算机网络的学习中我们就知道了，这是http请求报文的内容，我们这里再回忆一下：一个HTTP协议的请求中，通常主要包含三个部分：</p>
<ol>
<li>方法/统一资源标示符(URI)/协议/版本 </li>
<li>请求标头</li>
<li>实体主体</li>
</ol>
<p>&#160; &#160; &#160; &#160;其中方法也就是所谓的get/post之类的请求方法，统一资源标示符也就是要访问的目标资源的路径，包括协议及协议版本，这些信息被放在请求的第一行。</p>
<p>&#160; &#160; &#160; &#160;随后，紧接着的便是请求标头；请求标头通常包含了与客户端环境及请求实体主体相关的有用信息。</p>
<p>&#160; &#160; &#160; &#160;最后，在标头与实体主体之间是一个空行。它对于HTTP请求格式是很重要的，空行告诉HTTP服务器，实体主体从这里开始。</p>
<h2 id="实现最简易的Tomcat"><a href="#实现最简易的Tomcat" class="headerlink" title="实现最简易的Tomcat"></a>实现最简易的Tomcat</h2><p>&#160; &#160; &#160; &#160;前面已经说过了，我们这里想要研究的，是WEB服务器的基本实现原理。那么我们自然想要自己来实现一下所谓的WEB服务器，我们已经知道了：所谓的B/S结构，实际上就是客户端与服务器之间基于HTTP协议的网络通信。那么，肯定是离不开socket与io的，所以我们可以简单的模拟一个最简易功能的web服务器：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line">import java.io.IOException;</div><div class="line">import java.io.InputStream;</div><div class="line">import java.net.ServerSocket;</div><div class="line">import java.net.Socket;</div><div class="line"></div><div class="line">/**</div><div class="line"> * Created by zoujing on 2017/5/10.</div><div class="line"> */</div><div class="line"></div><div class="line">public class MyTomcat &#123;</div><div class="line"></div><div class="line">    public static void main(String[] args) &#123;</div><div class="line">        try &#123;</div><div class="line">            ServerSocket tomcat = new ServerSocket(9090);</div><div class="line">            System.out.println(&quot;服务器启动&quot;);</div><div class="line">            //</div><div class="line">            Socket s = tomcat.accept();</div><div class="line">            //</div><div class="line">            byte[] buf = new byte[1024];</div><div class="line">            InputStream in = s.getInputStream();</div><div class="line">            //</div><div class="line">            int length = in.read(buf);</div><div class="line">            String request = new String(buf,0,length);</div><div class="line">            //</div><div class="line">            System.out.println(request);</div><div class="line">            </div><div class="line"></div><div class="line">        &#125; catch (IOException e) &#123;</div><div class="line"></div><div class="line">            e.printStackTrace();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="demo中所需理论"><a href="#demo中所需理论" class="headerlink" title="demo中所需理论"></a>demo中所需理论</h3><p>&#160; &#160; &#160; &#160;demo中我们用到了ServerSocket和Socket，这也是Java网络编程中必须知道的，分别使用与服务器端和客户端。下面我们看下区别：</p>
<h4 id="serverSocket类"><a href="#serverSocket类" class="headerlink" title="serverSocket类"></a>serverSocket类</h4><p>&#160; &#160; &#160; &#160;创建一个ServerSocket类，同时在运行该语句的计算机的指定端口处建立一个监听服务，如：上面的ServerSocket tomcat = new ServerSocket(9090);这里指定提供监听服务的端口是9090，一台计算机可以同时提供多个服务，这些不同的服务之间通过端口号来区别，不同的端口号上提供不同的服务。</p>
<p>&#160; &#160; &#160; &#160;为了随时监听可能的Client请求，执行如下的语句：Socket s = tomcat.accept();该语句调用了ServerSocket对象的accept()方法，这个方法的执行将使Server端的程序处于等待状态，程序将一直阻塞直到捕捉到一个来自Client端的请求，并返回一个用于与该Client通信的Socket对象Link-Socket。此后Server程序只要向这个Socket对象读写数据，就可以实现向远端的Client读写数据。结束监听时，关闭ServerSocket对象：如tomcat.close()；</p>
<h4 id="Socket类"><a href="#Socket类" class="headerlink" title="Socket类"></a>Socket类</h4><p>&#160; &#160; &#160; &#160;当Client程序需要从Server端获取信息及其他服务时，应创建一个Socket对象：Socket MySocket=new Socket(“ServerComputerName”，600)；</p>
<p>&#160; &#160; &#160; &#160;Socket类的构造函数有两个参数，第一个参数是欲连接到的Server计算机的主机地址，第二个参数是该Server机上提供服务的端口号。Socket对象建立成功之后，就可以在Client和Server之间建立一个连接，并通过这个连接在两个端点之间传递数据。利用Socket类的方法getOutputStream()和getInputStream()分别获得向Socket读写数据的输入／输出流，最后将从Server端读取的数据重新返还到Server端。</p>
<p>&#160; &#160; &#160; &#160;当Server和Client端的通信结束时，可以调用Socket类的close()方法关闭Socket，拆除连接。ServerSocket 一般仅用于设置端口号和监听，真正进行通信的是服务器端的Socket与客户端的Socket，在ServerSocket 进行accept之后，就将主动权转让了。</p>
<h4 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h4><p>&#160; &#160; &#160; &#160;服务器端程序设计：</p>
<p>&#160; &#160; &#160; &#160;在服务器端，利用ServerSocket类的构造函数ServerSocket(int port)创建一个ServerSocket类的对象，port参数传递端口，这个端口就是服务器监听连接请求的端口，如果在这时出现错误将抛出IOException异常对象，否则将创建ServerSocket对象并开始准备接收连接请求。</p>
<p>&#160; &#160; &#160; &#160;服务程序从调用ServerSocket的accept()方法开始，直到连接建立。在建立连接后，accept()返回一个最近创建的Socket对象，该Socket对象绑定了客户程序的IP地址或端口号。</p>
<p>&#160; &#160; &#160; &#160;客户端程序设计：</p>
<p>&#160; &#160; &#160; &#160;当客户程序需要与服务器程序通信时，需在客户机创建一个Socket对象。Socket类有构造函数Socket(InetAddress addr，int port)和Socket(String host，intport)，两个构造函数都创建了一个基于Socket的连接服务器端流套接字的流套接字。对于第一个InetAd-dress子类对象通过addr参数获得服务器主机的IP地址，对于第二个函数host参数包被分配到InetAddress对象中，如果没有IP地址与host参数相一致，那么将抛出UnknownHostException异常对象。两个函数都通过参数port获得服务器的端口号。假设已经建立连接了，网络API将在客户端基于Socket的流套接字中捆绑客户程序的IP地址和任意一个端口号，否则两个函数都会抛出一个IOException对象。</p>
<p>&#160; &#160; &#160; &#160;如果创建了一个Socket对象，那么它可通过get-InputStream()方法从服务程序获得输入流读传送来的信息，也可通过调用getOutputStream()方法获得输出流来发送消息。在读写活动完成之后，客户程序调用close()方法关闭流和流套接字。</p>
<h3 id="分析demo"><a href="#分析demo" class="headerlink" title="分析demo"></a>分析demo</h3><p>&#160; &#160; &#160; &#160;在demo中，我们开启一个服务器socket来监听9090端口，所以我们在通过在浏览器中输入<a href="http://localhost:9090，进行访问，经过我们demo可以得到的输出结果如下：" target="_blank" rel="external">http://localhost:9090，进行访问，经过我们demo可以得到的输出结果如下：</a></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">服务器启动</div><div class="line">GET / HTTP/1.1</div><div class="line">Host: localhost:9090</div><div class="line">Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8</div><div class="line">Upgrade-Insecure-Requests: 1</div><div class="line">Cookie: JSESSIONID=92E7AE4F85499378342793886884D0C0; _ga=GA1.1.1073505442.1486722778</div><div class="line">User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_4) AppleWebKit/603.1.30 (KHTML, like Gecko) Version/10.1 Safari/603.1.30</div><div class="line">Accept-Language: zh-cn</div><div class="line">Accept-Encoding: gzip, deflate</div><div class="line">Connection: keep-alive</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;通过成果我们看到，我们已经成功的实现了最简易的tomcat。不过这里需要注意的是，我们自己山寨的tomcat服务器当中，之所以也成功的输出了Http协议的请求体，是因为：我们是通过web浏览器进行访问的，如果通过普通的socket进行对serversocket的连接访问，是没有这些请求信息的。因为我们前面已经说过了，web浏览器与服务器之间的通信必须遵循Http协议。</p>
<h2 id="实现简易浏览器"><a href="#实现简易浏览器" class="headerlink" title="实现简易浏览器"></a>实现简易浏览器</h2><p>&#160; &#160; &#160; &#160;通过前面的学习，我相信我们现在更想把浏览器功能也实现，剖析一下，接下来我们就试试：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div></pre></td><td class="code"><pre><div class="line">import java.io.IOException;</div><div class="line">import java.io.InputStream;</div><div class="line">import java.io.PrintWriter;</div><div class="line">import java.net.Socket;</div><div class="line"></div><div class="line">/**</div><div class="line"> * Created by zoujing on 2017/5/10.</div><div class="line"> */</div><div class="line">public class MyBrower &#123;</div><div class="line">    public static void main(String[] args) &#123;</div><div class="line">        try &#123;</div><div class="line">            Socket browser = new Socket(&quot;127.0.0.1&quot;, 9090);</div><div class="line">            PrintWriter pw = new PrintWriter(browser.getOutputStream(),true);</div><div class="line">            // 封装请求第一行</div><div class="line">            pw.println(&quot;GET/ HTTP/1.1&quot;);</div><div class="line">            // 封装请求头</div><div class="line">            pw.println(&quot;User-Agent: Java/1.8.0_131&quot;);</div><div class="line">            pw.println(&quot;Host: 127.0.0.1:9090&quot;);</div><div class="line">            pw.println(&quot;Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8&quot;);</div><div class="line">            pw.println(&quot;Connection: keep-alive&quot;);</div><div class="line">            // 空行</div><div class="line">            pw.println();</div><div class="line">            // 封装实体主体</div><div class="line">            pw.println(&quot;UserName=JessieZou&amp;Age=22&quot;);</div><div class="line">            // 写入完毕</div><div class="line">            browser.shutdownOutput();</div><div class="line"></div><div class="line"></div><div class="line">            // 接受服务器返回信息，</div><div class="line">            InputStream in = browser.getInputStream();</div><div class="line">            //</div><div class="line">            int length = 0;</div><div class="line">            StringBuffer request = new StringBuffer();</div><div class="line">            byte[] buf = new byte[1024];</div><div class="line">            //</div><div class="line">            while ((length = in.read(buf)) != -1) &#123;</div><div class="line">                String line = new String(buf, 0, length);</div><div class="line">                request.append(line);</div><div class="line">            &#125;</div><div class="line">            System.out.println(request);</div><div class="line">            //browser.close();</div><div class="line">        &#125; catch (IOException e) &#123;</div><div class="line">            System.out.println(&quot;亲，出现了异常哦!&quot;);</div><div class="line">        &#125;finally&#123;</div><div class="line"></div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;同时改进我们的tomcat如下：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div></pre></td><td class="code"><pre><div class="line">import java.io.IOException;</div><div class="line">import java.io.InputStream;</div><div class="line">import java.io.PrintWriter;</div><div class="line">import java.net.ServerSocket;</div><div class="line">import java.net.Socket;</div><div class="line"></div><div class="line">/**</div><div class="line"> * Created by zoujing on 2017/5/10.</div><div class="line"> */</div><div class="line"></div><div class="line">public class MyTomcat &#123;</div><div class="line"></div><div class="line">    public static void main(String[] args) &#123;</div><div class="line">        try &#123;</div><div class="line">            ServerSocket tomcat = new ServerSocket(9090);</div><div class="line">            System.out.println(&quot;服务器启动&quot;);</div><div class="line">            //</div><div class="line">            Socket s = tomcat.accept();</div><div class="line">            //</div><div class="line">            byte[] buf = new byte[1024];</div><div class="line">            InputStream in = s.getInputStream();</div><div class="line">            //</div><div class="line"></div><div class="line">            int length = 0;</div><div class="line">            StringBuffer request = new StringBuffer();</div><div class="line">            while ((length = in.read(buf)) != -1) &#123;</div><div class="line">                String line = new String(buf, 0, length);</div><div class="line">                request.append(line);</div><div class="line">            &#125;</div><div class="line">            //</div><div class="line">            System.out.println(&quot;request:&quot;+request);</div><div class="line"></div><div class="line">            PrintWriter pw = new PrintWriter(s.getOutputStream(),true);</div><div class="line">            pw.println(&quot;&lt;html&gt;&quot;);</div><div class="line">            pw.println(&quot;&lt;head&gt;&quot;);</div><div class="line">            pw.println(&quot;&lt;title&gt;LiveSession List&lt;/title&gt;&quot;);</div><div class="line">            pw.println(&quot;&lt;/head&gt;&quot;);</div><div class="line">            pw.println(&quot;&lt;body&gt;&quot;);</div><div class="line">            pw.println(&quot;&lt;p style=\&quot;font-weight: bold;color: red;\&quot;&gt;welcome to MyTomcat&lt;/p&gt;&quot;);</div><div class="line">            pw.println(&quot;&lt;/body&gt;&quot;);</div><div class="line">            s.close();</div><div class="line">            tomcat.close();</div><div class="line">        &#125; catch (IOException e) &#123;</div><div class="line">            // TODO Auto-generated catch block</div><div class="line">            e.printStackTrace();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="URL"><a href="#URL" class="headerlink" title="URL"></a>URL</h3><p>&#160; &#160; &#160; &#160;响应行和响应标头当中，实际上是负责将相关的一些有用信息返回给我们，但这部分是不需要在浏览器中所展示的。也就是说，我们的浏览器除了应当具备获取一个完整的HTTP响应的能力之外，还应该具备解析HTTP协议响应的能力。事实上，Java也为我们提供了这样的对象，那就是URL及URLConnection对象。</p>
<p>如果我们在我们的浏览器中，植入这样的对象，来进行与服务器之间的HTTP通信，那么：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div></pre></td><td class="code"><pre><div class="line">import java.io.IOException;</div><div class="line">import java.io.InputStream;</div><div class="line">import java.net.HttpURLConnection;</div><div class="line">import java.net.*;</div><div class="line"></div><div class="line">/**</div><div class="line"> * Created by zoujing on 2017/5/10.</div><div class="line"> */</div><div class="line">public class MyBrowser2 &#123;</div><div class="line">    public static void main(String[] args) &#123;</div><div class="line">        try &#123;</div><div class="line">            URL url = new URL(&quot;http://localhost:9090&quot;);</div><div class="line">            HttpURLConnection conn = (HttpURLConnection) url.openConnection();</div><div class="line"></div><div class="line">            InputStream in = conn.getInputStream();</div><div class="line">            byte[] buf = new byte[1024];</div><div class="line">            int length = 0;</div><div class="line">            StringBuffer text = new StringBuffer();</div><div class="line">            String line = null;</div><div class="line">            while ((length = in.read(buf)) != -1) &#123;</div><div class="line">                line = new String(buf, 0, length);</div><div class="line">                text.append(line);</div><div class="line">            &#125;</div><div class="line"></div><div class="line">            System.out.println(text);</div><div class="line"></div><div class="line">        &#125; catch (IOException e) &#123;</div><div class="line">            // TODO Auto-generated catch block</div><div class="line">            e.printStackTrace();</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;这次当我们再运行程序，查看输出信息，发现我们从URLConnection对象获取到的输入流当中，读取的响应信息，就如我们所愿的，只剩下了需要被解析显示在页面的响应实体的内容。实际上这也就是Java为我们提供的对象，将对HTTP协议内容的解析功能进行了封装。而究其根本来说，我们基本可以想象到，URLConnection = Socket + HTTP协议解析器。也就是说，该对象的底层除了通过Socket连接到WEB服务器之外，还封装了对HTTP协议内容的解析功能。</p>
<h3 id="总结-1"><a href="#总结-1" class="headerlink" title="总结"></a>总结</h3><p>&#160; &#160; &#160; &#160;我们先启动服务器，然后运行浏览器模拟网页浏览的过程，首先看到服务器端收到的请求信息，打印出来的信息和上面类似，我们这里就是把请求报文显示出来。</p>
<p>&#160; &#160; &#160; &#160;紧接着，服务器收到请求进行处理后，返回资源给浏览器，于是得到输出信息，在改进后的tomcat服务器，我们封装了html，不过是静态的。如果在搭配html解析，就可以像我们浏览器一样，能先到解析的页面。</p>
<p><img src="http://opb7t58xj.bkt.clouddn.com/%E5%B1%8F%E5%B9%95%E5%BF%AB%E7%85%A7%202017-05-10%20%E4%B8%8B%E5%8D%883.39.20.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;现在我们是单线程单连接，而且没有前段操作界面的实现，我们真正使用的tomcat和浏览器当然在多线程并发、io、网络、人性化操作等方面也做了大量工作进行效率优化和简化操作，美化界面。我们通过例子是去了解基本底层原理。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;web服务器和web浏览器的实现原理&quot;&gt;&lt;a href=&quot;#web服务器和web浏览器的实现原理&quot; class=&quot;headerlink&quot; title=&quot;web服务器和web浏览器的实现原理&quot;&gt;&lt;/a&gt;web服务器和web浏览器的实现原理&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;预警：此篇篇幅很长，可通过菜单导航观看。最近在复习计算机网络的知识，因为想在Java web方面深入了解，所以在思考B／S结构模式的工作是怎样的过程呢，刚好找了好些资料和别人的总结，现在把这些都整理一下。在基础中已经学习了socket和http协议，我们也知道通过浏览器输入一个地址，访问一个网页的操作。实际对应的底层操作简单来说就是：客户端(浏览器)面向于WEB服务器的网络通信。&lt;/p&gt;
    
    </summary>
    
      <category term="Java Web" scheme="http://yoursite.com/categories/Java-Web/"/>
    
    
      <category term="网络编程" scheme="http://yoursite.com/tags/%E7%BD%91%E7%BB%9C%E7%BC%96%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Java Web基础整理（二）</title>
    <link href="http://yoursite.com/2017/03/29/Java%20Web%E5%9F%BA%E7%A1%80%E6%95%B4%E7%90%86%EF%BC%88%E4%BA%8C%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/29/Java Web基础整理（二）/</id>
    <published>2017-03-29T09:20:19.000Z</published>
    <updated>2017-06-20T07:42:11.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Java-Web基础整理（二）"><a href="#Java-Web基础整理（二）" class="headerlink" title="Java Web基础整理（二）"></a>Java Web基础整理（二）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;最近再看Tomcat原理与Java Web这本书，作为准备，了解了JSP内置对象、JavaBean和一些基本概念之后我们在了解一下Servlet和Tomcat的搭配工作过程。还有一个讲Java Web发展历程的博文可以参考：<a href="http://blog.csdn.net/javaeeteacher/article/details/6478450" target="_blank" rel="external">Java Web发展历史</a></p>
 <a id="more"></a>
<h2 id="Servlet"><a href="#Servlet" class="headerlink" title="Servlet"></a>Servlet</h2><p>&#160; &#160; &#160; &#160;Servlet是一个Java应用程序，运行在服务器端，用来处理客户端请求并作出响应的程序。</p>
<p>&#160; &#160; &#160; &#160;Servlet多线程体系结构是建立在Java多线程机制之上的，它的生命周期是由Web容器负责的。</p>
<p> &#160; &#160; &#160; &#160;对于Servlet，它被设计为多线程的（如果它是单线程的，你就可以想象，当1000个人同时请求一个网页时，在第一个人获得请求结果之前，其它999个人都在郁闷地等待），如果为每个用户的每一次请求都创建 一个新的线程对象来运行的话，系统就会在创建线程和销毁线程上耗费很大的开销，大大降低系统的效率。</p>
<p>&#160; &#160; &#160; &#160;因此，Servlet多线程机制背后有一个线程池在支持，线程池在初始化初期就创建了一定数量的线程对象，通过提高对这些对象的利用率，避免高频率地创建对象，从而达到提高程序的效率的目的。（由线程来执行Servlet的service方法，servlet在Tomcat中是以单例模式存在的， Servlet的线程安全问题只有在大量的并发访问时才会显现出来，并且很难发现，因此在编写Servlet程序时要特别注意。线程安全问题主要是由实例变量造成的,因此在Servlet中应避免使用实例变量。如果应用程设计无法避免使用实例变量，那么使用同步来保护要使用的实例变量，但为保证系统的最佳性能，应该同步可用性最小的代码路径）</p>
<p> &#160; &#160; &#160; &#160;<font color="red">解决servlet线程安全的方案：同步对共享数据的操作 Synchronized (this){…}、避免使用实例变量。</font></p>
<h2 id="Tomcat与Servlet搭配工作"><a href="#Tomcat与Servlet搭配工作" class="headerlink" title="Tomcat与Servlet搭配工作"></a>Tomcat与Servlet搭配工作</h2><p> <img src="http://images.cnitblog.com/blog/384192/201302/24114945-4774512d1247438fa58c37399d3999ae.jpg" alt=""></p>
<p>步骤：</p>
<ol>
<li>Web Client 向Servlet容器（Tomcat）发出Http请求</li>
<li>Servlet容器接收Web Client的请求</li>
<li>Servlet容器创建一个HttpRequest对象，将Web Client请求的信息封装到这个对象中。</li>
<li>Servlet容器创建一个HttpResponse对象</li>
<li>Servlet容器调用HttpServlet对象的service方法，把HttpRequest对象与HttpResponse对象作为参数传给 HttpServlet 对象。</li>
<li>HttpServlet调用HttpRequest对象的有关方法，获取Http请求信息。</li>
<li>HttpServlet调用HttpResponse对象的有关方法，生成响应数据。</li>
<li>Servlet容器把HttpServlet的响应结果传给Web Client。</li>
</ol>
<h2 id="Servlet生命周期"><a href="#Servlet生命周期" class="headerlink" title="Servlet生命周期"></a>Servlet生命周期</h2><p>&#160; &#160; &#160; &#160;由Servlet容器决定，即一般是tomcat。如上过程。</p>
<p>&#160; &#160; &#160; &#160;Servlet生命周期分为三个阶段：</p>
<p>　　1，初始化阶段  调用init()方法</p>
<p>　　2，响应客户请求阶段　　调用service()方法</p>
<p>　　3，终止阶段　　调用destroy()方法</p>
<p>&#160; &#160; &#160; &#160;Servlet初始化阶段：</p>
<p>　　在下列时刻Servlet容器装载Servlet：</p>
<p>　　　　1，Servlet容器启动时自动装载某些Servlet，实现它只需要在web.XML文件中的<servlet></servlet>之间添加如下代码：</p>
<pre><code>&lt;loadon-startup&gt;1&lt;/loadon-startup&gt; 
</code></pre><p>　　　　2，在Servlet容器启动后，客户首次向Servlet发送请求</p>
<p>　　　　3，Servlet类文件被更新后，重新装载Servlet</p>
<p>　　Servlet被装载后，Servlet容器创建一个Servlet实例并且调用Servlet的init()方法进行初始化。在Servlet的整个生命周期内，init()方法只被调用一次。</p>
<p><img src="http://images.blogjava.net/blogjava_net/fancydeepin/myself/servlet.png" alt=""></p>
<h2 id="Servlet工作原理："><a href="#Servlet工作原理：" class="headerlink" title="Servlet工作原理："></a>Servlet工作原理：</h2><ol>
<li>首先简单解释一下Servlet接收和响应客户请求的过程，首先客户发送一个请求，Servlet是调用service()方法对请求进行响应的，通过源代码可见，service()方法中对请求的方式进行了匹配，选择调用doGet,doPost等这些方法，然后再进入对应的方法中调用逻辑层的方法，实现对客户的响应。在Servlet接口和GenericServlet中是没有doGet（）、doPost（）等等这些方法的，HttpServlet中定义了这些方法，但是都是返回error信息，所以，我们每次定义一个Servlet的时候，都必须实现doGet或doPost等这些方法。</li>
<li>每一个自定义的Servlet都必须实现Servlet的接口，Servlet接口中定义了五个方法，其中比较重要的三个方法涉及到Servlet的生命周期，分别是上文提到的init(),service(),destroy()方法。GenericServlet是一个通用的，不特定于任何协议的Servlet,它实现了Servlet接口。而HttpServlet继承于GenericServlet，因此HttpServlet也实现了Servlet接口。所以我们定义Servlet的时候只需要继承HttpServlet即可。</li>
<li>Servlet接口和GenericServlet是不特定于任何协议的，而HttpServlet是特定于HTTP协议的类，所以HttpServlet中实现了service()方法，并将请求ServletRequest、ServletResponse 强转为HttpRequest 和 HttpResponse。</li>
</ol>
<h2 id="创建Servlet对象的时机："><a href="#创建Servlet对象的时机：" class="headerlink" title="创建Servlet对象的时机："></a>创建Servlet对象的时机：</h2><ol>
<li>Servlet容器启动时：读取web.xml配置文件中的信息，构造指定的Servlet对象，创建ServletConfig对象，同时将ServletConfig对象作为参数来调用Servlet对象的init方法。</li>
<li>在Servlet容器启动后：客户首次向Servlet发出请求，Servlet容器会判断内存中是否存在指定的Servlet对象，如果没有则创建它，然后根据客户的请求创建HttpRequest、HttpResponse对象，从而调用Servlet 对象的service方法。</li>
<li>Servlet Servlet容器在启动时自动创建Servlet，这是由在web.xml文件中为Servlet设置的<load-on-startup>属性决定的。从中我们也能看到同一个类型的Servlet对象在Servlet容器中以单例的形式存在。</load-on-startup></li>
</ol>
<p>参考博文：<a href="http://www.cnblogs.com/xuekyo/archive/2013/02/24/2924072.html" target="_blank" rel="external">servlet原理</a>。</p>
<h2 id="session和cookies"><a href="#session和cookies" class="headerlink" title="session和cookies"></a>session和cookies</h2><p>&#160; &#160; &#160; &#160;cookie在客户端，session在服务端，某个会话数据会在服务端保存，内存缓存或持久化，对应一个sessionID，返回客户端放入cookie，如果客户端禁用了cookie，那么可以重定向在URL后面加sid=xxx，J sessionID是tomcat对sessionID的称呼，现在通过sessionID和https可以较为安全的工作。</p>
<p>参考博文：<a href="http://blog.csdn.net/fangaoxin/article/details/6952954/" target="_blank" rel="external">Cookie/Session机制详解</a></p>
<h2 id="Servlet与JSP的比较："><a href="#Servlet与JSP的比较：" class="headerlink" title="Servlet与JSP的比较："></a>Servlet与JSP的比较：</h2><p>　　有许多相似之处，都可以生成动态网页。</p>
<p>　　JSP的优点是擅长于网页制作，生成动态页面比较直观，缺点是不容易跟踪与排错。</p>
<p>　　Servlet是纯Java语言，擅长于处理流程和业务逻辑，缺点是生成动态网页不直观。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Java-Web基础整理（二）&quot;&gt;&lt;a href=&quot;#Java-Web基础整理（二）&quot; class=&quot;headerlink&quot; title=&quot;Java Web基础整理（二）&quot;&gt;&lt;/a&gt;Java Web基础整理（二）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;最近再看Tomcat原理与Java Web这本书，作为准备，了解了JSP内置对象、JavaBean和一些基本概念之后我们在了解一下Servlet和Tomcat的搭配工作过程。还有一个讲Java Web发展历程的博文可以参考：&lt;a href=&quot;http://blog.csdn.net/javaeeteacher/article/details/6478450&quot;&gt;Java Web发展历史&lt;/a&gt;&lt;/p&gt;
    
    </summary>
    
      <category term="Java Web" scheme="http://yoursite.com/categories/Java-Web/"/>
    
    
      <category term="Servlet、Tomcat" scheme="http://yoursite.com/tags/Servlet%E3%80%81Tomcat/"/>
    
  </entry>
  
  <entry>
    <title>Java Web基础整理（一）</title>
    <link href="http://yoursite.com/2017/03/26/Java%20Web%E5%9F%BA%E7%A1%80%E6%95%B4%E7%90%86%EF%BC%88%E4%B8%80%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/26/Java Web基础整理（一）/</id>
    <published>2017-03-26T05:45:33.000Z</published>
    <updated>2017-06-20T06:43:21.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Java-Web基础整理（一）"><a href="#Java-Web基础整理（一）" class="headerlink" title="Java Web基础整理（一）"></a>Java Web基础整理（一）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;Java最先的流行就是从Web上的动态页面小程序开始的，万维网的流行，Java Web的发展也很快，从最先的页面，到JSP，添加内置对象，JavaBean出现实现业务逻辑分离，到Servlet的发展。这里回顾一下这些发展历程。</p>
 <a id="more"></a>
<h2 id="JSP内置对象"><a href="#JSP内置对象" class="headerlink" title="JSP内置对象"></a>JSP内置对象</h2><p>&#160; &#160; &#160; &#160;JSP提供了由容器实现和管理的内置对象，首先一个问题就是为什么会出现内置对象呢，他的作用是什么？</p>
<p>&#160; &#160; &#160; &#160;答：JSP使用Java作文脚本程序语言，具有巨大的对象处理能力，动态创建Web页面，而在Java语言中，需要创建实例才能引用，操作繁琐，为了简化，<font color="red">JSP提供了9大内置对象和4个作用域，作用域如下图，对象分别是request、response、session、application、out、page、config、exception、pageContext。</font></p>
<p>&#160; &#160; &#160; &#160;&#160; &#160; &#160; &#160;&#160; &#160; &#160; &#160;<img src="http://img2.tuicool.com/2Ejm63.jpg!web" alt=""></p>
<p>&#160; &#160; &#160; &#160;内置对象的作用可参考博文，文字较多。<a href="http://www.tuicool.com/articles/VNB3AjA" target="_blank" rel="external">九大内置对象作用</a>。</p>
<p>&#160; &#160; &#160; &#160;重要的是request、response分别包装了Http请求和响应信息，session是适用于同一个应用程序中每个客户端的各个页面的共享数据，application是全局共享数据，out用于在JSP中输出各种类型数据。</p>
<p>&#160; &#160; &#160; &#160;这里需要补充的是容器、JSP容器，servlet容器，web容器，web服务器，应用服务器的概念。参考博文：<a href="http://blog.csdn.net/qmw19910301/article/details/53463285" target="_blank" rel="external">Servlet，Servlet容器，Jsp容器，Web容器</a>，<a href="https://www.zhihu.com/question/20096067" target="_blank" rel="external">Web 服务器与应用服务器的区别</a>。</p>
<h2 id="JavaBean"><a href="#JavaBean" class="headerlink" title="JavaBean"></a>JavaBean</h2><p>&#160; &#160; &#160; &#160;JavaBean的产生使JSP页面中的业务逻辑变得更加清晰，程序中的实体对象及业务逻辑可以单独封装到Java类中。不仅提高可读性和易维护性，还提高了可重用性。</p>
<p>&#160; &#160; &#160; &#160;在JSP网页初级阶段开源社区还没有那么活跃，也并没有分层和框架的出现，将大量代码嵌入HTML中效率很低，JavaBean出现和JSP组合开发模式将业务逻辑，如数据库操作通过引用JavaBean组件来完成。</p>
<p>&#160; &#160; &#160; &#160;Spring中也有很多实例的Bean，类似类的实例了，有一些概念，如PO,VO,BO,POJO等，参考博文<a href="http://blog.chinaunix.net/uid-20556037-id-2810927.html" target="_blank" rel="external">PO、VO、BO、DTO、POJO、DAO之间的关系 </a>。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Java-Web基础整理（一）&quot;&gt;&lt;a href=&quot;#Java-Web基础整理（一）&quot; class=&quot;headerlink&quot; title=&quot;Java Web基础整理（一）&quot;&gt;&lt;/a&gt;Java Web基础整理（一）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;Java最先的流行就是从Web上的动态页面小程序开始的，万维网的流行，Java Web的发展也很快，从最先的页面，到JSP，添加内置对象，JavaBean出现实现业务逻辑分离，到Servlet的发展。这里回顾一下这些发展历程。&lt;/p&gt;
    
    </summary>
    
      <category term="Java Web" scheme="http://yoursite.com/categories/Java-Web/"/>
    
    
      <category term="JSP、JavaBean" scheme="http://yoursite.com/tags/JSP%E3%80%81JavaBean/"/>
    
  </entry>
  
  <entry>
    <title>高效并发（五）</title>
    <link href="http://yoursite.com/2017/03/24/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91%EF%BC%88%E4%BA%94%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/24/高效并发（五）/</id>
    <published>2017-03-24T05:22:25.000Z</published>
    <updated>2017-05-25T01:08:45.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="concurrent包总结（三）"><a href="#concurrent包总结（三）" class="headerlink" title="concurrent包总结（三）"></a>concurrent包总结（三）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;上篇已经分析了线程池和同步工具。这里我们来介绍原子类和forkjoin框架。</p>
 <a id="more"></a>
<h2 id="forkjoin框架"><a href="#forkjoin框架" class="headerlink" title="forkjoin框架"></a>forkjoin框架</h2><p>&#160; &#160; &#160; &#160;Fork/Join框架是Java7提供了的一个用于并行执行任务的框架， 是一个把大任务分割成若干个小任务，最终汇总每个小任务结果后得到大任务结果的框架。</p>
<p>&#160; &#160; &#160; &#160;我们再通过Fork和Join这两个单词来理解下Fork/Join框架，Fork就是把一个大任务切分为若干子任务并行的执行，Join就是合并这些子任务的执行结果，最后得到这个大任务的结果。比如计算1+2+。。＋10000，可以分割成10个子任务，每个子任务分别对1000个数进行求和，最终汇总这10个子任务的结果。Fork/Join的运行流程图如下：</p>
<p><img src="http://cdn3.infoqstatic.com/statics_s2_20170523-0350/resource/articles/fork-join-introduction/zh/resources/21.png" alt=""></p>
<h3 id="工作窃取算法"><a href="#工作窃取算法" class="headerlink" title="工作窃取算法"></a>工作窃取算法</h3><p>&#160; &#160; &#160; &#160; 工作窃取（work-stealing）算法是指某个线程从其他队列里窃取任务来执行。工作窃取的运行流程图如下：</p>
<p><img src="http://cdn3.infoqstatic.com/statics_s2_20170523-0350/resource/articles/fork-join-introduction/zh/resources/image3.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;工作窃取算法的优点是充分利用线程进行并行计算，并减少了线程间的竞争，其缺点是在某些情况下还是存在竞争，比如双端队列里只有一个任务时。并且消耗了更多的系统资源，比如创建多个线程和多个双端队列。</p>
<h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p>&#160; &#160; &#160; &#160;第一步分割任务。首先我们需要有一个fork类来把大任务分割成子任务，有可能子任务还是很大，所以还需要不停的分割，直到分割出的子任务足够小。</p>
<p>&#160; &#160; &#160; &#160;第二步执行任务并合并结果。分割的子任务分别放在双端队列里，然后几个启动线程分别从双端队列里获取任务执行。子任务执行完的结果都统一放在一个队列里，启动一个线程从队列里拿数据，然后合并这些数据。</p>
<p>Fork/Join使用两个类来完成以上两件事情：</p>
<p>&#160; &#160; &#160; &#160;ForkJoinTask：我们要使用ForkJoin框架，必须首先创建一个ForkJoin任务。它提供在任务中执行fork()和join()操作的机制，通常情况下我们不需要直接继承ForkJoinTask类，而只需要继承它的子类，Fork/Join框架提供了以下两个子类：</p>
<ol>
<li>RecursiveAction：用于没有返回结果的任务。</li>
<li>RecursiveTask ：用于有返回结果的任务。</li>
</ol>
<p>&#160; &#160; &#160; &#160;ForkJoinPool ：ForkJoinTask需要通过ForkJoinPool来执行，任务分割出的子任务会添加到当前工作线程所维护的双端队列中，进入队列的头部。当一个工作线程的队列里暂时没有任务时，它会随机从其他工作线程的队列的尾部获取一个任务。</p>
<p>&#160; &#160; &#160; &#160;使用例子如下,需求是：计算1+2+3+4的结果。：</p>
<p><img src="http://cdn3.infoqstatic.com/statics_s2_20170523-0350/resource/articles/fork-join-introduction/zh/resources/31.png" alt=""></p>
<h2 id="原子类"><a href="#原子类" class="headerlink" title="原子类"></a>原子类</h2><p>&#160; &#160; &#160; &#160;源代码中没有volatile或者synchronized这些同步机制也没有锁，其实它的同步是通过硬件指令实现。Integer内部方法不是线程安全的所以并发编程推荐使用AtomicInteger类来替换包装类，但是原子类不是 java.lang.Integer 和相关类的通用替换方法，它们不定义诸如 hashCode 和 compareTo 之类的方法。</p>
<p>&#160; &#160; &#160; &#160;Java提供的原子类是靠 sun 基于 CAS 实现的，CAS 是一种乐观锁。</p>
<p>&#160; &#160; &#160; &#160;原子变量类相当于一种泛化的 volatile 变量，能够支持原子的和有条件的读-改-写操作。AtomicInteger 表示一个int类型的值，并提供了 get 和 set 方法，这些 Volatile 类型的int变量在读取和写入上有着相同的内存语义。它还提供了一个原子的 compareAndSet 方法（如果该方法成功执行，那么将实现与读取/写入一个 volatile 变量相同的内存效果），以及原子的添加、递增和递减等方法。AtomicInteger 表面上非常像一个扩展的 Counter 类，但在发生竞争的情况下能提供更高的可伸缩性，因为它直接利用了硬件对并发的支持。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;concurrent包总结（三）&quot;&gt;&lt;a href=&quot;#concurrent包总结（三）&quot; class=&quot;headerlink&quot; title=&quot;concurrent包总结（三）&quot;&gt;&lt;/a&gt;concurrent包总结（三）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;上篇已经分析了线程池和同步工具。这里我们来介绍原子类和forkjoin框架。&lt;/p&gt;
    
    </summary>
    
      <category term="高效并发" scheme="http://yoursite.com/categories/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91/"/>
    
    
      <category term="concurrent包总结" scheme="http://yoursite.com/tags/concurrent%E5%8C%85%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>高效并发（四）</title>
    <link href="http://yoursite.com/2017/03/21/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91%EF%BC%88%E5%9B%9B%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/21/高效并发（四）/</id>
    <published>2017-03-21T05:22:10.000Z</published>
    <updated>2017-05-25T00:30:52.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="concurrent包总结（二）"><a href="#concurrent包总结（二）" class="headerlink" title="concurrent包总结（二）"></a>concurrent包总结（二）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;上篇已经分析了lock和concurrent提供的集合类包括阻塞队列和容器类。这里我们来介绍</p>
 <a id="more"></a>
<h2 id="Executor框架"><a href="#Executor框架" class="headerlink" title="Executor框架"></a>Executor框架</h2><h3 id="线程池的作用"><a href="#线程池的作用" class="headerlink" title="线程池的作用"></a>线程池的作用</h3><p>&#160; &#160; &#160; &#160;线程池作用就是限制系统中执行线程的数量。根据系统的环境情况，可以自动或手动设置线程数量，达到运行的最佳效果；少了浪费了系统资源，多了造成系统拥挤效率不高。用线程池控制线程数量，其他线程 排队等候。一个任务执行完毕，再从队列的中取最前面的任务开始执行。若队列中没有等待进程，线程池的这一资源处于等待。当一个新任务需要运行时，如果线程 池中有等待的工作线程，就可以开始运行了；否则进入等待队列。</p>
<h3 id="为什么要用线程池"><a href="#为什么要用线程池" class="headerlink" title="为什么要用线程池"></a>为什么要用线程池</h3><ol>
<li>减少了创建和销毁线程的次数，每个工作线程都可以被重复利用，可执行多个任务</li>
<li>可以根据系统的承受能力，调整线程池中工作线线程的数目，防止因为因为消耗过多的内存，而把服务器累趴下(每个线程需要大约1MB内存，线程开的越多，消耗的内存也就越大，最后死机)</li>
</ol>
<h3 id="Executor"><a href="#Executor" class="headerlink" title="Executor"></a>Executor</h3><p>&#160; &#160; &#160; &#160;Java里面线程池的顶级接口是Executor，里面就有个execute方法，但是严格意义上讲Executor并不是一个线程池，而只是一个执行线程的工具。真正的线程池接口是ExecutorService。ThreadPoolExecutor是Executors类的底层实现。</p>
<h3 id="ExecutorService"><a href="#ExecutorService" class="headerlink" title="ExecutorService"></a>ExecutorService</h3><h4 id="shutDown"><a href="#shutDown" class="headerlink" title="shutDown()"></a>shutDown()</h4><p>&#160; &#160; &#160; &#160;当线程池调用该方法时,线程池的状态则立刻变成SHUTDOWN状态。此时，则不能再往线程池中添加任何任务，否则将会抛出RejectedExecutionException异常。但是，此时线程池不会立刻退出，直到添加到线程池中的任务都已经处理完成，才会退出。</p>
<h4 id="shutdownNow"><a href="#shutdownNow" class="headerlink" title="shutdownNow()"></a>shutdownNow()</h4><p>&#160; &#160; &#160; &#160;根据JDK文档描述，大致意思是：执行该方法，线程池的状态立刻变成STOP状态，并试图停止所有正在执行的线程，不再处理还在池队列中等待的任务，当然，它会返回那些未执行的任务。 </p>
<p>&#160; &#160; &#160; &#160;它试图终止线程的方法是通过调用Thread.interrupt()方法来实现的，但是大家知道，这种方法的作用有限，如果线程中没有sleep 、wait、Condition、定时锁等应用, interrupt()方法是无法中断当前的线程的。所以，ShutdownNow()并不代表线程池就一定立即就能退出，它可能必须要等待所有正在执行的任务都执行完成了才能退出。</p>
<h4 id="其他方法"><a href="#其他方法" class="headerlink" title="其他方法"></a>其他方法</h4><p>&#160; &#160; &#160; &#160;包括shubmit，invoke相关和Terminate相关。我们在实现类中在继续分析。</p>
<h3 id="ThreadPoolExecutor"><a href="#ThreadPoolExecutor" class="headerlink" title="ThreadPoolExecutor"></a>ThreadPoolExecutor</h3><h4 id="final-AtomicInteger-ctl"><a href="#final-AtomicInteger-ctl" class="headerlink" title="final AtomicInteger ctl"></a>final AtomicInteger ctl</h4><p>&#160; &#160; &#160; &#160;这个变量是整个类的核心，AtomicInteger保证了对这个变量的操作是原子的，通过巧妙的操作，ThreadPoolExecutor用这一个变量保存了两个内容：</p>
<ol>
<li>所有有效线程的数量</li>
<li>各个线程的状态（runState）</li>
</ol>
<p>低29位存线程数，高3位存runState,这样runState有5个值，解释如下：</p>
<pre><code>//线程池正常运行，可以接受新的任务并处理队列中的任务；
private static final int RUNNING    = -1 &lt;&lt; COUNT_BITS;
//不再接受新的任务，但是会执行队列中的任务；
private static final int SHUTDOWN   =  0 &lt;&lt; COUNT_BITS;
//不再接受新任务，不处理队列中的任务
private static final int STOP       =  1 &lt;&lt; COUNT_BITS;
private static final int TIDYING    =  2 &lt;&lt; COUNT_BITS;
private static final int TERMINATED =  3 &lt;&lt; COUNT_BITS;

// Packing and unpacking ctl
private static int runStateOf(int c)     { return c &amp; ~CAPACITY; }
private static int workerCountOf(int c)  { return c &amp; CAPACITY; }
private static int ctlOf(int rs, int wc) { return rs | wc; }
</code></pre><h4 id="内部封装了work类"><a href="#内部封装了work类" class="headerlink" title="内部封装了work类"></a>内部封装了work类</h4><p>&#160; &#160; &#160; &#160;内部类Worker是对任务的封装，所有submit的Runnable都被封装成了Worker，它本身也是一个Runnable， 然后利用AQS框架（关于AQS可以看我这篇文章）实现了一个简单的非重入的互斥锁， 实现互斥锁主要目的是为了中断的时候判断线程是在空闲还是运行。</p>
<h3 id="Executors"><a href="#Executors" class="headerlink" title="Executors"></a>Executors</h3><p>&#160; &#160; &#160; &#160;有4个重要实现类：</p>
<ol>
<li>newCachedThreadPool创建一个可缓存线程池，如果线程池长度超过处理需要，可灵活回收空闲线程，若无可回收，则新建线程。</li>
<li>newFixedThreadPool 创建一个定长线程池，可控制线程最大并发数，超出的线程会在队列中等待。</li>
<li>newScheduledThreadPool 创建一个定长线程池，支持定时及周期性任务执行。</li>
<li>newSingleThreadExecutor 创建一个单线程化的线程池，它只会用唯一的工作线程来执行任务，保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。</li>
</ol>
<h2 id="同步工具类"><a href="#同步工具类" class="headerlink" title="同步工具类"></a>同步工具类</h2><p>&#160; &#160; &#160; &#160;还有号称四大金刚的同步器。同步工具类可以使任何一种对象，只要该对象可以根据自身的状态来协调控制线程的控制流。阻塞队列可以作为同步工具类，其他类型的同步工具类还包括：信号量（Semaphore）、栅栏（Barrier）以及闭锁（Latch）和交换（exchanger）参考博文：<a href="http://blog.csdn.net/zq602316498/article/details/41779431" target="_blank" rel="external">同步工具类解析</a></p>
<h3 id="CountDownLatch闭锁"><a href="#CountDownLatch闭锁" class="headerlink" title="CountDownLatch闭锁"></a>CountDownLatch闭锁</h3><p>首先我们来介绍闭锁。</p>
<p>&#160; &#160; &#160; &#160;闭锁作用相当于一扇门：在闭锁到达某一状态之前，这扇门一直是关闭的，所有的线程都会在这扇门前等待（阻塞）。只有门打开后，所有的线程才会同时继续运行。<br>闭锁可以用来确保某些活动直到其它活动都完成后才继续执行，例如：</p>
<ol>
<li>确保某个计算在其所有资源都被初始化之后才继续执行。二元闭锁（只有两个状态）可以用来表示“资源R已经被初始化”，而所有需要R操作都必须先在这个闭锁上等待。</li>
<li>确保某个服务在所有其他服务都已经启动之后才启动。这时就需要多个闭锁。让S在每个闭锁上等待，只有所有的闭锁都打开后才会继续运行。<br>3。 等待直到某个操作的参与者（例如，多玩家游戏中的玩家）都就绪再继续执行。在这种情况下，当所有玩家都准备就绪时，闭锁将到达结束状态。</li>
</ol>
<p>&#160; &#160; &#160; &#160;CountDownLatch 是一种灵活的闭锁实现，可以用在上述各种情况中使用。闭锁状态包含一个计数器，初始化为一个正数，表示要等待的事件数量。countDown() 方法会递减计数器，表示等待的事件中发生了一件。await() 方法则阻塞，直到计数器值变为0。</p>
<p>&#160; &#160; &#160; &#160;下面，我们使用闭锁来实现在主线程中计算多个子线程运行时间的功能。具体逻辑是使用两个闭锁，“起始门”用来控制子线程同时运行，“结束门”用来标识子线程是否都结束。</p>
<h3 id="CyclicBarrier和exchanger"><a href="#CyclicBarrier和exchanger" class="headerlink" title="CyclicBarrier和exchanger"></a>CyclicBarrier和exchanger</h3><p>&#160; &#160; &#160; &#160;栅栏（Bariier）类似于闭锁，它能阻塞一组线程知道某个事件发生。栅栏与闭锁的关键区别在于，所有的线程必须同时到达栅栏位置，才能继续执行。闭锁用于等待等待时间，而栅栏用于等待线程。</p>
<p>&#160; &#160; &#160; &#160;CyclicBarrier 可以使一定数量的参与方反复的在栅栏位置汇聚，它在并行迭代算法中非常有用：将一个问题拆成一系列相互独立的子问题。当线程到达栅栏位置时，调用await() 方法，这个方法是阻塞方法，直到所有线程到达了栅栏位置，那么栅栏被打开，此时所有线程被释放，而栅栏将被重置以便下次使用。</p>
<p>&#160; &#160; &#160; &#160;另一种形式的栅栏是Exchanger，它是一种两方（Two-Party）栅栏，各方在栅栏位置上交换数据。例如当一个线程想缓冲区写入数据，而另一个线程从缓冲区中读取数据。这些线程可以使用 Exchanger 来汇合，并将慢的缓冲区与空的缓冲区交换。当两个线程通过 Exchanger 交换对象时，这种交换就把这两个对象安全的发布给另一方。</p>
<p>&#160; &#160; &#160; &#160;Exchanger 可能被视为 SynchronousQueue 的双向形式。我们也可以用两个SynchronousQueue来实现 Exchanger的功能。</p>
<h3 id="信号量Semaphone"><a href="#信号量Semaphone" class="headerlink" title="信号量Semaphone"></a>信号量Semaphone</h3><p>&#160; &#160; &#160; &#160;之前讲的闭锁控制访问的时间，而信号量则用来控制访问某个特定资源的操作数量，控制空间。而且闭锁只能够减少，一次性使用，而信号量则申请可释放，可增可减。 计数信号量还可以用来实现某种资源池，或者对容器施加边界。</p>
<p>&#160; &#160; &#160; &#160;Semaphone 管理这一组许可（permit），可通过构造函数指定。同时提供了阻塞方法acquire，用来获取许可。同时提供了release方法表示释放一个许可。</p>
<p>&#160; &#160; &#160; &#160;Semaphone 可以将任何一种容器变为有界阻塞容器，如用于实现资源池。例如数据库连接池。我们可以构造一个固定长度的连接池，使用阻塞方法 acquire和release获取释放连接，而不是获取不到便失败。（当然，一开始设计时就使用BlockingQueue来保存连接池的资源是一种更简单的方法）。</p>
<h3 id="FutureTask"><a href="#FutureTask" class="headerlink" title="FutureTask"></a>FutureTask</h3><p>&#160; &#160; &#160; &#160;FutureTask也可以用作闭锁。它表示一种抽象的可生成结果的计算。是通过 Callable 来实现的，相当于一种可生成结果的 Runnable ，并且可处于以下三种状态：等待运行，正在运行，运行完成。当FutureTask进入完成状态后，它会停留在这个状态上。</p>
<p>&#160; &#160; &#160; &#160;Future.get 用来获取计算结果，如果FutureTask还未运行完成，则会阻塞。FutureTask 将计算结果从执行计算的线程传递到获取这个结果的线程，而FutureTask 的规范确保了这种传递过程能实现结果的安全发布。</p>
<p>&#160; &#160; &#160; &#160;FutureTask在Executor框架中表示异步任务，还可以用来表示一些时间较长的计算，这些计算可以在使用计算结果之前启动。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;concurrent包总结（二）&quot;&gt;&lt;a href=&quot;#concurrent包总结（二）&quot; class=&quot;headerlink&quot; title=&quot;concurrent包总结（二）&quot;&gt;&lt;/a&gt;concurrent包总结（二）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;上篇已经分析了lock和concurrent提供的集合类包括阻塞队列和容器类。这里我们来介绍&lt;/p&gt;
    
    </summary>
    
      <category term="高效并发" scheme="http://yoursite.com/categories/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91/"/>
    
    
      <category term="concurrent包总结" scheme="http://yoursite.com/tags/concurrent%E5%8C%85%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>高效并发（三）</title>
    <link href="http://yoursite.com/2017/03/18/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91%EF%BC%88%E4%B8%89%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/18/高效并发（三）/</id>
    <published>2017-03-18T01:16:03.000Z</published>
    <updated>2017-05-24T00:21:07.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="concurrent包总结（一）"><a href="#concurrent包总结（一）" class="headerlink" title="concurrent包总结（一）"></a>concurrent包总结（一）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;在前面的基础知识之上，我们开始对concurrent包的全面分析。</p>
 <a id="more"></a>
<h2 id="线程安全集合类"><a href="#线程安全集合类" class="headerlink" title="线程安全集合类"></a>线程安全集合类</h2><p>&#160; &#160; &#160; &#160;可参考博文：<a href="http://www.importnew.com/24594.html" target="_blank" rel="external">并发集合总结</a>。</p>
<h3 id="Concurrent-Colleciton相关"><a href="#Concurrent-Colleciton相关" class="headerlink" title="Concurrent Colleciton相关"></a>Concurrent Colleciton相关</h3><h4 id="ConcurrentHashMap"><a href="#ConcurrentHashMap" class="headerlink" title="ConcurrentHashMap"></a>ConcurrentHashMap</h4><p>&#160; &#160; &#160; &#160;采用了锁分离技术，它使用了多个锁代替HashTable中的单个锁，其中get方法并没有加锁。它把区间按照并发级别(concurrentLevel)，分成了若干个segment。默认情况下内部按并发级别为16来创建。对于每个segment的容量，默认情况也是16。当然并发级别(concurrentLevel)和每个段(segment)的初始容量都是可以通过构造函数设定的。</p>
<p>参考博文：<a href="http://www.importnew.com/21781.html" target="_blank" rel="external">concurrenthashmap详解</a></p>
<p><img src="http://images.cnitblog.com/blog/587773/201409/062059451104363.jpg" alt=""></p>
<p>&#160; &#160; &#160; &#160;看起来只是把以前HashTable的一个hash bucket创建了16份而已。那还有其他特殊的吗？其中Segment继承了ReentrantLock，表明每个segment都可以当做一个锁。我们就从put，get，remove方法重点去了解。</p>
<p>&#160; &#160; &#160; &#160;get方法：count变量表示segment中存在entry的个数,hashentry数据结构，还有get方法没有加同步监视器。我们来看下如下代码：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div></pre></td><td class="code"><pre><div class="line">V get(Object key, int hash) &#123;</div><div class="line">        if (count != 0) &#123; // read-volatile // ①</div><div class="line">            HashEntry&lt;K,V&gt; e = getFirst(hash); </div><div class="line">            while (e != null) &#123;</div><div class="line">                if (e.hash == hash &amp;&amp; key.equals(e.key)) &#123;</div><div class="line">                    V v = e.value;</div><div class="line">                    if (v != null)  // ② 注意这里，结合双层检测锁DCL</div><div class="line">                        return v;</div><div class="line">                    return readValueUnderLock(e); // recheck</div><div class="line">                &#125;</div><div class="line">                e = e.next;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        return null;</div><div class="line">&#125;</div><div class="line"></div><div class="line"></div><div class="line">transient volatile int count;</div><div class="line"></div><div class="line"></div><div class="line">static final class HashEntry&lt;K,V&gt; &#123;</div><div class="line">    final K key;</div><div class="line">    final int hash;</div><div class="line">    volatile V value;</div><div class="line">    final HashEntry&lt;K,V&gt; next;</div><div class="line">    。。。</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;count和value使用了volatile来修改。我们前文说过，Java5之后，JMM实现了对volatile的保证：对volatile域的写入操作happens-before于每一个后续对同一个域的读写操作。所以，每次判断count变量的时候，即使恰好其他线程改变了segment也会体现出来。</p>
<h4 id="CopyOnWriteArrayList"><a href="#CopyOnWriteArrayList" class="headerlink" title="CopyOnWriteArrayList"></a>CopyOnWriteArrayList</h4><p>&#160; &#160; &#160; &#160;CopyOnWrite容器即写时复制的容器。通俗的理解是当我们往一个容器添加元素的时候，不直接往当前容器添加，而是先将当前容器进行Copy，复制出一个新的容器，然后新的容器里添加元素，添加完元素之后，再将原容器的引用指向新的容器。这样做的好处是我们可以对CopyOnWrite容器进行并发的读，而不需要加锁，因为当前容器不会添加任何元素。所以CopyOnWrite容器也是一种读写分离的思想，读和写不同的容器。</p>
<p>我们要注意以下两点：</p>
<ol>
<li><p>减少扩容开销。根据实际需要，初始化CopyOnWriteMap的大小，避免写时CopyOnWriteMap扩容的开销。</p>
</li>
<li><p>使用批量添加。因为每次添加，容器每次都会进行复制，所以减少添加次数，可以减少容器的复制次数。如使用上面代码里的addBlackList方法。</p>
</li>
</ol>
<p>缺点：</p>
<p>&#160; &#160; &#160; &#160;一、内存占用问题。因为CopyOnWrite的写时复制机制，所以在进行写操作的时候，内存里会同时驻扎两个对象的内存，旧的对象和新写入的对象（注意:在复制的时候只是复制容器里的引用，只是在写的时候会创建新对象添加到新容器里，而旧容器的对象还在使用，所以有两份对象内存）。如果这些对象占用的内存比较大，比如说200M左右，那么再写入100M数据进去，内存就会占用300M，那么这个时候很有可能造成频繁的Yong GC和Full GC。之前我们系统中使用了一个服务由于每晚使用CopyOnWrite机制更新大对象，造成了每晚15秒的Full GC，应用响应时间也随之变长。</p>
<p>&#160; &#160; &#160; &#160;针对内存占用问题，可以通过压缩容器中的元素的方法来减少大对象的内存消耗，比如，如果元素全是10进制的数字，可以考虑把它压缩成36进制或64进制。或者不使用CopyOnWrite容器，而使用其他的并发容器，如ConcurrentHashMap。</p>
<p>&#160; &#160; &#160; &#160;二、数据一致性问题。CopyOnWrite容器只能保证数据的最终一致性，不能保证数据的实时一致性。所以如果你希望写入的的数据，马上能读到，请不要使用CopyOnWrite容器。</p>
<p>参考博文：<a href="http://www.importnew.com/22470.html" target="_blank" rel="external">copyonwriteArrayList解析</a></p>
<h4 id="ConcurrentSkipListmap"><a href="#ConcurrentSkipListmap" class="headerlink" title="ConcurrentSkipListmap"></a>ConcurrentSkipListmap</h4><p>&#160; &#160; &#160; &#160;跳跃表其实也是一种通过“空间来换取时间”的一个算法，通过在每个节点中增加了向前的指针，从而提升查找的效率。跳跃表使用概率均衡技术而不是使用强制性均衡技术，因此，对于插入和删除结点比传统上的平衡树算法更为简洁高效。 跳表是一种随机化的数据结构，目前开源软件 Redis 和 LevelDB 都有用到它。</p>
<p><img src="http://img.blog.csdn.net/20160820163336489?watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQv/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/Center" alt=""></p>
<p>参考博文：<a href="http://blog.csdn.net/sunxianghuang/article/details/52221913" target="_blank" rel="external">concurrentskipListmap解析</a></p>
<h3 id="Queue接口相关"><a href="#Queue接口相关" class="headerlink" title="Queue接口相关"></a>Queue接口相关</h3><h4 id="ConcurrentLinkedQueue"><a href="#ConcurrentLinkedQueue" class="headerlink" title="ConcurrentLinkedQueue"></a>ConcurrentLinkedQueue</h4><p>&#160; &#160; &#160; &#160;在并发编程中我们有时候需要使用线程安全的队列。如果我们要实现一个线程安全的队列有两种实现方式一种是使用阻塞算法，另一种是使用非阻塞算法。使用阻塞算法的队列可以用一个锁（入队和出队用同一把锁）或两个锁（入队和出队用不同的锁）等方式来实现，而非阻塞的实现方式则可以使用循环CAS的方式来实现 。</p>
<p>&#160; &#160; &#160; &#160;ConcurrentLinkedQueue是一个基于链接节点的无界线程安全队列，它采用先进先出的规则对节点进行排序，当我们添加一个元素的时候，它会添加到队列的尾部，当我们获取一个元素时，它会返回队列头部的元素。它采用了“wait－free”算法来实现。参考博文：<a href="http://ifeve.com/concurrentlinkedqueue/" target="_blank" rel="external">ConcurrentLinkedQueue解析</a>。</p>
<p>&#160; &#160; &#160; &#160;ConcurrentLinkedQueue由head节点和tair节点组成，每个节点（Node）由节点元素（item）和指向下一个节点的引用(next)组成，节点与节点之间就是通过这个next关联起来，从而组成一张链表结构的队列。默认情况下head节点存储的元素为空，tair节点等于head节点。</p>
<pre><code>private transient volatile Node&lt;e&gt; tail = head;
</code></pre><p><img src="http://ifeve.com/wp-content/uploads/2013/01/ConcurrentLinekedQueue队列入队结构变化图.jpg" alt=""></p>
<p>&#160; &#160; &#160; &#160;我们主要来看入队和出队操作，如上图是入队操作，发现入队主要做两件事情，第一是将入队节点设置成当前队列尾节点的下一个节点。第二是更新tail节点，如果tail节点的next节点不为空，则将入队节点设置成tail节点，如果tail节点的next节点为空，则将入队节点设置成tail的next节点，所以tail节点不总是尾节点。这时单线程的方式，而多线程是采用CAS方式来实现：</p>
<h4 id="BlockingQueue"><a href="#BlockingQueue" class="headerlink" title="BlockingQueue"></a>BlockingQueue</h4><p>&#160; &#160; &#160; &#160;参考博文：<a href="http://blog.csdn.net/suifeng3051/article/details/48807423" target="_blank" rel="external">BlockingQueue解析</a>。BlockingQueue即阻塞队列，从阻塞这个词可以看出，在某些情况下对阻塞队列的访问可能会造成阻塞。被阻塞的情况主要有如下两种：</p>
<ol>
<li>当队列满了的时候进行入队列操作</li>
<li>当队列空了的时候进行出队列操作</li>
</ol>
<p>阻塞队列主要用在生产者/消费者的场景:</p>
<p><img src="http://img.blog.csdn.net/20150929153140497" alt=""></p>
<p>&#160; &#160; &#160; &#160;负责生产的线程不断的制造新对象并插入到阻塞队列中，直到达到这个队列的上限值。队列达到上限值之后生产线程将会被阻塞，直到消费的线程对这个队列进行消费。同理，负责消费的线程不断的从队列中消费对象，直到这个队列为空，当队列为空时，消费线程将会被阻塞，除非队列中有新的对象被插入。</p>
<p>在Java6中，BlockingQueue的实现类主要有以下几种：</p>
<ol>
<li>ArrayBlockingQueue</li>
<li>DelayQueue</li>
<li>LinkedBlockingQueue</li>
<li>PriorityBlockingQueue</li>
<li>SynchronousQueue</li>
</ol>
<h5 id="ArrayBlockingQueue"><a href="#ArrayBlockingQueue" class="headerlink" title="ArrayBlockingQueue"></a>ArrayBlockingQueue</h5><p>&#160; &#160; &#160; &#160; ArrayBlockingQueue:一个有边界的阻塞队列，它的内部实现是一个数组。有边界的意思是它的容量是有限的，我们必须在其初始化的时候指定它的容量大小，容量大小一旦指定就不可改变。ArrayBlockingQueue是以先进先出的方式存储数据，最新插入的对象是尾部，最新移出的对象是头部。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">BlockingQueue queue = new ArrayBlockingQueue(1024);</div><div class="line">queue.put(&quot;1&quot;);</div><div class="line">Object object = queue.take();</div></pre></td></tr></table></figure>
<h5 id="DelayQueue"><a href="#DelayQueue" class="headerlink" title="DelayQueue"></a>DelayQueue</h5><p>&#160; &#160; &#160; &#160;DelayQueue阻塞的是其内部元素，DelayQueue中的元素必须实现 java.util.concurrent.Delayed接口，这个接口的定义非常简单：</p>
<pre><code>public interface Delayed extends Comparable&lt;Delayed&gt; {
long getDelay(TimeUnit unit);
}
</code></pre><p>&#160; &#160; &#160; &#160;getDelay()方法的返回值就是队列元素被释放前的保持时间，如果返回0或者一个负值，就意味着该元素已经到期需要被释放，此时DelayedQueue会通过其take()方法释放此对象。从上面Delayed 接口定义可以看到，它还继承了Comparable接口，这是因为DelayedQueue中的元素需要进行排序，一般情况，我们都是按元素过期时间的优先级进行排序。其实DelayQueue应用场景很多，比如定时关闭连接、缓存对象，超时处理等各种场景。</p>
<h5 id="LinkedBlockingQueue"><a href="#LinkedBlockingQueue" class="headerlink" title="LinkedBlockingQueue"></a>LinkedBlockingQueue</h5><p>LinkedBlockingQueue阻塞队列大小的配置是可选的，如果我们初始化时指定一个大小，它就是有边界的，如果不指定，它就是无边界的。说是无边界，其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。和ArrayBlockingQueue一样，LinkedBlockingQueue 也是以先进先出的方式存储数据，最新插入的对象是尾部，最新移出的对象是头部。下面是一个初始化和使用LinkedBlockingQueue的例子：</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">BlockingQueue&lt;String&gt; unbounded = new LinkedBlockingQueue&lt;String&gt;();</div><div class="line">BlockingQueue&lt;String&gt; bounded   = new LinkedBlockingQueue&lt;String&gt;(1024);</div><div class="line">bounded.put(&quot;Value&quot;);</div><div class="line">String value = bounded.take();</div></pre></td></tr></table></figure>
<h5 id="PriorityBlockingQueue"><a href="#PriorityBlockingQueue" class="headerlink" title="PriorityBlockingQueue"></a>PriorityBlockingQueue</h5><p>&#160; &#160; &#160; &#160;一个没有边界的队列，它的排序规则和 java.util.PriorityQueue一样。需要注意，PriorityBlockingQueue中允许插入null对象。所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口，队列优先级的排序规则就是按照我们对这个接口的实现来定义的。另外，我们可以从PriorityBlockingQueue获得一个迭代器Iterator，但这个迭代器并不保证按照优先级顺序进行迭代。</p>
<h5 id="SynchronousQueue"><a href="#SynchronousQueue" class="headerlink" title="SynchronousQueue"></a>SynchronousQueue</h5><p>&#160; &#160; &#160; &#160;队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞，除非这个元素被另一个线程消费。</p>
<h2 id="lock锁"><a href="#lock锁" class="headerlink" title="lock锁"></a>lock锁</h2><p>&#160; &#160; &#160; &#160;这里首先我们需要总结的是我们应该聪明的使用锁，在jvm的学习中我们已经知道了一些锁优化机制，一些锁的类型，那就是我们在使用的时候应该注意些什么。</p>
<p>这里有一些博文可以参考：<a href="https://www.ibm.com/developerworks/cn/java/j-lo-lock/" target="_blank" rel="external">如何聪明地使用锁</a>。竞争锁是造成多线程应用程序性能瓶颈的主要原因，在保证程序正确性的前提下，解决同步带来的性能损失的第一步不是去除锁，而是降低锁的竞争。通常，有以下三类方法可以降低锁的竞争：减少持有锁的时间，降低请求锁的频率，或者用其他协调机制取代独占锁。</p>
<h3 id="ReentranLock"><a href="#ReentranLock" class="headerlink" title="ReentranLock"></a>ReentranLock</h3><p>&#160; &#160; &#160; &#160;ReentrantLock是可重入锁，什么是可重入锁呢？可重入锁就是当前持有该锁的线程能够多次获取该锁，无需等待。可重入锁是如何实现的呢？这要从ReentrantLock的一个内部类Sync的父类说起，Sync的父类是AbstractQueuedSynchronizer（后面简称AQS）。Sync被设计成为安全的外部不可访问的内部类。参考博文：<a href="http://www.importnew.com/24006.html" target="_blank" rel="external">ReentranLock解析</a>，<a href="http://www.codeceo.com/article/reentrantlock-learn.html" target="_blank" rel="external">ReentranLock解析2</a>.</p>
<p><img src="http://static.codeceo.com/images/2016/08/reentrantlock01.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;这里我们首先来说一下AQS。AQS是JDK1.5提供的一个基于FIFO等待队列实现的一个用于实现同步器的基础框架，这个基础框架的重要性可以这么说，JCU包里面几乎所有的有关锁、多线程并发以及线程同步器等重要组件的实现都是基于AQS这个框架。参考博文：<a href="http://www.importnew.com/22924.html" target="_blank" rel="external">深度解析AQS</a>。<a href="http://www.importnew.com/22102.html" target="_blank" rel="external">AQS实现分析（上）</a>,<a href="http://www.importnew.com/22108.html" target="_blank" rel="external">AQS实现分析（下）</a></p>
<p>&#160; &#160; &#160; &#160;<font color="red">AQS的核心思想是基于volatile int state这样的一个属性同时配合Unsafe工具对其原子性的操作来实现对当前锁的状态进行修改。当state的值为0的时候，标识改Lock不被任何线程所占有。</font>我们假设目前有三个线程Thread1、Thread2、Thread3同时去竞争锁，如果结果是Thread1获取了锁，Thread2和Thread3进入了等待队列，那么他们的样子如下：</p>
<p><img src="http://static.oschina.net/uploads/space/2016/0403/104016_XiVq_1759553.png" alt=""></p>
<p>&#160; &#160; &#160; &#160;AQS的等待队列基于一个双向链表实现的，HEAD节点不关联线程，后面两个节点分别关联Thread2和Thread3，他们将会按照先后顺序被串联在这个队列上。这个时候如果后面再有线程进来的话将会被当做队列的TAIL。</p>
<p>&#160; &#160; &#160; &#160;三个线程同时进来，他们会首先会通过CAS去修改state的状态，如果修改成功，那么竞争成功，因此这个时候三个线程只有一个CAS成功，其他两个线程失败，也就是tryAcquire返回false。addWaiter会把将当前线程关联的EXCLUSIVE类型的节点入队列，如果队尾节点不为null，则说明队列中已经有线程在等待了，那么直接入队尾。对于我们举的例子，这边的逻辑应该是走enq，也就是开始队尾是null，其实这个时候整个队列都是null的。</p>
<p>&#160; &#160; &#160; &#160;<font color="red">他们是如何实现不进行加锁，当有多个线程，或者说很多很多的线程同时执行的时候，怎么能保证最终他们都能够乖乖的入队列而不会出现并发问题的呢？这也是这部分代码的经典之处，多线程竞争，热点、单点在队列尾部，多个线程都通过【CAS+死循环】这个free-lock黄金搭档来对队列进行修改，每次能够保证只有一个成功，如果失败下次重试，如果是N个线程，那么每个线程最多loop N次，最终都能够成功。</font></p>
<p>&#160; &#160; &#160; &#160;羊群效应：</p>
<p>&#160; &#160; &#160; &#160;这里说一下羊群效应，当有多个线程去竞争同一个锁的时候，假设锁被某个线程占用，那么如果有成千上万个线程在等待锁，有一种做法是同时唤醒这成千上万个线程去去竞争锁，这个时候就发生了羊群效应，海量的竞争必然造成资源的剧增和浪费，因此终究只能有一个线程竞争成功，其他线程还是要老老实实的回去等待。<font color="red">AQS的FIFO的等待队列给解决在锁竞争方面的羊群效应问题提供了一个思路：保持一个FIFO队列，队列每个节点只关心其前一个节点的状态，线程唤醒也只唤醒队头等待线程。其实这个思路已经被应用到了分布式锁的实践中，</font>见：Zookeeper分布式锁的改进实现方案。</p>
<h3 id="ReetrantReadWriteLock"><a href="#ReetrantReadWriteLock" class="headerlink" title="ReetrantReadWriteLock"></a>ReetrantReadWriteLock</h3><p>&#160; &#160; &#160; &#160;ReadWriteLock管理一组锁，一个是只读的锁，一个是写锁。读锁可以在没有写锁的时候被多个线程同时持有，写锁是独占的。 所有读写锁的实现必须确保写操作对读操作的内存影响。换句话说，一个获得了读锁的线程必须能看到前一个释放的写锁所更新的内容。 读写锁比互斥锁允许对于共享数据更大程度的并发。每次只能有一个写线程，但是同时可以有多个线程并发地读数据。ReadWriteLock适用于读多写少的并发情况。ReadWriteLock是一个接口，主要有两个方法，如下： </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">public interface ReadWriteLock &#123;</div><div class="line">    /**</div><div class="line">     * 返回读锁</div><div class="line">     */</div><div class="line">    Lock readLock();</div><div class="line"></div><div class="line">    /**</div><div class="line">     * 返回写锁</div><div class="line">     */</div><div class="line">    Lock writeLock();</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>参考博文：<a href="http://blog.csdn.net/qq_19431333/article/details/70568478" target="_blank" rel="external">ReetrantReadWriteLock解析</a>，</p>
<h3 id="Condition"><a href="#Condition" class="headerlink" title="Condition"></a>Condition</h3><p>&#160; &#160; &#160; &#160;Condition 将 Object 监视器方法（wait、notify 和 notifyAll）分解成截然不同的对象，以便通过将这些对象与任意 Lock 实现组合使用，为每个对象提供多个等待 set （wait-set）。其中，Lock 替代了 synchronized 方法和语句的使用，Condition 替代了 Object 监视器方法的使用。</p>
<p>&#160; &#160; &#160; &#160;在Condition中，用await()替换wait()，用signal()替换notify()，用signalAll()替换notifyAll()，传统线程的通信方式，Condition都可以实现，这里注意，Condition是被绑定到Lock上的，要创建一个Lock的Condition必须用newCondition()方法。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">class BoundedBuffer &#123;  </div><div class="line">   final Lock lock = new ReentrantLock();//锁对象  </div><div class="line">   final Condition notFull  = lock.newCondition();//写线程条件   </div><div class="line">   final Condition notEmpty = lock.newCondition();//读线程条件   </div><div class="line">  </div><div class="line">   final Object[] items = new Object[100];//缓存队列  </div><div class="line">   int putptr/*写索引*/, takeptr/*读索引*/, count/*队列中存在的数据个数*/;  </div><div class="line">  </div><div class="line">   public void put(Object x) throws InterruptedException &#123;  </div><div class="line">     lock.lock();  </div><div class="line">     try &#123;  </div><div class="line">       while (count == items.length)//如果队列满了   </div><div class="line">         notFull.await();//阻塞写线程  </div><div class="line">       items[putptr] = x;//赋值   </div><div class="line">       if (++putptr == items.length) putptr = 0;//如果写索引写到队列的最后一个位置了，那么置为0  </div><div class="line">       ++count;//个数++  </div><div class="line">       notEmpty.signal();//唤醒读线程  </div><div class="line">     &#125; finally &#123;  </div><div class="line">       lock.unlock();  </div><div class="line">     &#125;  </div><div class="line">   &#125;  </div><div class="line">  </div><div class="line">   public Object take() throws InterruptedException &#123;  </div><div class="line">     lock.lock();  </div><div class="line">     try &#123;  </div><div class="line">       while (count == 0)//如果队列为空  </div><div class="line">         notEmpty.await();//阻塞读线程  </div><div class="line">       Object x = items[takeptr];//取值   </div><div class="line">       if (++takeptr == items.length) takeptr = 0;//如果读索引读到队列的最后一个位置了，那么置为0  </div><div class="line">       --count;//个数--  </div><div class="line">       notFull.signal();//唤醒写线程  </div><div class="line">       return x;  </div><div class="line">     &#125; finally &#123;  </div><div class="line">       lock.unlock();  </div><div class="line">     &#125;  </div><div class="line">   &#125;   </div><div class="line"> &#125;</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;这是一个处于多线程工作环境下的缓存区，缓存区提供了两个方法，put和take，put是存数据，take是取数据，内部有个缓存队列，具体变量和方法说明见代码，这个缓存区类实现的功能：有多个线程往里面存数据和从里面取数据，其缓存队列(先进先出后进后出)能缓存的最大数值是100，多个线程间是互斥的，当缓存队列中存储的值达到100时，将写线程阻塞，并唤醒读线程，当缓存队列中存储的值为0时，将读线程阻塞，并唤醒写线程，这也是ArrayBlockingQueue的内部实现。下面分析一下代码的执行过程：</p>
<ol>
<li><p>一个写线程执行，调用put方法；</p>
</li>
<li><p>判断count是否为100，显然没有100；</p>
</li>
<li><p>继续执行，存入值；</p>
</li>
<li><p>判断当前写入的索引位置++后，是否和100相等，相等将写入索引值变为0，并将count+1；</p>
</li>
<li><p>仅唤醒读线程阻塞队列中的一个；</p>
</li>
<li><p>一个读线程执行，调用take方法；</p>
</li>
<li><p>……</p>
</li>
<li><p>仅唤醒写线程阻塞队列中的一个。</p>
</li>
</ol>
<p>&#160; &#160; &#160; &#160;这就是多个Condition的强大之处，假设缓存队列中已经存满，那么阻塞的肯定是写线程，唤醒的肯定是读线程，相反，阻塞的肯定是读线程，唤醒的肯定是写线程，那么假设只有一个Condition会有什么效果呢，缓存队列中已经存满，这个Lock不知道唤醒的是读线程还是写线程了，如果唤醒的是读线程，皆大欢喜，如果唤醒的是写线程，那么线程刚被唤醒，又被阻塞了，这时又去唤醒，这样就浪费了很多时间。</p>
<p>参考博文：<a href="http://blog.csdn.net/luonanqin/article/details/41894755" target="_blank" rel="external">Condition的await-signal流程详解</a>,<a href="http://blog.csdn.net/ghsau/article/details/7481142" target="_blank" rel="external">Condition-线程通信更高效的方式</a>,<a href="http://www.importnew.com/9281.html" target="_blank" rel="external">怎么理解Condition</a>.</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;concurrent包总结（一）&quot;&gt;&lt;a href=&quot;#concurrent包总结（一）&quot; class=&quot;headerlink&quot; title=&quot;concurrent包总结（一）&quot;&gt;&lt;/a&gt;concurrent包总结（一）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;在前面的基础知识之上，我们开始对concurrent包的全面分析。&lt;/p&gt;
    
    </summary>
    
      <category term="高效并发" scheme="http://yoursite.com/categories/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91/"/>
    
    
      <category term="concurrent包总结" scheme="http://yoursite.com/tags/concurrent%E5%8C%85%E6%80%BB%E7%BB%93/"/>
    
  </entry>
  
  <entry>
    <title>高效并发（二）</title>
    <link href="http://yoursite.com/2017/03/16/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91%EF%BC%88%E4%BA%8C%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/16/高效并发（二）/</id>
    <published>2017-03-16T02:55:01.000Z</published>
    <updated>2017-06-16T08:22:30.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="并发基本知识总结（二）"><a href="#并发基本知识总结（二）" class="headerlink" title="并发基本知识总结（二）"></a>并发基本知识总结（二）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;在学习完虚拟机之后，我们了解了java的内存模型、实现线程安全的方法和jvm的一些锁优化机制，我们现在就把方向在往线程的基本操作，到concurrent包里面的线程安全类、原子类和一些lock的实现类。了解整个java并发基础。</p>
 <a id="more"></a>
<h2 id="传统方式"><a href="#传统方式" class="headerlink" title="传统方式"></a>传统方式</h2><h3 id="产生线程的方法"><a href="#产生线程的方法" class="headerlink" title="产生线程的方法"></a>产生线程的方法</h3><ol>
<li>继承thread，重写run方法</li>
<li>thread+runnable</li>
<li>callable+future（新型）</li>
</ol>
<h3 id="控制线程"><a href="#控制线程" class="headerlink" title="控制线程"></a>控制线程</h3><ol>
<li>join</li>
<li>sleep</li>
<li>yield</li>
<li>修改优先级</li>
</ol>
<h3 id="传统线程里面的通信"><a href="#传统线程里面的通信" class="headerlink" title="传统线程里面的通信"></a>传统线程里面的通信</h3><ol>
<li>notify</li>
<li>notifyAll</li>
<li>wait</li>
</ol>
<h2 id="并发必要知识"><a href="#并发必要知识" class="headerlink" title="并发必要知识"></a>并发必要知识</h2><p>&#160; &#160; &#160; &#160;由第一节我们知道了jvm的内存模型的特点：原子性、可见性、有序性。其中多个过程之间内存可见性的顺序可能不一致，比如下面的程序例子。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">public class Test1 &#123;</div><div class="line">    private int a=1, b=2;</div><div class="line"> </div><div class="line">    public void foo()&#123;  // 线程1 </div><div class="line">        a=3;</div><div class="line">        b=4;</div><div class="line">    &#125;</div><div class="line"> </div><div class="line">    public int getA()&#123; // 线程2</div><div class="line">        return a;</div><div class="line">    &#125;    </div><div class="line">    public int getB()&#123; // 线程2</div><div class="line">        return b;</div><div class="line">    &#125;</div><div class="line">&#125;</div><div class="line"></div><div class="line"></div><div class="line"></div><div class="line">//可能出现的结果</div><div class="line"></div><div class="line">A：a=1, b=2  // 都未改变</div><div class="line">B：a=3, b=4  // 都改变了</div><div class="line">C：a=3, b=2  //  a改变了，b未改变</div><div class="line">D：a=1, b=4  //  b改变了，a未改变</div></pre></td></tr></table></figure>
<p>造成这个的原因有下面两个：</p>
<font color="red"><br><br>1. Java编译器的重排序(Reording)操作有可能导致执行顺序和代码顺序不一致<br><br><br>2. 从线程工作内存写回主存时顺序无法保证。<br><br><br></font><br>&#160; &#160; &#160; &#160;正因为上面的那些问题，JMM中一个重要问题就是：如何让多线程之间，对象的状态对于各线程的“可视性”是顺序一致的。它的解决方式就是 Happens-before 规则：<br>JMM为所有程序内部动作定义了一个偏序关系，叫做happens-before。要想保证执行动作B的线程看到动作A的结果（无论A和B是否发生在同一个线程中），A和B之间就必须满足happens-before关系。<br><br><img src="http://opb7t58xj.bkt.clouddn.com/happen-before.png" alt=""><br><br>&#160; &#160; &#160; &#160;<font color="red">我们重点关注的是②，③，这两条也是我们通常编程中常用的。也就是加锁（包括新的lock类和synchronized）和volatile（语义增强了），当然也有不变类（final）和atomic原子类。</font>

<h3 id="监视器"><a href="#监视器" class="headerlink" title="监视器"></a>监视器</h3><p>&#160; &#160; &#160; &#160;早期Java中的锁只有最基本的synchronized，它是一种互斥的实现方式。在Java5之后，增加了一些其它锁，比如ReentrantLock，它基本作用和synchronized相似，但提供了更多的操作方式，比如在获取锁时不必像synchronized那样只是傻等，可以设置定时，轮询，或者中断，这些方法使得它在获取多个锁的情况可以避免死锁操作。</p>
<p>&#160; &#160; &#160; &#160;而我们需要了解的是ReentrantLock的性能相对synchronized来说有很大的提高。（不过据说Java6后对synchronized进行了优化，两者已经接近了。）在ConcurrentHashMap中，每个hash区间使用的锁正是ReentrantLock。</p>
<h3 id="增强的volatile语义"><a href="#增强的volatile语义" class="headerlink" title="增强的volatile语义"></a>增强的volatile语义</h3><p>&#160; &#160; &#160; &#160;Volatile可以看做一种轻量级的锁，但又和锁有些不同。</p>
<ol>
<li>它对于多线程，不是一种互斥（mutex）关系。</li>
<li>用volatile修饰的变量，不能保证该变量状态的改变对于其他线程来说是一种“原子化操作”。</li>
</ol>
<p>&#160; &#160; &#160; &#160;在Java5之前，JMM对Volatile的定义是：保证读写volatile都直接发生在main memory中，线程的working memory不进行缓存。它只承诺了读和写过程的可见性，并没有对Reording做限制，所以旧的Volatile并不太可靠。在Java5之后，JMM对volatile的语义进行了增强。就是我们看到的③ volatile变量法则。对volatile域的写入操作先行于每一个后续的读写操作。</p>
<p>&#160; &#160; &#160; &#160;所以，在使用Volatile时，需要注意：首先，需不需要互斥；其次，对象状态的改变是不是原子化的。它可以简化实现或者同步策略，确保引用对象的可见性，比如标志生命周期的事件：开始或者关闭。</p>
<p>&#160; &#160; &#160; &#160;脆弱的使用条件：</p>
<ol>
<li>写入变量不依赖变量的当前值，或者能够保证只有单一的线程修改变量的值。</li>
<li>变量不需要和其他变量共同参与不变约束。</li>
<li>访问变量时不需要其他原因加锁。<h3 id="不变模式（immutable）"><a href="#不变模式（immutable）" class="headerlink" title="不变模式（immutable）"></a>不变模式（immutable）</h3>&#160; &#160; &#160; &#160;多线程安全里最简单的一种保障方式。因为你拿他没有办法，想改变它也没有机会。不变模式主要通过final关键字来限定的。在JMM中final关键字还有特殊的语义。Final域使得确保初始化安全性（initialization safety）成为可能，初始化安全性让不可变形对象不需要同步就能自由地被访问和共享。</li>
</ol>
<h3 id="原子类"><a href="#原子类" class="headerlink" title="原子类"></a>原子类</h3><p>&#160; &#160; &#160; &#160;如果操作都是原子操作，那就实现线程安全了。</p>
<h2 id="基本知识"><a href="#基本知识" class="headerlink" title="基本知识"></a>基本知识</h2><p>&#160; &#160; &#160; &#160;四种进程或线程同步互斥的控制方法：</p>
<ol>
<li><p>临界区:通过对多线程的串行化来访问公共资源或一段代码，速度快，适合控制数据访问。 </p>
</li>
<li><p>互斥量:为协调共同对一个共享资源的单独访问而设计的。 </p>
</li>
<li><p>信号量:为控制一个具有有限数量用户资源而设计。 </p>
</li>
<li><p>事件:用来通知线程有一些事件已发生，从而启动后继任务的开始。</p>
</li>
</ol>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！q q q</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;并发基本知识总结（二）&quot;&gt;&lt;a href=&quot;#并发基本知识总结（二）&quot; class=&quot;headerlink&quot; title=&quot;并发基本知识总结（二）&quot;&gt;&lt;/a&gt;并发基本知识总结（二）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;在学习完虚拟机之后，我们了解了java的内存模型、实现线程安全的方法和jvm的一些锁优化机制，我们现在就把方向在往线程的基本操作，到concurrent包里面的线程安全类、原子类和一些lock的实现类。了解整个java并发基础。&lt;/p&gt;
    
    </summary>
    
      <category term="高效并发" scheme="http://yoursite.com/categories/%E9%AB%98%E6%95%88%E5%B9%B6%E5%8F%91/"/>
    
    
      <category term="并发基础知识" scheme="http://yoursite.com/tags/%E5%B9%B6%E5%8F%91%E5%9F%BA%E7%A1%80%E7%9F%A5%E8%AF%86/"/>
    
  </entry>
  
  <entry>
    <title>剑指offer：近期整理</title>
    <link href="http://yoursite.com/2017/03/15/%E5%89%91%E6%8C%87offer%EF%BC%9A%E8%BF%91%E6%9C%9F%E6%95%B4%E7%90%86/"/>
    <id>http://yoursite.com/2017/03/15/剑指offer：近期整理/</id>
    <published>2017-03-15T13:27:55.000Z</published>
    <updated>2017-05-15T13:41:33.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>&#160; &#160; &#160; &#160;一次写一篇效率太低，以后多几篇一起篇，特别是同一类型的题目，本次是位运算、数学计算、数组操作、链表操作的四道题应用。</p>
  <a id="more"></a>
<h1 id="近期整理的四道题"><a href="#近期整理的四道题" class="headerlink" title="近期整理的四道题"></a>近期整理的四道题</h1><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><ol>
<li>输入一个整数，输出该数二进制表示中1的个数。其中负数用补码表示。</li>
<li>给定一个double类型的浮点数base和int类型的整数exponent。求base的exponent次方。</li>
<li>输入一个整数数组，实现一个函数来调整该数组中数字的顺序，使得所有的奇数位于数组的前半部分，所有的偶数位于位于数组的后半部分，并保证奇数和奇数，偶数和偶数之间的相对位置不变。</li>
<li>输入一个链表，输出该链表中倒数第k个结点。</li>
</ol>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><ol>
<li>第一道题，我们采用精妙的位运算，关于位运算的应用，包括乘除，在第二题用过，交换值等我们分析过，在这道题里，<font color="red">把一个整数减去1，再和原整数做与运算，会把该整数最右边一个1变成0</font>。</li>
<li>我们采用递归求指数结果，然后在考虑奇偶，正负的问题。</li>
<li>遍历一遍数组，求出奇偶的个数，然后在遍历一遍把奇偶值填进去。</li>
<li>我们先使一个head往前走k-1步，然后第二个head和第一个一起走，当第一个走到链表的结尾时，第二个所在的地方就是倒数第k个。</li>
</ol>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div></pre></td><td class="code"><pre><div class="line">public int NumberOf1(int n) &#123;</div><div class="line"></div><div class="line">        int count=0;</div><div class="line">        while(n!=0)&#123;</div><div class="line">            count++;</div><div class="line">            n=(n-1)&amp;n;</div><div class="line">        &#125;</div><div class="line">        return count;</div><div class="line">    &#125;</div><div class="line">    </div><div class="line"></div><div class="line">public double Power(double base, int exponent) &#123;</div><div class="line">        int n=Math.abs(exponent);</div><div class="line">        if(n==0)</div><div class="line">            return 1;</div><div class="line">        if(n==1)</div><div class="line">            return base;</div><div class="line">        double  result=Power(base,n&gt;&gt;1);</div><div class="line">        result*=result;</div><div class="line">        if((n&amp;1)==1)</div><div class="line">            result*=base;</div><div class="line">        if(exponent&lt;0)</div><div class="line">            result=1/result;</div><div class="line">        return result;</div><div class="line">  &#125;</div><div class="line">  </div><div class="line"> public void reOrderArray(int [] array) &#123;</div><div class="line">        int [] result = new int[array.length];</div><div class="line">        int numOfOdd = 0;</div><div class="line">        int numOfEven = 0;</div><div class="line">        for(int i=0;i&lt;array.length;i++)&#123;</div><div class="line">            if((array[i]&amp;0x1) == 1)&#123;</div><div class="line">                numOfOdd++;</div><div class="line">            &#125;else&#123;</div><div class="line">                numOfEven++;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">         </div><div class="line">        int indexOfOdd = 0;</div><div class="line">        int indexOfEven = numOfOdd;</div><div class="line">        for(int i=0;i&lt;array.length;i++)&#123;</div><div class="line">            if((array[i]&amp;0x1) == 1)&#123;</div><div class="line">                result[indexOfOdd++] = array[i];</div><div class="line">            &#125;else&#123;</div><div class="line">                result[indexOfEven++] = array[i];</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        for(int i=0;i&lt;array.length;i++)&#123;</div><div class="line">            array[i] = result[i];</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    </div><div class="line">public ListNode FindKthToTail(ListNode head,int k) &#123;</div><div class="line"></div><div class="line">        if(head==null||k&lt;=0)&#123;</div><div class="line">            return null;</div><div class="line">        &#125;</div><div class="line">        ListNode n1=head;</div><div class="line">        </div><div class="line">        ListNode n2=head;</div><div class="line">        for(int i=1;i&lt;k;i++)&#123;</div><div class="line">            if(n1.next!=null)&#123;</div><div class="line">                n1=n1.next;</div><div class="line">            &#125;else&#123;</div><div class="line">                return null;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">       while(n1.next!=null)&#123;</div><div class="line">           n1=n1.next;</div><div class="line">           n2=n2.next;</div><div class="line">       &#125;</div><div class="line">        return n2;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<h1 id="友情链接"><a href="#友情链接" class="headerlink" title="友情链接"></a>友情链接</h1><p>所有的剑指offer代码，我托管到GitHub上了，可以直接运行，对照博客理解思路。<a href="https://github.com/wustzoujing/Algorithm_Learning" target="_blank" rel="external">wustzoujing/Algorithm_Learning</a>。觉得还可以的麻烦fork一下。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;一次写一篇效率太低，以后多几篇一起篇，特别是同一类型的题目，本次是位运算、数学计算、数组操作、链表操作的四道题应用。&lt;/p&gt;
    
    </summary>
    
      <category term="剑指offer详解" scheme="http://yoursite.com/categories/%E5%89%91%E6%8C%87offer%E8%AF%A6%E8%A7%A3/"/>
    
    
      <category term="算法题" scheme="http://yoursite.com/tags/%E7%AE%97%E6%B3%95%E9%A2%98/"/>
    
  </entry>
  
  <entry>
    <title>Redis学习笔记（二）</title>
    <link href="http://yoursite.com/2017/03/12/Redis%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%BA%8C%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/12/Redis学习笔记（二）/</id>
    <published>2017-03-12T08:54:09.000Z</published>
    <updated>2017-06-28T03:16:52.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Redis学习笔记（二）"><a href="#Redis学习笔记（二）" class="headerlink" title="Redis学习笔记（二）"></a>Redis学习笔记（二）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;上节总结了Redis的基本操作、特性和原理，我们还需要从使用技巧，优化策略等方面来学习。</p>
 <a id="more"></a>
<h2 id="redis和关系型数据库适用场景"><a href="#redis和关系型数据库适用场景" class="headerlink" title="redis和关系型数据库适用场景"></a>redis和关系型数据库适用场景</h2><ol>
<li><a href="http://www.jb51.net/article/62646.htm" target="_blank" rel="external">Redis数据库的使用场景介绍</a></li>
<li><a href="http://blog.csdn.net/a2534725767/article/details/54017956" target="_blank" rel="external">对比使用实例说明</a></li>
</ol>
<h2 id="key设计原则"><a href="#key设计原则" class="headerlink" title="key设计原则"></a>key设计原则</h2><p>参考实例：<a href="http://blog.csdn.net/zdp072/article/details/50986702" target="_blank" rel="external">redis key设计技巧</a>，<a href="http://www.oschina.net/question/12_27517" target="_blank" rel="external">浅谈 Redis 数据库的键值设计</a></p>
<p>总结如下：</p>
<ol>
<li>表达从属关系（一对多，多对多），最好用集合； 比如： 书名和标签，关注与被关注（微博粉丝关系）等等。</li>
<li>求最近的，一般利用链表后入后出的特性。比如：最近N个登录的用户，可以维护一个登录的链表，控制他的长度，使得里面永远保存的是最近的N个登录用户。</li>
<li>对于排序，积分榜这类需求，可以用有序集合，比如：我们把用户和登录次数统一存储在一个sorted set里，然后就可以求出登录次数最多用户。</li>
<li>对于大数据量的非是即否关系，还可以通过位图（setbit）的方式，比如：1亿个用户, 每个用户 登陆/做任意操作,记为今天活跃,否则记为不活跃；（每天一个位图来记录，会员id就是位图的位置）；</li>
</ol>
<h2 id="优化策略"><a href="#优化策略" class="headerlink" title="优化策略"></a>优化策略</h2><ol>
<li><a href="http://blog.sina.com.cn/s/blog_4be888450100z2ze.html" target="_blank" rel="external">Redis优化经验</a></li>
<li><a href="http://www.4wei.cn/archives/1002605" target="_blank" rel="external">Redis 优化要点</a></li>
</ol>
<p>总结：</p>
<ol>
<li>根据业务需要选择合适的数据类型，并为不同的应用场景设置相应的紧凑存储参数。</li>
<li>当业务场景不需要数据持久化时，关闭所有的持久化方式可以获得最佳的性能以及最大的内存使用量。</li>
<li>如果需要使用持久化，根据是否可以容忍重启丢失部分数据在快照方式与语句追加方式之间选择其一，不要使用虚拟内存以及diskstore方式。</li>
<li>不要让你的Redis所在机器物理内存使用超过实际内存总量的3/5。</li>
</ol>
<p>&#160; &#160; &#160; &#160;redis.conf中的maxmemory选项，该选项是告诉Redis当使用了多少物理内存后就开始拒绝后续的写入请求，该参数能很好的保护好你的Redis不会因为使用了过多的物理内存而导致swap,最终严重影响性能甚至崩溃。<br>redis.conf文件中 vm-enabled 为 no</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Redis学习笔记（二）&quot;&gt;&lt;a href=&quot;#Redis学习笔记（二）&quot; class=&quot;headerlink&quot; title=&quot;Redis学习笔记（二）&quot;&gt;&lt;/a&gt;Redis学习笔记（二）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;上节总结了Redis的基本操作、特性和原理，我们还需要从使用技巧，优化策略等方面来学习。&lt;/p&gt;
    
    </summary>
    
      <category term="Redis" scheme="http://yoursite.com/categories/Redis/"/>
    
    
      <category term="NoSql" scheme="http://yoursite.com/tags/NoSql/"/>
    
  </entry>
  
  <entry>
    <title>剑指offer：斐波那契数列相关的三道题</title>
    <link href="http://yoursite.com/2017/03/09/%E5%89%91%E6%8C%87offer%EF%BC%9A%E6%96%90%E6%B3%A2%E9%82%A3%E5%A5%91%E6%95%B0%E5%88%97%E7%9B%B8%E5%85%B3%E7%9A%84%E4%B8%89%E9%81%93%E9%A2%98/"/>
    <id>http://yoursite.com/2017/03/09/剑指offer：斐波那契数列相关的三道题/</id>
    <published>2017-03-09T12:21:41.000Z</published>
    <updated>2017-05-15T10:00:05.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>&#160; &#160; &#160; &#160;如果我们需要重复多次计算相同的问题，通常可以选择递归和循环两种方法，递归的代码简洁，利用分治的算法思想，但是我们就要注意递归有时候效率会很低，而且容易栈溢出，接下来几题中，我们就要注意。</p>
  <a id="more"></a>
<h1 id="斐波那契数列相关的三道题"><a href="#斐波那契数列相关的三道题" class="headerlink" title="斐波那契数列相关的三道题"></a>斐波那契数列相关的三道题</h1><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><ol>
<li>大家都知道斐波那契数列，现在要求输入一个整数n，请你输出斐波那契数列的第n项。n&lt;=39。</li>
<li>一只青蛙一次可以跳上1级台阶，也可以跳上2级。求该青蛙跳上一个n级的台阶总共有多少种跳法。</li>
<li>一只青蛙一次可以跳上1级台阶，也可以跳上2级……它也可以跳上n级。求该青蛙跳上一个n级的台阶总共有多少种跳法。</li>
<li>我们可以用2 x 1的小矩形横着或者竖着去覆盖更大的矩形。请问用n个2 x 1的小矩形无重叠地覆盖一个2 x n 的大矩形，总共有多少种方法？</li>
</ol>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>&#160; &#160; &#160; &#160;第一题如果用递归做，会发现效率很低，因为我们利用f(n)=f(n-1)+f(n-2)计算f(n)，导致每次的重复计算f(n-1)、f(n-2)，其实我们已经计算过这些值了，所以我们采用非递归来做。</p>
<p>&#160; &#160; &#160; &#160;青蛙跳台阶的第一道，我们发现这就是斐波那契数列，跳到某一台阶的次数就是跳到前一格台阶的次数加上跳到前两格台阶的次数。采用和上一题一样的做法。</p>
<p>&#160; &#160; &#160; &#160;青蛙跳台阶的第二道，我们用数学归纳法可以证明f(n)=2^2-1,其实这类题目也有很多，我们直接找到通式可以得到结果，像京东的几道题，还有求极限的，有兴趣可以去了解一下。</p>
<p>&#160; &#160; &#160; &#160;方块覆盖问题，其实仔细想想，也是青蛙跳台阶一样的方法。</p>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">public int Fibonacci(int n) &#123;</div><div class="line"></div><div class="line">        if(n&lt;1)</div><div class="line">            return 0;</div><div class="line">        if(n==1)</div><div class="line">            return 1;</div><div class="line">        if(n==2) </div><div class="line">            return 1;</div><div class="line">        int a=1,b=1,reslut=0;</div><div class="line">        for(int i=3;i&lt;=n;++i)&#123;</div><div class="line">            reslut=a+b;</div><div class="line">            a=b;</div><div class="line">            b=reslut;</div><div class="line">        &#125;</div><div class="line">         return reslut;   </div><div class="line">    &#125;</div><div class="line">    </div><div class="line"></div><div class="line">    public int JumpFloor(int target) &#123;</div><div class="line"></div><div class="line">        if(target&lt;=0)&#123;</div><div class="line">            return 0;</div><div class="line">        &#125;</div><div class="line">        if(target==1)&#123;</div><div class="line">            return 1;</div><div class="line">        &#125;</div><div class="line">        if(target==2)&#123;</div><div class="line">            return 2;</div><div class="line">        &#125;</div><div class="line">        int a=1,b=2,c=0;</div><div class="line">        for(int i=3;i&lt;=target;i++)&#123;</div><div class="line">            c=b+a;</div><div class="line">            a=b;</div><div class="line">            b=c;</div><div class="line">        &#125;</div><div class="line">        return c;</div><div class="line">    &#125;</div><div class="line">    </div><div class="line">    </div><div class="line">    public int JumpFloorII(int target) &#123;</div><div class="line">        </div><div class="line">        if(target&lt;=0)&#123;</div><div class="line">            return 0;</div><div class="line">        &#125;</div><div class="line">        else&#123;</div><div class="line">            int c=1;</div><div class="line">            for(int i=1;i&lt;target;i++)&#123;</div><div class="line">                c=2*c;</div><div class="line">            &#125;</div><div class="line">            return c;</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">        </div><div class="line">    public int RectCover(int target) &#123;</div><div class="line"></div><div class="line">        if(target&lt;=0)&#123;</div><div class="line">            return 0;</div><div class="line">        &#125;</div><div class="line">        if(target==1)&#123;</div><div class="line">            return 1;</div><div class="line">        &#125;</div><div class="line">        if(target==2)&#123;</div><div class="line">            return 2;</div><div class="line">        &#125;</div><div class="line">        int a=1,b=2,c=0;</div><div class="line">        for(int i=3;i&lt;=target;i++)&#123;</div><div class="line">            c=b+a;</div><div class="line">            a=b;</div><div class="line">            b=c;</div><div class="line">        &#125;</div><div class="line">        return c;</div><div class="line">    &#125;</div></pre></td></tr></table></figure>
<h1 id="友情链接"><a href="#友情链接" class="headerlink" title="友情链接"></a>友情链接</h1><p>所有的剑指offer代码，我托管到GitHub上了，可以直接运行，对照博客理解思路。<a href="https://github.com/wustzoujing/Algorithm_Learning" target="_blank" rel="external">wustzoujing/Algorithm_Learning</a>。觉得还可以的麻烦fork一下。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;如果我们需要重复多次计算相同的问题，通常可以选择递归和循环两种方法，递归的代码简洁，利用分治的算法思想，但是我们就要注意递归有时候效率会很低，而且容易栈溢出，接下来几题中，我们就要注意。&lt;/p&gt;
    
    </summary>
    
      <category term="剑指offer详解" scheme="http://yoursite.com/categories/%E5%89%91%E6%8C%87offer%E8%AF%A6%E8%A7%A3/"/>
    
    
      <category term="算法题" scheme="http://yoursite.com/tags/%E7%AE%97%E6%B3%95%E9%A2%98/"/>
    
  </entry>
  
  <entry>
    <title>Redis学习笔记（一）</title>
    <link href="http://yoursite.com/2017/03/08/Redis%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%EF%BC%88%E4%B8%80%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/08/Redis学习笔记（一）/</id>
    <published>2017-03-08T01:00:20.000Z</published>
    <updated>2017-06-28T03:16:41.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Redis学习笔记（一）"><a href="#Redis学习笔记（一）" class="headerlink" title="Redis学习笔记（一）"></a>Redis学习笔记（一）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;Redis作为一种典型的NoSql，可以做缓存也可以持久化，我们要从事务，消息订阅，持久化机制，集群部署，客户端使用等方面进行学习。</p>
 <a id="more"></a>
<h2 id="特点"><a href="#特点" class="headerlink" title="特点"></a>特点</h2><p>&#160; &#160; &#160; &#160;安装部署可以根据自己的系统去找安装教程，这个不多说。Redis官方介绍说是一种开源的，BSD许可的，高级的key-value,单线程存储系统，可以用来存储字符串，哈希，链表，集合，索所以常用来提供数据结构。</p>
<p>&#160; &#160; &#160; &#160;相比另一个缓存数据看memcached，他的特点如下：</p>
<ol>
<li>Redis有持久化机制，除了缓存还可以做存储。</li>
<li>Redis数据结构丰富，而memcached只有字符串。</li>
</ol>
<p>&#160; &#160; &#160; &#160;安装成功后，有五个文件夹，分别是redis性能测试工具，aof日志工具，rdb日志工具，连接客户端和redis服务进程。</p>
<h2 id="指令"><a href="#指令" class="headerlink" title="指令"></a>指令</h2><p>&#160; &#160; &#160; &#160;相关指令有get，set，move，select，ttl等等，支持模糊匹配，还有一些配置设置，不同数据结构不一样，可以查API，Redis对Java，C#，C++，PHP等语言的支持，其实也就是这些操作的语言的工具包。</p>
<h2 id="数据结构底层原理"><a href="#数据结构底层原理" class="headerlink" title="数据结构底层原理"></a>数据结构底层原理</h2><ol>
<li><a href="http://blog.csdn.net/caishenfans/article/details/44784131" target="_blank" rel="external">Redis的五种对象类型及其底层实现</a>. </li>
<li><a href="http://blog.csdn.net/hanhuili/article/details/17710781" target="_blank" rel="external">深入理解Redis：底层数据结构</a></li>
</ol>
<h2 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h2><ol>
<li><a href="http://www.jb51.net/article/54774.htm" target="_blank" rel="external">Redis中5种数据结构的使用场景介绍</a></li>
<li><a href="http://blog.csdn.net/u011204847/article/details/51302109" target="_blank" rel="external">Redis高级特性及应用场景</a></li>
</ol>
<h2 id="事务和锁"><a href="#事务和锁" class="headerlink" title="事务和锁"></a>事务和锁</h2><ol>
<li><a href="http://blog.csdn.net/hechurui/article/details/49508749" target="_blank" rel="external">Redis事务介绍</a></li>
<li><a href="http://blog.csdn.net/bugall/article/details/52386698" target="_blank" rel="external">redis 事务实现原理</a></li>
</ol>
<h2 id="消息发布和订阅"><a href="#消息发布和订阅" class="headerlink" title="消息发布和订阅"></a>消息发布和订阅</h2><ol>
<li><a href="http://blog.csdn.net/clh604/article/details/19754939" target="_blank" rel="external">Redis 发布/订阅机制原理分析</a></li>
<li><a href="http://www.cnblogs.com/redcreen/archive/2011/02/15/1955521.html" target="_blank" rel="external">redis 发布订阅</a></li>
</ol>
<h2 id="持久化机制"><a href="#持久化机制" class="headerlink" title="持久化机制"></a>持久化机制</h2><p>&#160; &#160; &#160; &#160;持久化有两种，快照rdb和日志aof。</p>
<ol>
<li><a href="http://blog.csdn.net/yinxiangbing/article/details/48627997" target="_blank" rel="external">redis 的两种持久化方式及原理</a></li>
<li><a href="http://www.cnblogs.com/Fairy-02-11/p/6182478.html" target="_blank" rel="external">redis持久化方法对比分析</a></li>
<li><a href="http://blog.csdn.net/freebird_lb/article/details/7778981" target="_blank" rel="external">Redis持久化</a></li>
</ol>
<h2 id="集群部署"><a href="#集群部署" class="headerlink" title="集群部署"></a>集群部署</h2><ol>
<li><a href="https://my.oschina.net/pwd/blog/381212" target="_blank" rel="external">redis集群,主从复制</a></li>
</ol>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;Redis学习笔记（一）&quot;&gt;&lt;a href=&quot;#Redis学习笔记（一）&quot; class=&quot;headerlink&quot; title=&quot;Redis学习笔记（一）&quot;&gt;&lt;/a&gt;Redis学习笔记（一）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;Redis作为一种典型的NoSql，可以做缓存也可以持久化，我们要从事务，消息订阅，持久化机制，集群部署，客户端使用等方面进行学习。&lt;/p&gt;
    
    </summary>
    
      <category term="Redis" scheme="http://yoursite.com/categories/Redis/"/>
    
    
      <category term="NoSql" scheme="http://yoursite.com/tags/NoSql/"/>
    
  </entry>
  
  <entry>
    <title>剑指offer：旋转数组的最小数字</title>
    <link href="http://yoursite.com/2017/03/07/%E5%89%91%E6%8C%87offer%EF%BC%9A%E6%97%8B%E8%BD%AC%E6%95%B0%E7%BB%84%E7%9A%84%E6%9C%80%E5%B0%8F%E6%95%B0%E5%AD%97/"/>
    <id>http://yoursite.com/2017/03/07/剑指offer：旋转数组的最小数字/</id>
    <published>2017-03-07T11:10:20.000Z</published>
    <updated>2017-05-15T09:59:54.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>&#160; &#160; &#160; &#160;这道题找数组中的最小数字，类似的题目之前蘑菇街视频面试都考过，找数组中的最大的两个值或者三个值。直观解题方法就是循环一遍数组，这样的复杂度是O（n），但是由题目可知，数组已部分排序，我们利用二分查找的方法是可以减少我们的复杂度的，达到O（log n）。</p>
  <a id="more"></a>
<h1 id="旋转数组的最小数字"><a href="#旋转数组的最小数字" class="headerlink" title="旋转数组的最小数字"></a>旋转数组的最小数字</h1><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><p>&#160; &#160; &#160; &#160;把一个数组最开始的若干个元素搬到数组的末尾，我们称之为数组的旋转。输入一个非递减排序的数组的一个旋转，输出旋转数组的最小元素。例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转，该数组的最小值为1。NOTE：给出的所有元素都大于0，若数组大小为0，请返回0。</p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>&#160; &#160; &#160; &#160;二分查找法的核心思想就是：每次查找都可以排除不需要查找的一半，这道题我们是可以利用这个思想的。</p>
<p>&#160; &#160; &#160; &#160;我们定义两个标志，初识是分别指向第一位和最后一位，按照题目的旋转规则，我们找到数组中间的元素，如果中间的元素位于前面的递增数组中，那他应该小于等于第二个个标志指向的元素，此时数组中最小的元素应该位于该中间元素的后面，排除了一半，利用了二分的思想；同样，如果中间元素位于后面递增子数组中，那么它应该大于等于第二个标志指向的元素。如果相等，把最高位往前移一位，最后第一个标志上就是最小值。</p>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div></pre></td><td class="code"><pre><div class="line">public class minNumberInRotateArray_6 &#123;</div><div class="line"></div><div class="line">    public int minNumberInRotateArray(int [] array) &#123;</div><div class="line"></div><div class="line">        //定义两个标志指向首位索引</div><div class="line">        int low = 0 ; int high = array.length - 1;</div><div class="line">        //结束条件</div><div class="line">        while(low &lt; high)&#123;</div><div class="line">            //取中间元素</div><div class="line">            int mid = low + (high - low) / 2;</div><div class="line">            //中间元素比尾数大，最小值在后半段</div><div class="line">            if(array[mid] &gt; array[high])&#123;</div><div class="line">                low = mid + 1;</div><div class="line"></div><div class="line">            &#125;</div><div class="line">            //如果相等，高位减1</div><div class="line">            else if(array[mid] == array[high])&#123;</div><div class="line">                high = high - 1;</div><div class="line">            &#125;</div><div class="line">            //如果中间元素比尾数小，最小值在前半段</div><div class="line">            else&#123;</div><div class="line">                high = mid;</div><div class="line">            &#125;</div><div class="line">        &#125;</div><div class="line">        //循环结束，低位标志就是最小值所在</div><div class="line">        return array[low];</div><div class="line"></div><div class="line">    &#125;</div><div class="line"></div><div class="line"></div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="友情链接"><a href="#友情链接" class="headerlink" title="友情链接"></a>友情链接</h1><p>所有的剑指offer代码，我托管到GitHub上了，可以直接运行，对照博客理解思路。<a href="https://github.com/wustzoujing/Algorithm_Learning" target="_blank" rel="external">wustzoujing/Algorithm_Learning</a>。觉得还可以的麻烦fork一下。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;这道题找数组中的最小数字，类似的题目之前蘑菇街视频面试都考过，找数组中的最大的两个值或者三个值。直观解题方法就是循环一遍数组，这样的复杂度是O（n），但是由题目可知，数组已部分排序，我们利用二分查找的方法是可以减少我们的复杂度的，达到O（log n）。&lt;/p&gt;
    
    </summary>
    
      <category term="剑指offer详解" scheme="http://yoursite.com/categories/%E5%89%91%E6%8C%87offer%E8%AF%A6%E8%A7%A3/"/>
    
    
      <category term="算法题" scheme="http://yoursite.com/tags/%E7%AE%97%E6%B3%95%E9%A2%98/"/>
    
  </entry>
  
  <entry>
    <title>剑指offer：用两个栈实现队列</title>
    <link href="http://yoursite.com/2017/03/06/%E5%89%91%E6%8C%87offer%EF%BC%9A%E7%94%A8%E4%B8%A4%E4%B8%AA%E6%A0%88%E5%AE%9E%E7%8E%B0%E9%98%9F%E5%88%97/"/>
    <id>http://yoursite.com/2017/03/06/剑指offer：用两个栈实现队列/</id>
    <published>2017-03-06T10:19:11.000Z</published>
    <updated>2017-05-15T09:59:41.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>&#160; &#160; &#160; &#160;栈和队列是非常常见的数据结构。栈先进后出，队列先进先出，Java中有定义好的栈和队列数据结构。要熟悉源码。栈和队列也有很多的联系，本题就是用两个栈实现一个队列。</p>
  <a id="more"></a>
<h1 id="用两个栈实现队列"><a href="#用两个栈实现队列" class="headerlink" title="用两个栈实现队列"></a>用两个栈实现队列</h1><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><p>&#160; &#160; &#160; &#160;用两个栈来实现一个队列，完成队列的Push和Pop操作。 队列中的元素为int类型。</p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>&#160; &#160; &#160; &#160;我们知道栈是先进后出，而队列是先进先出。两个栈是可以实现先进后出的，我们模拟进队列操作时，进队列都进入第一个栈，然后出队列操作时，先把栈1全部倒入栈2，栈2出栈完成出队列操作，为了保证顺序，栈2中元素还要在倒入栈1。</p>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div></pre></td><td class="code"><pre><div class="line">import java.util.Stack;</div><div class="line"></div><div class="line">public class Solution &#123;</div><div class="line">    //定义两个栈</div><div class="line">    Stack&lt;Integer&gt; stack1 = new Stack&lt;Integer&gt;();</div><div class="line">    Stack&lt;Integer&gt; stack2 = new Stack&lt;Integer&gt;();</div><div class="line">    //进队都往第一个栈进。</div><div class="line">    public void push(int node) &#123;</div><div class="line">        stack1.push(node);</div><div class="line">    &#125;</div><div class="line">    //出队列函数</div><div class="line">    public int pop() &#123;</div><div class="line">        //如果栈1不为空，把栈1值导入栈2，完成逆序</div><div class="line">        while(!stack1.isEmpty())&#123;</div><div class="line">            stack2.push(stack1.pop());</div><div class="line">        &#125;</div><div class="line">        //栈2栈顶出队列</div><div class="line">        int first=stack2.pop();</div><div class="line">        //这里是重点：栈2的值都要倒回栈1，因为如果不倒回，出队列操作之后，栈2还有元素，但是之</div><div class="line">        //后进队列的元素又倒回栈2，这样就乱序了，倒回保证每次栈2中先进的都在下面，整体倒入栈2</div><div class="line">        //后肯定保证是先进的元素在栈顶</div><div class="line">        while(!stack2.isEmpty())&#123;</div><div class="line">            stack1.push(stack2.pop());</div><div class="line">        &#125;</div><div class="line">        //</div><div class="line">        return first;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="友情链接"><a href="#友情链接" class="headerlink" title="友情链接"></a>友情链接</h1><p>所有的剑指offer代码，我托管到GitHub上了，可以直接运行，对照博客理解思路。<a href="https://github.com/wustzoujing/Algorithm_Learning" target="_blank" rel="external">wustzoujing/Algorithm_Learning</a>。觉得还可以的麻烦fork一下。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;栈和队列是非常常见的数据结构。栈先进后出，队列先进先出，Java中有定义好的栈和队列数据结构。要熟悉源码。栈和队列也有很多的联系，本题就是用两个栈实现一个队列。&lt;/p&gt;
    
    </summary>
    
      <category term="剑指offer详解" scheme="http://yoursite.com/categories/%E5%89%91%E6%8C%87offer%E8%AF%A6%E8%A7%A3/"/>
    
    
      <category term="算法题" scheme="http://yoursite.com/tags/%E7%AE%97%E6%B3%95%E9%A2%98/"/>
    
  </entry>
  
  <entry>
    <title>JDK源码阅读（三）</title>
    <link href="http://yoursite.com/2017/03/06/JDK%E6%BA%90%E7%A0%81%E9%98%85%E8%AF%BB%EF%BC%88%E4%B8%89%EF%BC%89/"/>
    <id>http://yoursite.com/2017/03/06/JDK源码阅读（三）/</id>
    <published>2017-03-06T00:19:46.000Z</published>
    <updated>2017-05-22T12:12:01.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="JDK源码阅读（三）"><a href="#JDK源码阅读（三）" class="headerlink" title="JDK源码阅读（三）"></a>JDK源码阅读（三）</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>&#160; &#160; &#160; &#160;在学习map类之后，我们今天了解list接口相关的一些类。本文转自系列博文：<a href="http://blog.csdn.net/column/details/collection.html?page=1" target="_blank" rel="external">Java集合类剖析</a>。</p>
 <a id="more"></a>
<h2 id="ArrayList"><a href="#ArrayList" class="headerlink" title="ArrayList"></a>ArrayList</h2><p>&#160; &#160; &#160; &#160; ArrayList是基于数组实现的，是一个动态数组，其容量能自动增长，类似于C语言中的动态申请内存，动态增长内存。</p>
<p>&#160; &#160; &#160; &#160;ArrayList不是线程安全的，只能用在单线程环境下，多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类，也可以使用concurrent并发包下的CopyOnWriteArrayList类。</p>
<p>&#160; &#160; &#160; &#160;ArrayList实现了Serializable接口，因此它支持序列化，能够通过序列化传输，实现了RandomAccess接口，支持快速随机访问，实际上就是通过下标序号进行快速访问，实现了Cloneable接口，能被克隆。</p>
<h3 id="代码分析"><a href="#代码分析" class="headerlink" title="代码分析"></a>代码分析</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div><div class="line">75</div><div class="line">76</div><div class="line">77</div><div class="line">78</div><div class="line">79</div><div class="line">80</div><div class="line">81</div><div class="line">82</div><div class="line">83</div><div class="line">84</div><div class="line">85</div><div class="line">86</div><div class="line">87</div><div class="line">88</div><div class="line">89</div><div class="line">90</div><div class="line">91</div><div class="line">92</div><div class="line">93</div><div class="line">94</div><div class="line">95</div><div class="line">96</div><div class="line">97</div><div class="line">98</div><div class="line">99</div><div class="line">100</div><div class="line">101</div><div class="line">102</div><div class="line">103</div><div class="line">104</div><div class="line">105</div><div class="line">106</div><div class="line">107</div><div class="line">108</div><div class="line">109</div><div class="line">110</div><div class="line">111</div><div class="line">112</div><div class="line">113</div><div class="line">114</div><div class="line">115</div><div class="line">116</div><div class="line">117</div><div class="line">118</div><div class="line">119</div><div class="line">120</div><div class="line">121</div><div class="line">122</div><div class="line">123</div><div class="line">124</div><div class="line">125</div><div class="line">126</div><div class="line">127</div><div class="line">128</div><div class="line">129</div><div class="line">130</div><div class="line">131</div><div class="line">132</div><div class="line">133</div><div class="line">134</div><div class="line">135</div><div class="line">136</div><div class="line">137</div><div class="line">138</div><div class="line">139</div><div class="line">140</div><div class="line">141</div><div class="line">142</div><div class="line">143</div><div class="line">144</div><div class="line">145</div><div class="line">146</div><div class="line">147</div><div class="line">148</div><div class="line">149</div><div class="line">150</div><div class="line">151</div><div class="line">152</div><div class="line">153</div><div class="line">154</div><div class="line">155</div><div class="line">156</div><div class="line">157</div><div class="line">158</div><div class="line">159</div><div class="line">160</div><div class="line">161</div><div class="line">162</div><div class="line">163</div><div class="line">164</div><div class="line">165</div><div class="line">166</div><div class="line">167</div><div class="line">168</div><div class="line">169</div><div class="line">170</div><div class="line">171</div><div class="line">172</div><div class="line">173</div><div class="line">174</div><div class="line">175</div><div class="line">176</div><div class="line">177</div><div class="line">178</div><div class="line">179</div><div class="line">180</div><div class="line">181</div><div class="line">182</div><div class="line">183</div><div class="line">184</div><div class="line">185</div><div class="line">186</div><div class="line">187</div><div class="line">188</div><div class="line">189</div><div class="line">190</div><div class="line">191</div><div class="line">192</div><div class="line">193</div><div class="line">194</div><div class="line">195</div><div class="line">196</div><div class="line">197</div><div class="line">198</div><div class="line">199</div><div class="line">200</div><div class="line">201</div><div class="line">202</div><div class="line">203</div><div class="line">204</div><div class="line">205</div><div class="line">206</div><div class="line">207</div><div class="line">208</div><div class="line">209</div><div class="line">210</div><div class="line">211</div><div class="line">212</div><div class="line">213</div><div class="line">214</div><div class="line">215</div><div class="line">216</div><div class="line">217</div><div class="line">218</div><div class="line">219</div><div class="line">220</div><div class="line">221</div><div class="line">222</div><div class="line">223</div><div class="line">224</div><div class="line">225</div><div class="line">226</div><div class="line">227</div><div class="line">228</div><div class="line">229</div><div class="line">230</div><div class="line">231</div><div class="line">232</div><div class="line">233</div><div class="line">234</div><div class="line">235</div><div class="line">236</div><div class="line">237</div><div class="line">238</div><div class="line">239</div><div class="line">240</div><div class="line">241</div><div class="line">242</div><div class="line">243</div><div class="line">244</div><div class="line">245</div><div class="line">246</div><div class="line">247</div><div class="line">248</div><div class="line">249</div><div class="line">250</div><div class="line">251</div><div class="line">252</div><div class="line">253</div><div class="line">254</div><div class="line">255</div><div class="line">256</div><div class="line">257</div><div class="line">258</div><div class="line">259</div><div class="line">260</div><div class="line">261</div><div class="line">262</div><div class="line">263</div><div class="line">264</div><div class="line">265</div><div class="line">266</div><div class="line">267</div><div class="line">268</div><div class="line">269</div><div class="line">270</div><div class="line">271</div><div class="line">272</div><div class="line">273</div><div class="line">274</div><div class="line">275</div><div class="line">276</div><div class="line">277</div><div class="line">278</div><div class="line">279</div><div class="line">280</div><div class="line">281</div><div class="line">282</div><div class="line">283</div><div class="line">284</div><div class="line">285</div><div class="line">286</div><div class="line">287</div><div class="line">288</div><div class="line">289</div><div class="line">290</div><div class="line">291</div><div class="line">292</div><div class="line">293</div><div class="line">294</div><div class="line">295</div><div class="line">296</div><div class="line">297</div><div class="line">298</div><div class="line">299</div><div class="line">300</div><div class="line">301</div><div class="line">302</div><div class="line">303</div><div class="line">304</div><div class="line">305</div><div class="line">306</div><div class="line">307</div><div class="line">308</div><div class="line">309</div><div class="line">310</div><div class="line">311</div><div class="line">312</div><div class="line">313</div><div class="line">314</div><div class="line">315</div><div class="line">316</div><div class="line">317</div><div class="line">318</div><div class="line">319</div><div class="line">320</div><div class="line">321</div><div class="line">322</div><div class="line">323</div><div class="line">324</div><div class="line">325</div><div class="line">326</div><div class="line">327</div><div class="line">328</div><div class="line">329</div><div class="line">330</div><div class="line">331</div><div class="line">332</div><div class="line">333</div><div class="line">334</div><div class="line">335</div><div class="line">336</div><div class="line">337</div><div class="line">338</div><div class="line">339</div><div class="line">340</div><div class="line">341</div><div class="line">342</div><div class="line">343</div><div class="line">344</div><div class="line">345</div><div class="line">346</div><div class="line">347</div><div class="line">348</div><div class="line">349</div><div class="line">350</div><div class="line">351</div><div class="line">352</div><div class="line">353</div><div class="line">354</div><div class="line">355</div><div class="line">356</div><div class="line">357</div><div class="line">358</div><div class="line">359</div><div class="line">360</div><div class="line">361</div><div class="line">362</div><div class="line">363</div><div class="line">364</div><div class="line">365</div><div class="line">366</div><div class="line">367</div><div class="line">368</div></pre></td><td class="code"><pre><div class="line"></div><div class="line">package java.util;    </div><div class="line">   </div><div class="line">public class ArrayList&lt;E&gt; extends AbstractList&lt;E&gt;    </div><div class="line">        implements List&lt;E&gt;, RandomAccess, Cloneable, java.io.Serializable    </div><div class="line">&#123;    </div><div class="line">    // 序列版本号    </div><div class="line">    private static final long serialVersionUID = 8683452581122892189L;    </div><div class="line">   </div><div class="line">    // ArrayList基于该数组实现，用该数组保存数据   </div><div class="line">    private transient Object[] elementData;    </div><div class="line">   </div><div class="line">    // ArrayList中实际数据的数量    </div><div class="line">    private int size;    </div><div class="line">   </div><div class="line">    // ArrayList带容量大小的构造函数。    </div><div class="line">    public ArrayList(int initialCapacity) &#123;    </div><div class="line">        super();    </div><div class="line">        if (initialCapacity &lt; 0)    </div><div class="line">            throw new IllegalArgumentException(&quot;Illegal Capacity: &quot;+    </div><div class="line">                                               initialCapacity);    </div><div class="line">        // 新建一个数组    </div><div class="line">        this.elementData = new Object[initialCapacity];    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // ArrayList无参构造函数。默认容量是10。    </div><div class="line">    public ArrayList() &#123;    </div><div class="line">        this(10);    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 创建一个包含collection的ArrayList    </div><div class="line">    public ArrayList(Collection&lt;? extends E&gt; c) &#123;    </div><div class="line">        elementData = c.toArray();    </div><div class="line">        size = elementData.length;    </div><div class="line">        if (elementData.getClass() != Object[].class)    </div><div class="line">            elementData = Arrays.copyOf(elementData, size, Object[].class);    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">   </div><div class="line">    // 将当前容量值设为实际元素个数    </div><div class="line">    public void trimToSize() &#123;    </div><div class="line">        modCount++;    </div><div class="line">        int oldCapacity = elementData.length;    </div><div class="line">        if (size &lt; oldCapacity) &#123;    </div><div class="line">            elementData = Arrays.copyOf(elementData, size);    </div><div class="line">        &#125;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">   </div><div class="line">    // 确定ArrarList的容量。    </div><div class="line">    // 若ArrayList的容量不足以容纳当前的全部元素，设置 新的容量=“(原始容量x3)/2 + 1”    </div><div class="line">    public void ensureCapacity(int minCapacity) &#123;    </div><div class="line">        // 将“修改统计数”+1，该变量主要是用来实现fail-fast机制的    </div><div class="line">        modCount++;    </div><div class="line">        int oldCapacity = elementData.length;    </div><div class="line">        // 若当前容量不足以容纳当前的元素个数，设置 新的容量=“(原始容量x3)/2 + 1”    </div><div class="line">        if (minCapacity &gt; oldCapacity) &#123;    </div><div class="line">            Object oldData[] = elementData;    </div><div class="line">            int newCapacity = (oldCapacity * 3)/2 + 1;    </div><div class="line">            //如果还不够，则直接将minCapacity设置为当前容量  </div><div class="line">            if (newCapacity &lt; minCapacity)    </div><div class="line">                newCapacity = minCapacity;    </div><div class="line">            elementData = Arrays.copyOf(elementData, newCapacity);    </div><div class="line">        &#125;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 添加元素e    </div><div class="line">    public boolean add(E e) &#123;    </div><div class="line">        // 确定ArrayList的容量大小    </div><div class="line">        ensureCapacity(size + 1);  // Increments modCount!!    </div><div class="line">        // 添加e到ArrayList中    </div><div class="line">        elementData[size++] = e;    </div><div class="line">        return true;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 返回ArrayList的实际大小    </div><div class="line">    public int size() &#123;    </div><div class="line">        return size;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // ArrayList是否包含Object(o)    </div><div class="line">    public boolean contains(Object o) &#123;    </div><div class="line">        return indexOf(o) &gt;= 0;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    //返回ArrayList是否为空    </div><div class="line">    public boolean isEmpty() &#123;    </div><div class="line">        return size == 0;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 正向查找，返回元素的索引值    </div><div class="line">    public int indexOf(Object o) &#123;    </div><div class="line">        if (o == null) &#123;    </div><div class="line">            for (int i = 0; i &lt; size; i++)    </div><div class="line">            if (elementData[i]==null)    </div><div class="line">                return i;    </div><div class="line">            &#125; else &#123;    </div><div class="line">                for (int i = 0; i &lt; size; i++)    </div><div class="line">                if (o.equals(elementData[i]))    </div><div class="line">                    return i;    </div><div class="line">            &#125;    </div><div class="line">            return -1;    </div><div class="line">        &#125;    </div><div class="line">   </div><div class="line">        // 反向查找，返回元素的索引值    </div><div class="line">        public int lastIndexOf(Object o) &#123;    </div><div class="line">        if (o == null) &#123;    </div><div class="line">            for (int i = size-1; i &gt;= 0; i--)    </div><div class="line">            if (elementData[i]==null)    </div><div class="line">                return i;    </div><div class="line">        &#125; else &#123;    </div><div class="line">            for (int i = size-1; i &gt;= 0; i--)    </div><div class="line">            if (o.equals(elementData[i]))    </div><div class="line">                return i;    </div><div class="line">        &#125;    </div><div class="line">        return -1;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 反向查找(从数组末尾向开始查找)，返回元素(o)的索引值    </div><div class="line">    public int lastIndexOf(Object o) &#123;    </div><div class="line">        if (o == null) &#123;    </div><div class="line">            for (int i = size-1; i &gt;= 0; i--)    </div><div class="line">            if (elementData[i]==null)    </div><div class="line">                return i;    </div><div class="line">        &#125; else &#123;    </div><div class="line">            for (int i = size-1; i &gt;= 0; i--)    </div><div class="line">            if (o.equals(elementData[i]))    </div><div class="line">                return i;    </div><div class="line">        &#125;    </div><div class="line">        return -1;    </div><div class="line">    &#125;    </div><div class="line">     </div><div class="line">   </div><div class="line">    // 返回ArrayList的Object数组    </div><div class="line">    public Object[] toArray() &#123;    </div><div class="line">        return Arrays.copyOf(elementData, size);    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 返回ArrayList元素组成的数组  </div><div class="line">    public &lt;T&gt; T[] toArray(T[] a) &#123;    </div><div class="line">        // 若数组a的大小 &lt; ArrayList的元素个数；    </div><div class="line">        // 则新建一个T[]数组，数组大小是“ArrayList的元素个数”，并将“ArrayList”全部拷贝到新数组中    </div><div class="line">        if (a.length &lt; size)    </div><div class="line">            return (T[]) Arrays.copyOf(elementData, size, a.getClass());    </div><div class="line">   </div><div class="line">        // 若数组a的大小 &gt;= ArrayList的元素个数；    </div><div class="line">        // 则将ArrayList的全部元素都拷贝到数组a中。    </div><div class="line">        System.arraycopy(elementData, 0, a, 0, size);    </div><div class="line">        if (a.length &gt; size)    </div><div class="line">            a[size] = null;    </div><div class="line">        return a;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 获取index位置的元素值    </div><div class="line">    public E get(int index) &#123;    </div><div class="line">        RangeCheck(index);    </div><div class="line">   </div><div class="line">        return (E) elementData[index];    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 设置index位置的值为element    </div><div class="line">    public E set(int index, E element) &#123;    </div><div class="line">        RangeCheck(index);    </div><div class="line">   </div><div class="line">        E oldValue = (E) elementData[index];    </div><div class="line">        elementData[index] = element;    </div><div class="line">        return oldValue;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 将e添加到ArrayList中    </div><div class="line">    public boolean add(E e) &#123;    </div><div class="line">        ensureCapacity(size + 1);  // Increments modCount!!    </div><div class="line">        elementData[size++] = e;    </div><div class="line">        return true;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 将e添加到ArrayList的指定位置    </div><div class="line">    public void add(int index, E element) &#123;    </div><div class="line">        if (index &gt; size || index &lt; 0)    </div><div class="line">            throw new IndexOutOfBoundsException(    </div><div class="line">            &quot;Index: &quot;+index+&quot;, Size: &quot;+size);    </div><div class="line">   </div><div class="line">        ensureCapacity(size+1);  // Increments modCount!!    </div><div class="line">        System.arraycopy(elementData, index, elementData, index + 1,    </div><div class="line">             size - index);    </div><div class="line">        elementData[index] = element;    </div><div class="line">        size++;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 删除ArrayList指定位置的元素    </div><div class="line">    public E remove(int index) &#123;    </div><div class="line">        RangeCheck(index);    </div><div class="line">   </div><div class="line">        modCount++;    </div><div class="line">        E oldValue = (E) elementData[index];    </div><div class="line">   </div><div class="line">        int numMoved = size - index - 1;    </div><div class="line">        if (numMoved &gt; 0)    </div><div class="line">            System.arraycopy(elementData, index+1, elementData, index,    </div><div class="line">                 numMoved);    </div><div class="line">        elementData[--size] = null; // Let gc do its work    </div><div class="line">   </div><div class="line">        return oldValue;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 删除ArrayList的指定元素    </div><div class="line">    public boolean remove(Object o) &#123;    </div><div class="line">        if (o == null) &#123;    </div><div class="line">                for (int index = 0; index &lt; size; index++)    </div><div class="line">            if (elementData[index] == null) &#123;    </div><div class="line">                fastRemove(index);    </div><div class="line">                return true;    </div><div class="line">            &#125;    </div><div class="line">        &#125; else &#123;    </div><div class="line">            for (int index = 0; index &lt; size; index++)    </div><div class="line">            if (o.equals(elementData[index])) &#123;    </div><div class="line">                fastRemove(index);    </div><div class="line">                return true;    </div><div class="line">            &#125;    </div><div class="line">        &#125;    </div><div class="line">        return false;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">   </div><div class="line">    // 快速删除第index个元素    </div><div class="line">    private void fastRemove(int index) &#123;    </div><div class="line">        modCount++;    </div><div class="line">        int numMoved = size - index - 1;    </div><div class="line">        // 从&quot;index+1&quot;开始，用后面的元素替换前面的元素。    </div><div class="line">        if (numMoved &gt; 0)    </div><div class="line">            System.arraycopy(elementData, index+1, elementData, index,    </div><div class="line">                             numMoved);    </div><div class="line">        // 将最后一个元素设为null    </div><div class="line">        elementData[--size] = null; // Let gc do its work    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 删除元素    </div><div class="line">    public boolean remove(Object o) &#123;    </div><div class="line">        if (o == null) &#123;    </div><div class="line">            for (int index = 0; index &lt; size; index++)    </div><div class="line">            if (elementData[index] == null) &#123;    </div><div class="line">                fastRemove(index);    </div><div class="line">            return true;    </div><div class="line">            &#125;    </div><div class="line">        &#125; else &#123;    </div><div class="line">            // 便利ArrayList，找到“元素o”，则删除，并返回true。    </div><div class="line">            for (int index = 0; index &lt; size; index++)    </div><div class="line">            if (o.equals(elementData[index])) &#123;    </div><div class="line">                fastRemove(index);    </div><div class="line">            return true;    </div><div class="line">            &#125;    </div><div class="line">        &#125;    </div><div class="line">        return false;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 清空ArrayList，将全部的元素设为null    </div><div class="line">    public void clear() &#123;    </div><div class="line">        modCount++;    </div><div class="line">   </div><div class="line">        for (int i = 0; i &lt; size; i++)    </div><div class="line">            elementData[i] = null;    </div><div class="line">   </div><div class="line">        size = 0;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 将集合c追加到ArrayList中    </div><div class="line">    public boolean addAll(Collection&lt;? extends E&gt; c) &#123;    </div><div class="line">        Object[] a = c.toArray();    </div><div class="line">        int numNew = a.length;    </div><div class="line">        ensureCapacity(size + numNew);  // Increments modCount    </div><div class="line">        System.arraycopy(a, 0, elementData, size, numNew);    </div><div class="line">        size += numNew;    </div><div class="line">        return numNew != 0;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 从index位置开始，将集合c添加到ArrayList    </div><div class="line">    public boolean addAll(int index, Collection&lt;? extends E&gt; c) &#123;    </div><div class="line">        if (index &gt; size || index &lt; 0)    </div><div class="line">            throw new IndexOutOfBoundsException(    </div><div class="line">            &quot;Index: &quot; + index + &quot;, Size: &quot; + size);    </div><div class="line">   </div><div class="line">        Object[] a = c.toArray();    </div><div class="line">        int numNew = a.length;    </div><div class="line">        ensureCapacity(size + numNew);  // Increments modCount    </div><div class="line">   </div><div class="line">        int numMoved = size - index;    </div><div class="line">        if (numMoved &gt; 0)    </div><div class="line">            System.arraycopy(elementData, index, elementData, index + numNew,    </div><div class="line">                 numMoved);    </div><div class="line">   </div><div class="line">        System.arraycopy(a, 0, elementData, index, numNew);    </div><div class="line">        size += numNew;    </div><div class="line">        return numNew != 0;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    // 删除fromIndex到toIndex之间的全部元素。    </div><div class="line">    protected void removeRange(int fromIndex, int toIndex) &#123;    </div><div class="line">    modCount++;    </div><div class="line">    int numMoved = size - toIndex;    </div><div class="line">        System.arraycopy(elementData, toIndex, elementData, fromIndex,    </div><div class="line">                         numMoved);    </div><div class="line">   </div><div class="line">    // Let gc do its work    </div><div class="line">    int newSize = size - (toIndex-fromIndex);    </div><div class="line">    while (size != newSize)    </div><div class="line">        elementData[--size] = null;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">    private void RangeCheck(int index) &#123;    </div><div class="line">    if (index &gt;= size)    </div><div class="line">        throw new IndexOutOfBoundsException(    </div><div class="line">        &quot;Index: &quot;+index+&quot;, Size: &quot;+size);    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">   </div><div class="line">    // 克隆函数    </div><div class="line">    public Object clone() &#123;    </div><div class="line">        try &#123;    </div><div class="line">            ArrayList&lt;E&gt; v = (ArrayList&lt;E&gt;) super.clone();    </div><div class="line">            // 将当前ArrayList的全部元素拷贝到v中    </div><div class="line">            v.elementData = Arrays.copyOf(elementData, size);    </div><div class="line">            v.modCount = 0;    </div><div class="line">            return v;    </div><div class="line">        &#125; catch (CloneNotSupportedException e) &#123;    </div><div class="line">            // this shouldn&apos;t happen, since we are Cloneable    </div><div class="line">            throw new InternalError();    </div><div class="line">        &#125;    </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">   </div><div class="line">    // java.io.Serializable的写入函数    </div><div class="line">    // 将ArrayList的“容量，所有的元素值”都写入到输出流中    </div><div class="line">    private void writeObject(java.io.ObjectOutputStream s)    </div><div class="line">        throws java.io.IOException&#123;    </div><div class="line">    // Write out element count, and any hidden stuff    </div><div class="line">    int expectedModCount = modCount;    </div><div class="line">    s.defaultWriteObject();    </div><div class="line">   </div><div class="line">        // 写入“数组的容量”    </div><div class="line">        s.writeInt(elementData.length);    </div><div class="line">   </div><div class="line">    // 写入“数组的每一个元素”    </div><div class="line">    for (int i=0; i&lt;size; i++)    </div><div class="line">            s.writeObject(elementData[i]);    </div><div class="line">   </div><div class="line">    if (modCount != expectedModCount) &#123;    </div><div class="line">            throw new ConcurrentModificationException();    </div><div class="line">        &#125;    </div><div class="line">   </div><div class="line">    &#125;    </div><div class="line">   </div><div class="line">   </div><div class="line">    // java.io.Serializable的读取函数：根据写入方式读出    </div><div class="line">    // 先将ArrayList的“容量”读出，然后将“所有的元素值”读出    </div><div class="line">    private void readObject(java.io.ObjectInputStream s)    </div><div class="line">        throws java.io.IOException, ClassNotFoundException &#123;    </div><div class="line">        // Read in size, and any hidden stuff    </div><div class="line">        s.defaultReadObject();    </div><div class="line">   </div><div class="line">        // 从输入流中读取ArrayList的“容量”    </div><div class="line">        int arrayLength = s.readInt();    </div><div class="line">        Object[] a = elementData = new Object[arrayLength];    </div><div class="line">   </div><div class="line">        // 从输入流中将“所有的元素值”读出    </div><div class="line">        for (int i=0; i&lt;size; i++)    </div><div class="line">            a[i] = s.readObject();    </div><div class="line">    &#125;    </div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><ol>
<li>注意trantient关键字，说明修饰变量不参与序列化。注意fail-fast和fail-safe的区别。参考博文：<a href="http://blog.csdn.net/ch717828/article/details/46892051" target="_blank" rel="external">fail-fast和fail-safe</a>。</li>
<li><p>ArrayList基于数组实现，可以通过下标索引直接查找到指定位置的元素，因此查找效率高，但每次插入或删除元素，就要大量地移动元素，插入删除元素的效率低。</p>
</li>
<li><p>在查找给定元素索引值等的方法中，源码都将该元素的值分为null和不为null两种情况处理，ArrayList中允许元素为null。</p>
</li>
<li>注意其三个不同的构造方法。无参构造方法构造的ArrayList的容量默认为10，带有Collection参数的构造方法，将Collection转化为数组赋给ArrayList的实现数组elementData。</li>
<li>注意扩充容量的方法ensureCapacity。ArrayList在每次增加元素（可能是1个，也可能是一组）时，都要调用该方法来确保足够的容量。当容量不足以容纳当前的元素个数时，就设置新的容量为旧的容量的1.5倍加1，如果设置后的新容量还不够，则直接新容量设置为传入的参数（也就是所需的容量），而后用Arrays.copyof()方法将元素拷贝到新的数组（详见下面的第3点）。从中可以看出，当容量不够时，每次增加元素，都要将原来的元素拷贝到一个新的数组中，非常之耗时，也因此建议在事先能确定元素数量的情况下，才使用ArrayList，否则建议使用LinkedList。</li>
<li>注意arraylist和array的互相转化。toArray()和toArray(T[])。</li>
<li>ArrayList的实现中大量地调用了Arrays.copyof()和System.arraycopy()方法。我们有必要对这两个方法的实现做下深入的了解。首先来看Arrays.copyof()方法。它有很多个重载的方法，但实现思路都是一样的，我们来看泛型版本的源码：</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line">public static &lt;T&gt; T[] copyOf(T[] original, int newLength) &#123;  </div><div class="line">    return (T[]) copyOf(original, newLength, original.getClass());  </div><div class="line">&#125;  </div><div class="line"></div><div class="line">public static &lt;T,U&gt; T[] copyOf(U[] original, int newLength, Class&lt;? extends T[]&gt; newType) &#123;  </div><div class="line">    T[] copy = ((Object)newType == (Object)Object[].class)  </div><div class="line">        ? (T[]) new Object[newLength]  </div><div class="line">        : (T[]) Array.newInstance(newType.getComponentType(), newLength);  </div><div class="line">    System.arraycopy(original, 0, copy, 0,  </div><div class="line">                     Math.min(original.length, newLength));  </div><div class="line">    return copy;  </div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160; &#160; &#160; &#160;首先来看Arrays.copyof()方法。它有很多个重载的方法，但实现思路都是一样的,很明显调用了另一个copyof方法，该方法有三个参数，最后一个参数指明要转换的数据的类型可以很明显地看出，该方法实际上是在其内部又创建了一个长度为newlength的数组，调用System.arraycopy()方法，将原来数组中的元素复制到了新的数组中。</p>
<p>&#160; &#160; &#160; &#160;下面来看System.arraycopy()方法。该方法被标记了native，调用了系统的C/C++代码，在JDK中是看不到的，但在openJDK中可以看到其源码。该函数实际上最终调用了C语言的memmove()函数，因此它可以保证同一个数组内元素的正确复制和移动，比一般的复制方法的实现效率要高很多，很适合用来批量处理数组。Java强烈推荐在复制大量数组元素时用该方法，以取得更高的效率。</p>
<h2 id="LinkedList"><a href="#LinkedList" class="headerlink" title="LinkedList"></a>LinkedList</h2><p>&#160; &#160; &#160; &#160;LinkedList是基于双向循环链表（从源码中可以很容易看出）实现的，除了可以当做链表来操作外，它还可以当做栈、队列和双端队列来使用。LinkedList同样是非线程安全的，只在单线程下适合使用。LinkedList实现了Serializable接口，因此它支持序列化，能够通过序列化传输，实现了Cloneable接口，能被克隆。</p>
<p>&#160; &#160; &#160; &#160;源码解析地址，参考博文：<a href="http://blog.csdn.net/ns_code/article/details/35787253" target="_blank" rel="external">LinkedList源码剖析</a>.</p>
<p>&#160; &#160; &#160; &#160;list集合中还有vector和stack。</p>
<h1 id="说明"><a href="#说明" class="headerlink" title="说明"></a>说明</h1><p>&#160; &#160; &#160; &#160;文中出现的图片，文字描述有些来自互联网，但是出处无法考究，如果侵犯您的相关权益，请联系我，核实后我会马上加上转载说明。谢谢！！！</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;JDK源码阅读（三）&quot;&gt;&lt;a href=&quot;#JDK源码阅读（三）&quot; class=&quot;headerlink&quot; title=&quot;JDK源码阅读（三）&quot;&gt;&lt;/a&gt;JDK源码阅读（三）&lt;/h1&gt;&lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;在学习map类之后，我们今天了解list接口相关的一些类。本文转自系列博文：&lt;a href=&quot;http://blog.csdn.net/column/details/collection.html?page=1&quot;&gt;Java集合类剖析&lt;/a&gt;。&lt;/p&gt;
    
    </summary>
    
      <category term="JDK源码解析" scheme="http://yoursite.com/categories/JDK%E6%BA%90%E7%A0%81%E8%A7%A3%E6%9E%90/"/>
    
    
      <category term="JDK集合类源码" scheme="http://yoursite.com/tags/JDK%E9%9B%86%E5%90%88%E7%B1%BB%E6%BA%90%E7%A0%81/"/>
    
  </entry>
  
  <entry>
    <title>剑指offer：重建二叉树</title>
    <link href="http://yoursite.com/2017/03/05/%E5%89%91%E6%8C%87offer%EF%BC%9A%E9%87%8D%E5%BB%BA%E4%BA%8C%E5%8F%89%E6%A0%91/"/>
    <id>http://yoursite.com/2017/03/05/剑指offer：重建二叉树/</id>
    <published>2017-03-05T09:16:00.000Z</published>
    <updated>2017-05-15T09:59:35.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>&#160; &#160; &#160; &#160;树也是一种重要的数据结构，它的逻辑是：除了根节点之外每个节点都只有一个父节点，根节点没有父节点，除了叶节点之外所有的节点都有一个或多个字节点。我们经常用到的是二叉树（还包括一些特殊的二叉树：二叉搜索树、大小根堆、红黑树等使用也广泛），它的遍历方式有前序遍历、中序遍历、后序遍历、宽度优先遍历。知道中序遍历和前后遍历任意其他一个，我们就能重建二叉树，这题就是已经前中序要求重建二叉树。</p>
  <a id="more"></a>
<h1 id="重建二叉树"><a href="#重建二叉树" class="headerlink" title="重建二叉树"></a>重建二叉树</h1><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><p>&#160; &#160; &#160; &#160;输入某二叉树的前序遍历和中序遍历的结果，请重建出该二叉树。假设输入的前序遍历和中序遍历的结果中都不含重复的数字。例如输入前序遍历序列{1,2,4,7,3,5,6,8}和中序遍历序列{4,7,2,1,5,3,8,6}，则重建二叉树并返回。</p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>&#160; &#160; &#160; &#160;我们知道在二叉树的前序遍历中，第一个总是根值。中序遍历中，根节点的值在序列的中间。左子树的根节点位于根节点的左边，右子树的结点在根节点的右边，因此我们需要扫描中序遍历才能找到根节点的值。找到根结点之后，中序遍历分成两部分，分别是左右子树的中序遍历，而我们在前序遍历中根据左右子树的个数就能确定左右子树的前序遍历，因此我们就能递归的完成。具体步骤如下：</p>
<ol>
<li>先进行输入序列的检查。</li>
<li>把中序序列放入一个hashmap，值为key，索引为value，这样很方便的通过前序序列中的值得到在中序遍历中的索引，很巧妙，而且时间复杂度较低。</li>
<li>利用递归完成树重建，为了代码的简洁性，写了preIn函数完成功能。具体输入参数和意义如下注释部分。</li>
</ol>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div></pre></td><td class="code"><pre><div class="line">public TreeNode reConstructBinaryTree(int [] pre,int [] in) &#123;</div><div class="line">    //为空则返回空，输入检查，还可检查两者长度是否相等。</div><div class="line">    if(pre==null||in==null)&#123;</div><div class="line">          return null;</div><div class="line">    &#125;   </div><div class="line">    java.util.HashMap&lt;Integer,Integer&gt; map=new java.util.HashMap&lt;Integer,Integer&gt;();</div><div class="line">    //中序遍历进map</div><div class="line">    for(int i=0;i&lt;in.length;i++)&#123;</div><div class="line">       map.put(in[i],i); </div><div class="line">    &#125;</div><div class="line">    //交给重建函数</div><div class="line">    return preIn(pre,0,pre.length-1,in,0,in.length-1,map);</div><div class="line">&#125;</div><div class="line">//p，n，map属于不变的，这样写代码简洁一点，变得是前序遍历和中序遍历在原序列中的起止索引。</div><div class="line">public TreeNode preIn(int[] p,int pi,int pj,int[] n,int ni,int nj,java.util.HashMap&lt;Integer,Integer&gt; map)&#123;</div><div class="line">    //前序遍历开始位置大于结束位置，返回空。</div><div class="line">    if(pi&gt;pj)&#123;</div><div class="line">        return null;</div><div class="line">    &#125;</div><div class="line">    //前序遍历开始位置就是根节点。</div><div class="line">    TreeNode head =new TreeNode(p[pi]);</div><div class="line">    //获得根节点在中序序列中的索引位置</div><div class="line">    int index=map.get(p[pi]);</div><div class="line">    //递归左子树，前序遍历pi自增1就行，结束位置算法是（pi+1）+（index-1-ni），因为</div><div class="line">    //index-1为中序遍历结束位置减去ni开始位置就是中序遍历的个数，在加上pi+1就是前序遍历</div><div class="line">    //结束为止</div><div class="line">    head.left=preIn(p,pi+1,pi+index-ni,n,ni,index-1,map);</div><div class="line">    //右子树的前序开始位置为左子树结束位置自增1，结束位置是pj，中序遍历开始是根节点位置自</div><div class="line">    //增1，结束位置是nj</div><div class="line">    head.right=preIn(p,pi+index-ni+1,pj,n,index+1,nj,map);</div><div class="line">    //重建完成返回</div><div class="line">    return head;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="友情链接"><a href="#友情链接" class="headerlink" title="友情链接"></a>友情链接</h1><p>所有的剑指offer代码，我托管到GitHub上了，可以直接运行，对照博客理解思路。<a href="https://github.com/wustzoujing/Algorithm_Learning" target="_blank" rel="external">wustzoujing/Algorithm_Learning</a>。觉得还可以的麻烦fork一下。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;树也是一种重要的数据结构，它的逻辑是：除了根节点之外每个节点都只有一个父节点，根节点没有父节点，除了叶节点之外所有的节点都有一个或多个字节点。我们经常用到的是二叉树（还包括一些特殊的二叉树：二叉搜索树、大小根堆、红黑树等使用也广泛），它的遍历方式有前序遍历、中序遍历、后序遍历、宽度优先遍历。知道中序遍历和前后遍历任意其他一个，我们就能重建二叉树，这题就是已经前中序要求重建二叉树。&lt;/p&gt;
    
    </summary>
    
      <category term="剑指offer详解" scheme="http://yoursite.com/categories/%E5%89%91%E6%8C%87offer%E8%AF%A6%E8%A7%A3/"/>
    
    
      <category term="算法题" scheme="http://yoursite.com/tags/%E7%AE%97%E6%B3%95%E9%A2%98/"/>
    
  </entry>
  
  <entry>
    <title>剑指offer：从尾到头打印链表</title>
    <link href="http://yoursite.com/2017/03/04/%E5%89%91%E6%8C%87offer%EF%BC%9A%E4%BB%8E%E5%B0%BE%E5%88%B0%E5%A4%B4%E6%89%93%E5%8D%B0%E9%93%BE%E8%A1%A8/"/>
    <id>http://yoursite.com/2017/03/04/剑指offer：从尾到头打印链表/</id>
    <published>2017-03-04T12:41:39.000Z</published>
    <updated>2017-05-15T09:59:22.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>&#160; &#160; &#160; &#160;链表是常见的数据结构，上面篇已经讲了字符串和数组，这题就有关链表，从数据结构中我们知道了链表的基本性质：链表是一种物理存储单元上非连续、非顺序的存储结构，数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点（链表中每一个元素称为结点）组成，结点可以在运行时动态生成。每个结点包括两个部分：一个是存储数据元素的数据域，另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构，操作复杂。由于不必须按顺序存储，链表在插入的时候可以达到O(1)的复杂度，比另一种线性表顺序表快得多，但是查找一个节点或者访问特定编号的节点则需要O(n)的时间，而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。</p>
  <a id="more"></a>
<p>&#160; &#160; &#160; &#160;补充说明：使用链表结构可以克服数组链表需要预先知道数据大小的缺点，链表结构可以充分利用计算机内存空间，实现灵活的内存动态管理。但是链表失去了数组随机读取的优点，同时链表由于增加了结点的指针域，空间开销比较大。链表最明显的好处就是，常规数组排列关联项目的方式可能不同于这些数据项目在记忆体或磁盘上顺序，数据的存取往往要在不同的排列顺序中转换。链表允许插入和移除表上任意位置上的节点，但是不允许随机存取。链表有很多种不同的类型：单向链表，双向链表以及循环链表。在Java中我们用自定义类来表示链表，本题就给出了链表默认类。—-源自<em>百度百科</em>，觉得讲的很清楚。</p>
<h1 id="从尾到头打印链表"><a href="#从尾到头打印链表" class="headerlink" title="从尾到头打印链表"></a>从尾到头打印链表</h1><h2 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h2><p>&#160; &#160; &#160; &#160;输入一个链表，从尾到头打印链表每个节点的值。</p>
<h2 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h2><p>&#160; &#160; &#160; &#160;从头到尾输出链表比较简单，因为是题目要求是打印且是从尾到头，打印一般不允许我们改原链表结构的，所以反向链表在从头打印不行的，我们有两个思路：</p>
<ol>
<li>考虑用递归来实现，链表最后一个节点的next节点为空，这也是我们递归结束条件。如果链表当前节点的next不为空，我们递归先打印它的next节点，但是递归都怕栈溢出。</li>
</ol>
<ol>
<li>考虑到这个类似先进后出，可用数据结构栈来实现这种顺序,但是没有递归简洁，而且先要遍历一遍存入栈，然后在出栈。</li>
</ol>
<h2 id="实现"><a href="#实现" class="headerlink" title="实现"></a>实现</h2><h3 id="递归实现"><a href="#递归实现" class="headerlink" title="递归实现"></a>递归实现</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line"> import java.util.ArrayList;</div><div class="line"> public class Solution &#123;</div><div class="line"> </div><div class="line">    ArrayList&lt;Integer&gt; arrayList=new ArrayList&lt;Integer&gt;();</div><div class="line">    public ArrayList&lt;Integer&gt; printListFromTailToHead(ListNode listNode) &#123;</div><div class="line">        //递归结束条件  </div><div class="line">        if(listNode!=null)&#123;</div><div class="line">            //递归打印它的next数组</div><div class="line">            this.printListFromTailToHead(listNode.next);</div><div class="line">            arrayList.add(listNode.val);</div><div class="line">        &#125;</div><div class="line">        return arrayList;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="栈实现"><a href="#栈实现" class="headerlink" title="栈实现"></a>栈实现</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div></pre></td><td class="code"><pre><div class="line">import java.util.ArrayList;</div><div class="line">import java.util.Stack;</div><div class="line">public class Solution &#123;</div><div class="line">    </div><div class="line">    public ArrayList&lt;Integer&gt; printListFromTailToHead(ListNode listNode) &#123;</div><div class="line">        ArrayList&lt;Integer&gt; arrayList=new ArrayList&lt;Integer&gt;();</div><div class="line">        Stack&lt;Integer&gt; s=new Stack&lt;Integer&gt;();</div><div class="line">        //链表依次进栈</div><div class="line">        while(listNode!=null)&#123;</div><div class="line">            s.push(listNode.val);</div><div class="line">            listNode=listNode.next;</div><div class="line">        &#125;</div><div class="line">        //依次出栈到输出动态数组完成逆序打印</div><div class="line">        while(!s.empty())&#123;</div><div class="line">            arrayList.add(s.pop());</div><div class="line">        &#125;</div><div class="line">        return arrayList;</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h1 id="友情链接"><a href="#友情链接" class="headerlink" title="友情链接"></a>友情链接</h1><p>所有的剑指offer代码，我托管到GitHub上了，可以直接运行，对照博客理解思路。<a href="https://github.com/wustzoujing/Algorithm_Learning" target="_blank" rel="external">wustzoujing/Algorithm_Learning</a>。觉得还可以的麻烦fork一下。</p>
]]></content>
    
    <summary type="html">
    
      &lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h1&gt;&lt;p&gt;&amp;#160; &amp;#160; &amp;#160; &amp;#160;链表是常见的数据结构，上面篇已经讲了字符串和数组，这题就有关链表，从数据结构中我们知道了链表的基本性质：链表是一种物理存储单元上非连续、非顺序的存储结构，数据元素的逻辑顺序是通过链表中的指针链接次序实现的。链表由一系列结点（链表中每一个元素称为结点）组成，结点可以在运行时动态生成。每个结点包括两个部分：一个是存储数据元素的数据域，另一个是存储下一个结点地址的指针域。 相比于线性表顺序结构，操作复杂。由于不必须按顺序存储，链表在插入的时候可以达到O(1)的复杂度，比另一种线性表顺序表快得多，但是查找一个节点或者访问特定编号的节点则需要O(n)的时间，而线性表和顺序表相应的时间复杂度分别是O(logn)和O(1)。&lt;/p&gt;
    
    </summary>
    
      <category term="剑指offer详解" scheme="http://yoursite.com/categories/%E5%89%91%E6%8C%87offer%E8%AF%A6%E8%A7%A3/"/>
    
    
      <category term="算法题" scheme="http://yoursite.com/tags/%E7%AE%97%E6%B3%95%E9%A2%98/"/>
    
  </entry>
  
</feed>
