<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>二十四的温馨小窝</title>
  
  
  <link href="http://example.com/atom.xml" rel="self"/>
  
  <link href="http://example.com/"/>
  <updated>2022-03-18T10:33:03.044Z</updated>
  <id>http://example.com/</id>
  
  <author>
    <name>K1bo24</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>【JavaWeb】</title>
    <link href="http://example.com/2022/03/01/JavaWeb/"/>
    <id>http://example.com/2022/03/01/JavaWeb/</id>
    <published>2022-03-01T08:36:54.101Z</published>
    <updated>2022-03-18T10:33:03.044Z</updated>
    
    <content type="html"><![CDATA[<h3 id="JavaWeb">JavaWeb</h3><h4 id="Tomcat">Tomcat</h4><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220301175409391.png" alt="image-20220301175409391" style="zoom: 80%;" /><ul><li><p>可以通过Tomcat/conf下的<strong>server.xml</strong>文件配置启动的<strong>端口号</strong></p><ul><li><p>Tomcat默认端口号为8080</p></li><li><p>mysql:  3306</p></li><li><p>http:  80</p></li><li><p>https:  442</p></li><li><pre><code class="language-xml">&lt;Connector port=&quot;8081&quot; protocol=&quot;HTTP/1.1&quot;          connectionTimeout=&quot;20000&quot;           redirectPort=&quot;8443&quot; /&gt;</code></pre></li></ul></li><li><p>可以配置<strong>主机的名称</strong></p><ul><li><p>默认的主机名为：localhost 或者是 127.0.0.1</p></li><li><pre><code class="language-xml">&lt;Host name=&quot;localhost&quot;  appBase=&quot;webapps&quot;            unpackWARs=&quot;true&quot; autoDeploy=&quot;true&quot;&gt;<figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">* ......</span><br><span class="line"></span><br><span class="line">&lt;&#x2F;br&gt;</span><br><span class="line"></span><br><span class="line">##### *网站是如何进行访问的*</span><br><span class="line"></span><br><span class="line">* 输入一个域名，点击回车访问；</span><br><span class="line">* 检查本机的hosts配置文件下有没有该域名的映射；</span><br><span class="line">  * 如果有，直接返回对应的ip地址；</span><br><span class="line">  * 如果没有，去DNS服务器中找对应的ip地址；</span><br><span class="line">    * 如果找到，就返回对应的ip地址；</span><br><span class="line">    * 如果没有找到，就会报错；</span><br><span class="line"></span><br><span class="line">&lt;&#x2F;br&gt;</span><br><span class="line"></span><br><span class="line">##### *发布一个web网站*</span><br><span class="line"></span><br><span class="line">* 将自己写的网站放到服务器（Tomcat）中指定的web应用的文件夹下（webapps），就能进行访问了。</span><br><span class="line"></span><br><span class="line">~~~java</span><br><span class="line">--webapps : Tomcat服务器的web目录</span><br><span class="line">    -ROOT </span><br><span class="line">    -kibo24：网站的目录名</span><br><span class="line">    -index.html  默认的网站首页</span><br><span class="line">    -static</span><br><span class="line">    -css</span><br><span class="line">    -style.scc</span><br><span class="line">    -js</span><br><span class="line">    -img</span><br><span class="line">    - ...</span><br><span class="line">        -WEB-INF</span><br><span class="line">    -web.xml : 网站的配置文件</span><br><span class="line">            -lib : web应用依赖的jar包</span><br></pre></td></tr></table></figure></code></pre></li></ul></li></ul></br></br><h4 id="HTTP">HTTP</h4><h5 id="HTTP的内容">HTTP的内容</h5><p>HTTP（超文本传输协议）是一个简单的请求-响应协议，它通常运行在TCP之上。</p><ul><li>文本：html，字符串等</li><li>超文本：图片，音乐，视频等</li><li>端口：80（HTTPS的端口号为443）</li></ul></br><h5 id="HTTP的两个时代">HTTP的两个时代</h5><ul><li>http1.0  <strong>短连接</strong><ul><li>客户端可以与web服务器进行一次连接后，只能获得一个web资源，然后会断开连接</li></ul></li><li>http1.1  <strong>长链接</strong><ul><li>客户端可以与web服务器进行一次连接后，可以获得多个web资源</li></ul></li></ul></br><h5 id="HTTP请求">HTTP请求</h5><ul><li>客户端–&gt;发送请求<strong>Request</strong>–&gt;服务器</li></ul><p>百度：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Request URL: https:<span class="comment">//www.baidu.com/              【请求地址】</span></span><br><span class="line">Request Method: GET                              【请求方法get/post】</span><br><span class="line">Status Code: <span class="number">200</span> OK                              【状态码】</span><br><span class="line">Remote Address: <span class="number">39.156</span><span class="number">.66</span><span class="number">.18</span>:<span class="number">443</span>                 【远程地址】</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Accept: text/html</span><br><span class="line">Accept-Encoding: gzip, deflate, br               【压缩算法】</span><br><span class="line">Accept-Language: zh-CN,zh;q=<span class="number">0.9</span>                  【语言】</span><br><span class="line">Cache-Control: max-age=<span class="number">0</span></span><br><span class="line">Connection: keep-alive</span><br></pre></td></tr></table></figure><h6 id="请求行">请求行</h6><ul><li>请求行中的请求方式：GET</li><li>请求方式：<strong>GET</strong> / <strong>POST</strong> / HEAD / DELETE / PUT / TRACT,…<ul><li><strong>GET</strong>: 一次请求能够携带的参数比较少，大小有限制，会在浏览器的URL地址栏显示数据内容，<strong>不够安全，但是高效</strong></li><li><strong>POST</strong>: 一次请求能够携带的参数没有限制，大小没有限制，不会在浏览器的URL地址栏显示数据内容，<strong>不够高效，但是安全</strong></li></ul></li></ul><h6 id="消息头">消息头</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">Accept: 告知服务器支持的数据类型</span><br><span class="line">Accept-Encoding: 告知服务器支持的数据压缩格式</span><br><span class="line">Accept-Language: 告知浏览器语言环境</span><br><span class="line">Cache-Control: 缓存控制</span><br><span class="line">Connection: 告知服务器，请求完成后是断开还是保持连接</span><br><span class="line">HOST: 主机</span><br></pre></td></tr></table></figure></br><h5 id="HTTP响应">HTTP响应</h5><ul><li>服务器–&gt;响应–&gt;客户端</li></ul><p>百度:</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Cache-Control: <span class="keyword">private</span>                           【缓存控制】</span><br><span class="line">Connection: keep-alive                           【连接：保持连接】</span><br><span class="line">Content-Encoding: gzip （一种网站数据压缩技术）      【编码】</span><br><span class="line">Content-Type: text/html;charset=utf-<span class="number">8</span>            【类型】</span><br></pre></td></tr></table></figure><h6 id="响应体">响应体</h6><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">Accept: 告知服务器支持的数据类型</span><br><span class="line">Accept-Encoding: 告知服务器支持的数据压缩格式</span><br><span class="line">Accept-Language: 告知浏览器语言环境</span><br><span class="line">Cache-Control: 缓存控制</span><br><span class="line">Connection: 告知服务器，请求完成后是断开还是保持连接</span><br><span class="line">HOST: 主机</span><br><span class="line">Refresh: 告知客户端多久刷新一次</span><br><span class="line">Location: 让网页重新定位</span><br></pre></td></tr></table></figure><h6 id="响应状态码">响应状态码</h6><p>200: 请求响应成功</p><p>3**: 请求重定向</p><ul><li>重定向：通过各种方法将各种网络请求重新定个方向转到其它位置</li></ul><p>404: 找不到资源</p><ul><li>资源不存在</li></ul><p>5**: 服务器代码错误</p><ul><li>502：网关错误</li></ul></br></br><h5 id="在浏览器输入网址后，从点击回车到网页展示，都发生了什么事？"><em>在浏览器输入网址后，从点击回车到网页展示，都发生了什么事？</em></h5><ul><li>输入地址</li><li>浏览器查找<strong>域名的ip地址</strong><ul><li>浏览器首先会从本地硬盘的<strong>hosts</strong>文件中查看是否存在这个域名，如果能匹配上则会<strong>直接使用hosts文件中的ip地址</strong></li><li>如果本地的<strong>hosts</strong>文件中<strong>没有匹配的域名ip时</strong>，浏览器会发送一个<strong>DNS请求</strong>到<strong>本地区的DNS服务器</strong></li><li><strong>本地DNS</strong>收到请求后，会查询<strong>缓存记录</strong>，如果查询到结果就<strong>把结果返回</strong>，如果没有查询到则向<strong>DNS根服务器递归查询</strong></li><li>如果<strong>DNS根目录</strong>都没有找到域名与ip的对应记录，<strong>根DNS服务器</strong>会告诉<strong>本地DNS</strong>服务器应该向<strong>对应的顶级域名解析服务器</strong>发起查询请求，如.com服务器</li><li><strong>本地DNS服务器</strong>向<strong>对应的顶级域名解析服务器</strong>发起<strong>查询请求</strong>，顶级域名解析服务器返回<strong>域名解析服务器的地址</strong>或返回<strong>域名不存在</strong></li><li>最后<strong>本地DNS服务器</strong>向<strong>域名的解析服务器</strong>发送请求，这时就能收到一个域名和ip地址的对应关系</li></ul></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/QQ%E5%9B%BE%E7%89%8720220302182113-gigapixel-scale-2_00x.png" alt="QQ图片20220302182113-gigapixel-scale-2_00x" style="zoom:50%;" /><ul><li><p>浏览器向web服务器发送一个HTTP请求</p><ul><li><p>浏览器<strong>获取到域名和ip的对应关系后</strong>，会以本机一个<strong>随机端口（1024~65535）<strong>向服务器的</strong>WEB程序（常用的有httpd，nginx等）<strong>的</strong>80端口</strong>发起TCP的连接请求</p></li><li><p>请求经过网络中间各种路由设备到达服务器端后，到达目标设备网卡进入到内核的TCP/IP协议栈开始识别该连接请求，解封包，一层一层剥开最终到达WEB程序，建立TCP/IP连接</p></li><li><p><strong>建立TCP/IP连接之后</strong>，就可以发起<strong>http</strong>请求</p><p>HTTP请求包含：</p><ul><li>请求方法URI协议/版本</li><li>请求头</li><li>请求正文</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220302183150427.png" alt="image-20220302183150427" style="zoom:67%;" /></li></ul></li><li><p>服务器处理请求</p></li><li><p>服务器返回一个<strong>HTTP响应</strong></p><p>服务器收到请求并处理请求后，会返回处理结果，也就是返回一个<strong>HTTP响应</strong></p><p>HTTP响应的构成：</p><ul><li>状态行</li><li>响应头</li><li>响应正文</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220302183332139.png" alt="image-20220302183332139" style="zoom:67%;" /></li><li><p>浏览器解析响应的内容并展示</p><ul><li><p>浏览器解析<strong>html文件</strong></p><p>浏览器在解析html文件时，会“自上而下”加载，并在加载过程中进行解析渲染</p></li><li><p>浏览器解析<strong>JS</strong></p><p>JS的解析是由浏览器中的JS解析引擎完成的</p></li><li><p>浏览器发送请求<strong>获取嵌入在HTML中的资源</strong></p><p>浏览器显示HTML时，会注意到需要获取其他地址内容的标签，浏览器会发送一个获取请求来重新获得这些文件</p></li></ul></li></ul></br><h4 id="Maven">Maven</h4><ul><li>在JavaWeb开发中，需要使用大量的jar包，需要手动导入。Maven能够自动导入和配置jar包</li></ul><h5 id="Maven项目架构管理工具">Maven项目架构管理工具</h5><p>我们目前用它是为了方便导入jar包</p><p><strong>Maven核心思想：约定大于配置</strong></p><ul><li><p>有约束，不要违反</p><p>Maven会规定好如何去编写java代码，必须要按照这个规范</p></li></ul></br><h5 id="配置Maven环境">配置Maven环境</h5><ul><li><p>M2_HOME    maven下的bin目录</p></li><li><p>MAVEN_HOME    maven的目录</p></li><li><p>Path中配置bin目录</p></li></ul><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303164927159.png" alt="image-20220303164927159"></p></br><h5 id="修改配置文件">修改配置文件</h5><ul><li><p>镜像：mirrors</p><p>加速文件的下载</p></li><li><p>国内建议使用阿里云的镜像</p></li></ul></br><h5 id="本地仓库">本地仓库</h5><p><strong>新建一个本地仓库</strong>：localRepository</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303172347901.png" alt="image-20220303172347901"></p></br><h5 id="在IDEA中使用Maven">在IDEA中使用Maven</h5><ul><li>创建一个MavenWeb项目</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303195304329.png" alt="image-20220303195304329" style="zoom:67%;" /><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303195439701.png" alt="image-20220303195439701" style="zoom:67%;" /><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303195845073.png" alt="image-20220303195845073" style="zoom:67%;" /><ul><li>等待项目初始化</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303200233590.png" alt="image-20220303200233590" style="zoom:67%;" /><ul><li>查看本地仓库</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303200425398.png" alt="image-20220303200425398" style="zoom:67%;" /><ul><li>IDEA中的Maven设置</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303200744026.png" alt="image-20220303200744026" style="zoom: 80%;" /><ul><li>如果不选择webapp，选择直接生成maven项目</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303201239257.png" alt="image-20220303201239257" style="zoom: 80%;" /><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303201355063.png" alt="image-20220303201355063"></p><ul><li>修改目录属性</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303201725777.png" alt="image-20220303201725777" style="zoom: 80%;" /><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303201952401.png" alt="image-20220303201952401" style="zoom:80%;" /><ul><li>在IDEA中配置Tomcat</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303203815217.png" alt="image-20220303203815217" style="zoom:80%;" /><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303204433816.png" alt="image-20220303204433816" style="zoom:80%;" /><ul><li>解决警告<strong>未指定应用程序服务器</strong></li></ul><p>​在配置中选中Tomcat路径即可</p><ul><li>解决警告<strong>没有为部署标记工件</strong></li></ul><p>​在部署中添加工件即可</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303204745883.png" alt="image-20220303204745883" style="zoom:80%;" /><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303204958918.png" alt="image-20220303204958918"></p><ul><li><p>启动Tomcat</p></li><li><p>运行项目</p></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220303205420892.png" alt="image-20220303205420892" style="zoom:80%;" /></br><h5 id="pom文件">pom文件</h5><p><strong>pom.xml</strong>文件是Maven的核心配置文件</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="comment">&lt;!--Maven版本和头文件--&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">project</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0&quot;</span> <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">  <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd&quot;</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">modelVersion</span>&gt;</span>4.0.0<span class="tag">&lt;/<span class="name">modelVersion</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--这就是刚才配置的GAV--&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.kibo24<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>JavaWeb-maven<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--项目的打包方式</span></span><br><span class="line"><span class="comment">  jar:java应用</span></span><br><span class="line"><span class="comment">  war:javaweb应用</span></span><br><span class="line"><span class="comment">  --&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">packaging</span>&gt;</span>war<span class="tag">&lt;/<span class="name">packaging</span>&gt;</span></span><br><span class="line">  <span class="comment">&lt;!--配置--&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">properties</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--项目的默认构建编码--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">project.build.sourceEncoding</span>&gt;</span>UTF-8<span class="tag">&lt;/<span class="name">project.build.sourceEncoding</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--编译版本--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">maven.compiler.source</span>&gt;</span>1.7<span class="tag">&lt;/<span class="name">maven.compiler.source</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">maven.compiler.target</span>&gt;</span>1.7<span class="tag">&lt;/<span class="name">maven.compiler.target</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">properties</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="comment">&lt;!--项目依赖--&gt;</span></span><br><span class="line">  <span class="tag">&lt;<span class="name">dependencies</span>&gt;</span></span><br><span class="line">    <span class="comment">&lt;!--具体依赖的jar包配置文件--&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">dependency</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>junit<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">version</span>&gt;</span>4.11<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">scope</span>&gt;</span>test<span class="tag">&lt;/<span class="name">scope</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">dependencies</span>&gt;</span></span><br><span class="line"></span><br><span class="line">  <span class="tag">&lt;<span class="name">build</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">finalName</span>&gt;</span>JavaWeb-maven<span class="tag">&lt;/<span class="name">finalName</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">pluginManagement</span>&gt;</span><span class="comment">&lt;!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --&gt;</span></span><br><span class="line">      <span class="tag">&lt;<span class="name">plugins</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-clean-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.1.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="comment">&lt;!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging --&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-resources-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.0.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-compiler-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.8.0<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-surefire-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.22.1<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-war-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">version</span>&gt;</span>3.2.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-install-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.5.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">plugin</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>maven-deploy-plugin<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">          <span class="tag">&lt;<span class="name">version</span>&gt;</span>2.8.2<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;/<span class="name">plugin</span>&gt;</span></span><br><span class="line">      <span class="tag">&lt;/<span class="name">plugins</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">pluginManagement</span>&gt;</span></span><br><span class="line">  <span class="tag">&lt;/<span class="name">build</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">project</span>&gt;</span></span><br></pre></td></tr></table></figure><p>Maven的高级之处在于：它会帮你导入这个jar包<strong>所依赖的其他jar包</strong></p></br><h5 id="Maven仓库的使用">Maven仓库的使用</h5><ul><li>地址：<a href="https://mvnrepository.com">https://mvnrepository.com</a></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306160647834.png" alt="image-20220306160647834" style="zoom:80%;" /><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306160906610.png" alt="image-20220306160906610"></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306161024634.png" alt="image-20220306161024634" style="zoom:67%;" /><ul><li><strong>依赖代码</strong>要添加到<strong>pom.xml</strong>里</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306161413160.png" alt="image-20220306161413160" style="zoom:80%;" /><ul><li>添加依赖成功，<strong>导入类</strong>后即可</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306161500298.png" alt="image-20220306161500298" style="zoom:80%;" /><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306174103426.png" alt="image-20220306174103426"></p><ul><li>展示效果</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306174157038.png" alt="image-20220306174157038" style="zoom:80%;" /></br></br><h4 id="Servlet">Servlet</h4><h5 id="Servlet简介">Servlet简介</h5><ul><li>Servlet是sun公司开发动态web的一门技术；</li><li>sun公司在这些API中提供了一个<strong>接口</strong>叫做Servlet，如果想开发Servlet程序，只需要完成两个小步骤：<ul><li>编写一个类，实现<strong>Servlet接口</strong></li><li>把开发好的Java类<strong>部署到web服务器</strong>中</li></ul></li><li>Servlet有两个实现类：<strong>HttpServlet</strong>，<strong>GenericServlet</strong><ul><li>关系为：<strong>Servlet接口–&gt;GenericServlet–&gt;HttpServlet–&gt;自己写的类（只需要继承HttpServlet类即可）</strong></li><li></li></ul></li></ul><p><em>把实现了Servlet接口的java程序叫做<strong>Servlet</strong></em></p></br><h5 id="Hello-Servlet">Hello Servlet</h5><ul><li><p>构建一个<strong>Maven项目</strong>，删掉里面的<strong>src目录</strong>，之后就在项目里创建<strong>模块</strong>。这个空工程就是<strong>Maven的主工程</strong>；</p><p>典中典之<strong>导入依赖</strong>，如果<strong>本地仓库</strong>没有导入的依赖，IDEA会负责下载。</p></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306180324106.png" alt="image-20220306180324106" style="zoom:80%;" /><ul><li><p>关于Maven的父子模块</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306181508413.png" alt="image-20220306181508413" style="zoom:80%;" /><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306181607955.png" alt="image-20220306181607955" style="zoom: 80%;" /><ul><li>父项目中会有：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">modules</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">module</span>&gt;</span>Servler-01<span class="tag">&lt;/<span class="name">module</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">modules</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>子项目中会有：</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">parent</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">artifactId</span>&gt;</span>javaweb-02-Servlet<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">groupId</span>&gt;</span>com.kibo24<span class="tag">&lt;/<span class="name">groupId</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">version</span>&gt;</span>1.0-SNAPSHOT<span class="tag">&lt;/<span class="name">version</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">parent</span>&gt;</span></span><br></pre></td></tr></table></figure><p>父项目中的jar包子项目可以直接使用，<strong>反之则不能</strong></p></li><li><p>Maven环境优化</p><ul><li><p>关于新建子项目（模块）的<strong>web.xml</strong>中初始代码老旧的问题：</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">web-app</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://xmlns.jcp.org/xml/ns/javaee&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://xmlns.jcp.org/xml/ns/javaee</span></span></span><br><span class="line"><span class="tag"><span class="string">                       http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">version</span>=<span class="string">&quot;4.0&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">metadata-complete</span>=<span class="string">&quot;true&quot;</span>&gt;</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="tag">&lt;/<span class="name">web-app</span>&gt;</span></span><br></pre></td></tr></table></figure></li><li><p>将子项目<strong>src/main目录</strong>下的的文件目录补齐，<strong>java标记为源，resources标记为资源</strong></p></li></ul></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306182531373.png" alt="image-20220306182531373" style="zoom:80%;" /><ul><li><p>编写一个Servlet程序</p><ul><li>创建一个<strong>普通类</strong>；</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306183115932.png" alt="image-20220306183115932" style="zoom: 80%;" /><ul><li>实现<strong>Servlet接口</strong>，这里直接继承<strong>HttpServlet类</strong>（不需要重新配置，直接使用父项目的包）</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">HelloServlet</span> <span class="keyword">extends</span> <span class="title">HttpServlet</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//由于get/post只是请求实现的不同方式，可以相互调用，因为业务逻辑都一样</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doGet</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//ServletOutputStream outputStream = resp.getOutputStream();</span></span><br><span class="line">        PrintWriter writer=resp.getWriter(); <span class="comment">//响应流</span></span><br><span class="line">        writer.println(<span class="string">&quot;Hello Servlet&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">doPost</span><span class="params">(HttpServletRequest req, HttpServletResponse resp)</span> <span class="keyword">throws</span> ServletException, IOException</span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        doGet(req, resp);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>编写<strong>Servlet的映射</strong></p><p>因为浏览器访问需要链接web服务器，所以需要把自己写的java程序，在web服务中注册Servlet（映射），还需要给它一个浏览器能访问的路径。</p><ul><li>在<strong>web.xml</strong>中映射</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306190056335.png" alt="image-20220306190056335" style="zoom:80%;" /></li><li><p>配置Tomcat</p></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306190253082.png" alt="image-20220306190253082" style="zoom:80%;" /><ul><li>测试项目</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220306190425581.png" alt="image-20220306190425581" style="zoom:80%;" /><h5 id="Servlet优先级问题">Servlet优先级问题</h5><ul><li><strong>通配符映射</strong>的优先级小于<strong>狭义定义的映射</strong>的优先级</li><li>如下代码（web.xml），会优先判断是不是<strong>hello</strong>映射</li></ul><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">servlet</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>hello<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-class</span>&gt;</span>com.kibo24.servlet.HelloServlet<span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>hello<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/hello<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line">------------------------------------------------------------------</span><br><span class="line">    </span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>error<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">            <span class="tag">&lt;<span class="name">servlet-class</span>&gt;</span>com.kibo24.servlet.ErrorServlet<span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>error<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">        <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/*<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure><h5 id="Servlet项目创建">Servlet项目创建</h5><ul><li>新建项目之后，配置好<strong>Maven</strong>环境</li><li>配置好<strong>Tomcat</strong>环境</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220318182538747.png" alt="image-20220318182538747" style="zoom: 80%;" /><ul><li>在项目下新建<strong>新模块</strong></li><li>修改新模块的<strong>pom.xml</strong>文件</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220318182821106.png" alt="image-20220318182821106" style="zoom:80%;" /><ul><li>在<strong>新模块/src/main</strong>下补全<strong>java</strong>目录和<strong>resources资源包</strong></li><li>修改新模块的<strong>web.xml</strong>文件</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220318182850722.png" alt="image-20220318182850722" style="zoom:80%;" /><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot;?&gt;</span></span><br><span class="line"><span class="tag">&lt;<span class="name">web-app</span> <span class="attr">xmlns</span>=<span class="string">&quot;http://xmlns.jcp.org/xml/ns/javaee&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xmlns:xsi</span>=<span class="string">&quot;http://www.w3.org/2001/XMLSchema-instance&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">xsi:schemaLocation</span>=<span class="string">&quot;http://xmlns.jcp.org/xml/ns/javaee</span></span></span><br><span class="line"><span class="tag"><span class="string">                       http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">version</span>=<span class="string">&quot;4.0&quot;</span></span></span><br><span class="line"><span class="tag">         <span class="attr">metadata-complete</span>=<span class="string">&quot;true&quot;</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">web-app</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>在<strong>java</strong>目录下新建软件包（如<strong>com.kibo24.servlet</strong>）</li><li>在<strong>java目录</strong>下新建类（如<strong>HelloServlet</strong>）</li><li>在<strong>类</strong>中重写<strong>doGet和doPost</strong>方法</li><li>在<strong>web.xml</strong>下配置<strong>Servlet映射</strong>，即<strong>servlet-name</strong>和<strong>servlet-mapping</strong></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20220318183222382.png" alt="image-20220318183222382" style="zoom:80%;" /><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">servlet</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>hello<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">servlet-class</span>&gt;</span>com.kibo24.servlet.HelloServlet<span class="tag">&lt;/<span class="name">servlet-class</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">servlet</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;<span class="name">servlet-mapping</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">servlet-name</span>&gt;</span>hello<span class="tag">&lt;/<span class="name">servlet-name</span>&gt;</span></span><br><span class="line">       <span class="tag">&lt;<span class="name">url-pattern</span>&gt;</span>/hello<span class="tag">&lt;/<span class="name">url-pattern</span>&gt;</span></span><br><span class="line">   <span class="tag">&lt;/<span class="name">servlet-mapping</span>&gt;</span></span><br></pre></td></tr></table></figure><ul><li>然后好像就可以启动测试了</li></ul>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;JavaWeb&quot;&gt;JavaWeb&lt;/h3&gt;
&lt;h4 id=&quot;Tomcat&quot;&gt;Tomcat&lt;/h4&gt;
&lt;img src=&quot;https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-202203011754</summary>
      
    
    
    
    <category term="Java" scheme="http://example.com/categories/Java/"/>
    
    
    <category term="Java" scheme="http://example.com/tags/Java/"/>
    
    <category term="JavaWeb" scheme="http://example.com/tags/JavaWeb/"/>
    
  </entry>
  
  <entry>
    <title>The Kaleidoscope Language（万花筒语言）——LLVM学习</title>
    <link href="http://example.com/2022/01/08/The%20Kaleidoscope%20Language/"/>
    <id>http://example.com/2022/01/08/The%20Kaleidoscope%20Language/</id>
    <published>2022-01-08T11:48:12.519Z</published>
    <updated>2022-01-08T11:47:50.554Z</updated>
    
    <content type="html"><![CDATA[<p>学习<strong>Kaleidoscope:Kaleidoscope Introduction and the Lexer（万花筒语言的简介和词法）</strong></p><span id="more"></span><h3 id="万花筒语言简介和词法">万花筒语言简介和词法</h3><h4 id="万花筒语言">万花筒语言</h4><p>万花筒语言中唯一的数据类型是<strong>64位浮点类型（也就是C语言中的double）</strong>，并且该语言不需要类型声明，例如下面的计算<strong>斐波那契数列</strong>的代码：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"># 计算第x个斐波那契数.</span><br><span class="line"><span class="meta"># def：定义</span></span><br><span class="line"><span class="function">def <span class="title">fib</span><span class="params">(x)</span></span></span><br><span class="line"><span class="function">  <span class="keyword">if</span> x &lt; 3 then</span></span><br><span class="line"><span class="function">    1</span></span><br><span class="line"><span class="function">  <span class="keyword">else</span></span></span><br><span class="line">    fib(x-1)+fib(x-2)</span><br><span class="line"></span><br><span class="line"># 这个表达式会输出第<span class="number">40</span>个斐波那契数</span><br><span class="line">fib(<span class="number">40</span>)</span><br></pre></td></tr></table></figure><p>允许Kaleidoscope调用标准库函数（LLVM JIT使这完全无关紧要）。这意味着您可以在使用之前使用’extern’关键字来定义函数（这对于相互递归函数也很有用）。例如：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">extern</span> <span class="title">sin</span><span class="params">(arg)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">extern</span> <span class="title">cos</span><span class="params">(arg)</span></span>;</span><br><span class="line"><span class="function"><span class="keyword">extern</span> <span class="title">atan2</span><span class="params">(arg1 arg2)</span></span>;</span><br><span class="line"><span class="built_in">atan2</span>(<span class="built_in">sin</span>(<span class="number">.4</span>), <span class="built_in">cos</span>(<span class="number">42</span>))</span><br></pre></td></tr></table></figure><h4 id="词法分析器-gettok">词法分析器 / gettok</h4><p>使用词法分析器分析万花筒语言</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//如果token是未知字符，词法分析器返回对应的0~255，否则词法分析器将返回其中一个已知的token</span></span><br><span class="line"><span class="class"><span class="keyword">enum</span> <span class="title">Token</span> &#123;</span></span><br><span class="line">  tok_eof = <span class="number">-1</span>,</span><br><span class="line">  <span class="comment">// commands</span></span><br><span class="line">  tok_def = <span class="number">-2</span>,</span><br><span class="line">  tok_extern = <span class="number">-3</span>,</span><br><span class="line">  <span class="comment">// primary</span></span><br><span class="line">  tok_identifier = <span class="number">-4</span>,</span><br><span class="line">  tok_number = <span class="number">-5</span>,</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">string</span> IdentifierStr; <span class="comment">// Filled in if tok_identifier</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">double</span> NumVal;             <span class="comment">// Filled in if tok_number</span></span><br></pre></td></tr></table></figure><p>词法分析器返回的每个标记将会是<strong>token的枚举值之一</strong>，或者它会是一个“未知”的字符，比如“+”，那么它将会作为<strong>ASCII值返回</strong>。</p><p>如果当前token是<strong>标识符</strong>，则string类型的全局变量<strong>IdentifierStr将会保存标识符的名称</strong>。</p><p>如果当前token是<strong>数字（如3.14）</strong>，则<strong>NumVal保存其值</strong>。（为了简单起见，这里使用了全局变量，实际上这并不是最佳选择）。</p><p>词法分析器的实际实现是一个名为gettok的函数，gettok函数。</p><p>gettok函数通过调用C语言的getchar函数从标准输入<strong>每次读取一个字符</strong>，在识别他们时会“吃掉”他们，并在<strong>LastChar中保存最后一个未处理的字符</strong>。</p><p>gettok函数要做的第一件事就是<strong>忽略token之间的空格</strong>，通过下面的循环实现这一点：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///gettok——从标准输入返回下一个token</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">gettok</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    static int LastChar=&#x27;&#x27;; //static类型，只初始化一次</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//跳过空白符</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="built_in">isspace</span>(LastChar))</span><br><span class="line">        LastChar=getchar();</span><br></pre></td></tr></table></figure><p>接下来gettok需要做的是识别标识符和特定的关键字比如“def”，通过下面的循环实现这一点</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="built_in">isalpha</span>(LastChar)) <span class="comment">//isalpha函数用于判断是不是字母(包含大小写)</span></span><br><span class="line">&#123;</span><br><span class="line">    IdentifierStr=LastChar;</span><br><span class="line">    <span class="keyword">while</span>(<span class="built_in">isalnum</span>((LastChar=getchar()))) <span class="comment">//isalnum函数用于判断是数字或字母</span></span><br><span class="line">        IdentifierStr+=LastChar;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//检测到关键字</span></span><br><span class="line">    <span class="keyword">if</span>(IdentifierStr == <span class="string">&quot;def&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> tok_def;</span><br><span class="line">    <span class="keyword">if</span>(IdentifierStr == <span class="string">&quot;extern&quot;</span>)</span><br><span class="line">        <span class="keyword">return</span> tok_extern;</span><br><span class="line">    <span class="comment">//其他关键字</span></span><br><span class="line">    <span class="keyword">return</span> tok_identifier;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>接下来处理数字相关：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="built_in">isdigit</span>(LastChar) || LastChar==<span class="string">&#x27;.&#x27;</span>) <span class="comment">//isdigit函数用于判断是不是十进制数字</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="comment">//用于存放数字串</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> NumStr;</span><br><span class="line">    <span class="keyword">do</span></span><br><span class="line">    &#123;</span><br><span class="line">        NumStr += LastChar;</span><br><span class="line">        LastChar = getChar();</span><br><span class="line">    &#125;<span class="keyword">while</span>(<span class="built_in">isdigit</span>(LastChar) || LastChar==<span class="string">&#x27;.&#x27;</span>);</span><br><span class="line"></span><br><span class="line">    Numval = strtod(NumStr.c_str(),<span class="number">0</span>); <span class="comment">//将string转化为double</span></span><br><span class="line">    <span class="keyword">return</span> tok_number;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上都是处理输入的非常简单的代码。从输入读取数值时，我们可以使用C语言的strtod函数将string类型转化为double类型，若要提高健壮性，则要进行数字检查（如1.23.456.7并不能和1.23一样处理），接下来是处理注释：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">    <span class="keyword">if</span>(LastChar==<span class="string">&#x27;#&#x27;</span>)  <span class="comment">//出现这个表示之后的一整行都是注释</span></span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">do</span></span><br><span class="line">        &#123;</span><br><span class="line">            LastChar=getchar();</span><br><span class="line">        &#125;<span class="keyword">while</span>(LastChar!=EOF &amp;&amp; LastChar!=<span class="string">&#x27;\n&#x27;</span> &amp;&amp; LastChar!=<span class="string">&#x27;\r&#x27;</span>);</span><br><span class="line">    </span><br><span class="line">        <span class="comment">//通过一路跳到行尾来处理注释，然后返回下一个token</span></span><br><span class="line">        <span class="keyword">if</span>(LastChar!=EOF)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">return</span> gettok();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span>(LastChar==EOF)  <span class="comment">//到达文件的末尾</span></span><br><span class="line">        <span class="keyword">return</span> tok_eof;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//否则，就返回这个字符的ASCII</span></span><br><span class="line">    <span class="keyword">int</span> ThisChar=LastChar;</span><br><span class="line">    LastChar=getChar();</span><br><span class="line">    <span class="keyword">return</span> ThisChar;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>至此，就已经有了万花筒语言的完整词法分析器。</p><h4 id="抽象语法树（AST）">抽象语法树（AST）</h4><h5 id="表达式">表达式</h5><h6 id="表达式-ExprAST-NumberExprAST">表达式 / ExprAST / NumberExprAST</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//所有表达式节点的基类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ExprAST</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="keyword">virtual</span> ~ExprAST()&#123;&#125;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">//数字抽象语法树——用于数字文字“1.0”的表达式类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NumberExprAST</span>:</span> <span class="keyword">public</span> ExprAST</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">double</span> Val;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    NumberExprAST(<span class="keyword">double</span> val) :val(Val)&#123;&#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>上面的代码显示了<strong>ExprAST基类</strong>的定义以及<strong>用于数字文本的一个子类</strong>的定义。</p><p>此代码的重要注意事项是NumberExprAST子类将数字文本的数值<strong>捕获为实例变量</strong>，便于编译器后续能够知道存储的数值是什么。</p><h6 id="变量表达式AST-VariableExprAST">变量表达式AST / VariableExprAST</h6><p>现在仅创建AST，因此现在没有写访问的方法（函数），以下是在万花筒语言的基本形式中使用的其他表达式的AST节点的定义：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//变量表达式抽象语法树——用于引用一个变量的表达式类，如“a”</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">VariableExprAST</span>:</span> <span class="keyword">public</span> ExprAST</span><br><span class="line">&#123;<span class="comment">//用于存储变量名</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> Name;</span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">//使用const保证传入的Name不被修改</span></span><br><span class="line">    VariableExprAST(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">string</span> &amp;Name): Name(Name)&#123;&#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><h6 id="二元运算符表达式AST-BinaryExprAST">二元运算符表达式AST / BinaryExprAST</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//二元运算符表达式抽象语法树——用于一个二元运算符的表达式类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BinaryExprAST</span>:</span> <span class="keyword">public</span> ExprAST</span><br><span class="line">&#123;</span><br><span class="line">    <span class="keyword">char</span> Op;</span><br><span class="line">    <span class="comment">//智能指针——一个仅能移动的类型，也可以自动释放内存。使用std::move函数将指针的所有权从一个unique_ptr转移到另一个unique_ptr</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; LHS,RHS;  <span class="comment">//左被运算符，右被运算符</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    <span class="comment">//将输入的Op给类内Op，将传入的智能指针转移到类内的智能指针</span></span><br><span class="line">    BinaryExprAST(<span class="keyword">char</span> op,<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; LHS,<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; RHS): Op(op),LHS(<span class="built_in">std</span>::move(LHS)),RHS(<span class="built_in">std</span>::move(RHS))&#123;&#125;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h6 id="函数调用表达式AST-CallExprAST">函数调用表达式AST / CallExprAST</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//函数调用表达式抽象语法树</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CallExprAST</span>:</span> <span class="keyword">public</span> ExprAST</span><br><span class="line">&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> Callee;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt;&gt; Args; <span class="comment">//用于存储函数参数列表的数组</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    CallExprAST(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">string</span> &amp;Callee,<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt;&gt; Args): Callee(Callee),Args(<span class="built_in">std</span>::move(Args))&#123;&#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p><strong>变量捕获变量名称，二元运算符捕获它们的操作符（如“+”），函数调用捕获函数名称和它的参数表达式的列表。这里没有讨论二元运算符的优先级和词法结构。</strong></p><h6 id="函数声明AST-PrototypeAST">函数声明AST / PrototypeAST</h6><p>对于我们的基本语言，以上这些就是我们定义的所有表达式的节点。<code>因为它没有条件控制流程，所以它不是图灵完备的。</code>接下来需要解决的是<strong>讨论函数接口的方法</strong>以及<strong>讨论函数本身的方法</strong>。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这个类表示函数的声明，捕获函数名及其参数列表（因此隐式表示函数接受的参数）</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PrototypeAST</span></span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> Name;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt; Args;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">    PrototypeAST(<span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">string</span> &amp;name,<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt; Args): Name(name),Args(<span class="built_in">std</span>::move(Args))&#123;&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="function"><span class="keyword">const</span> <span class="built_in">std</span>::<span class="built_in">string</span> &amp;<span class="title">getName</span><span class="params">()</span> <span class="keyword">const</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Name;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h6 id="函数定义AST-FunctionAST">函数定义AST / FunctionAST</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//这个类用于表示函数定义</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">FunctionAST</span></span></span><br><span class="line"><span class="class">&#123;</span>  </span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;PrototypeAST&gt; Proto; <span class="comment">//上方类的智能指针，存储函数【声明】</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; Body; <span class="comment">//存储函数体</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>: </span><br><span class="line">    FunctionAST(<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;Prototype&gt; Proto,<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; Body): Proto(<span class="built_in">std</span>::move(Proto)),Body(<span class="built_in">std</span>::move(Body))&#123;&#125;</span><br><span class="line"></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h5 id="解析基础">解析基础</h5><p>现在需要构建一个AST，需要定义<strong>解析器</strong>代码来构建它。这里的想法是我们要解析类似“x+y”（由<strong>词法分析器</strong>返回<strong>三个token</strong>）到AST中，可以通过这样的调用生成：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">auto</span> LHS = llvm::make_unique&lt;VariableExprAST&gt;(<span class="string">&quot;x&quot;</span>);  <span class="comment">//使用智能指针创建节点</span></span><br><span class="line"><span class="keyword">auto</span> RHS = llvm::make_unique&lt;VariableExprAST&gt;(<span class="string">&quot;y&quot;</span>);  <span class="comment">//使用智能指针创建节点</span></span><br><span class="line"><span class="keyword">auto</span> Result = <span class="built_in">std</span>::make_unique&lt;BinaryExprAST&gt;(<span class="string">&quot;+&quot;</span>,<span class="built_in">std</span>::move(LHS),<span class="built_in">std</span>::move(RHS));  <span class="comment">//使用智能指针创建节点</span></span><br></pre></td></tr></table></figure><p>首先需要定义一些基本的帮助程序：</p><h6 id="缓冲区-getNextToken">缓冲区 / getNextToken</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//提供一个简单的token缓冲区，CurTok是解析器正在查看的当前token</span></span><br><span class="line"><span class="comment">//getNextToken是从词法分析器读取下一个token，并用其来更新CurTok</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">int</span> CurTok;</span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">getNextToken</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> CurTok=gettok(); <span class="comment">//在上方【词法分析器】章节中定义的函数</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这样就在词法分析器周围实现了一个简单的token缓冲区，这允许我们在词法分析器返回时<strong>提前查看一个token</strong>，我们的<strong>解析器</strong>中的每个函数都假定<strong>CurTok是需要解析的当前标记</strong>。</p><h6 id="错误帮助-LogError">错误帮助 / LogError</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///这是用于错误处理的帮助函数</span></span><br><span class="line"><span class="function"><span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; <span class="title">LogError</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>*Str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    fprint(<span class="built_in">stderr</span>,<span class="string">&quot;LogError: %s\n&quot;</span>, Str); <span class="comment">//stderr：标准错误</span></span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//函数“原型”</span></span><br><span class="line"><span class="function"><span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;PrototypeAST&gt; <span class="title">LogErrorP</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span>* Str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    LogError(Str);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>LogError程序是我们的解析器用来处理错误的简单的辅助程序。</p><h5 id="基本表达式的解析">基本表达式的解析</h5><h6 id="数字表达式的解析-ParseNumberExpr">数字表达式的解析 / ParseNumberExpr</h6><p>接下来从<strong>数字文本</strong>开始，对于数字文本，有：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//numberexpr::=number</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; <span class="title">ParseNumberExpr</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123; <span class="comment">//可见上方对数字语法树的描述</span></span><br><span class="line">    <span class="keyword">auto</span> Result = llvm::make_unique&lt;NumberExprAST&gt;(NumVal); <span class="comment">//使用智能指针创建节点</span></span><br><span class="line">    getNextToken();  <span class="comment">//消耗掉数字</span></span><br><span class="line"><span class="keyword">return</span> <span class="built_in">std</span>::move(Result);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个程序希望在当前token是tok_number时被调用，它会获取当前数字的值，创建NumberExprAST子类的节点，然后将<strong>词法分析器前进到下一个token</strong>，最后返回。</p><h6 id="括号运算符的解析-ParseParenExpr">括号运算符的解析 / ParseParenExpr</h6><p>对于<strong>括号运算符</strong>，有：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// parenexpr ::= &#x27;(&#x27; expression &#x27;)&#x27;</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; <span class="title">ParseParenExpr</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    getNextToken();  <span class="comment">//“吃掉”左括号“（”</span></span><br><span class="line">    <span class="keyword">auto</span> V = ParseExpression(); <span class="comment">//解析表达式</span></span><br><span class="line">    <span class="keyword">if</span>(!V)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">nullptr</span>; <span class="comment">//如果解析表达式为空</span></span><br><span class="line">    <span class="keyword">if</span>(CurTok != <span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">return</span> LogError(<span class="string">&quot;expected &#x27;)&#x27;&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    getNextToken();  <span class="comment">//“吃掉”右括号“）”</span></span><br><span class="line">    <span class="keyword">return</span> V;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>这个函数通过<strong>递归使用</strong>PareExpression来完成【详见下方的<strong>解析表达式</strong>】。</p><p>括号不会导致AST节点本身的构造，括号最重要的作用是<strong>引导解析器并提供分组</strong>，解析器构造AST后，不需要括号。</p><h6 id="标识符表达式的解析-ParseIdentifierExpr">标识符表达式的解析 / ParseIdentifierExpr</h6><p>下面是用于处理<strong>变量引用</strong>和<strong>函数调用</strong>的程序：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// identifierexpr</span></span><br><span class="line"><span class="comment">/// ::= identifier</span></span><br><span class="line"><span class="comment">/// ::= identifier &#x27;(&#x27; expression* &#x27;)&#x27;</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; <span class="title">PraseIdentifierExpr</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> IdName = IdentifierStr;</span><br><span class="line">    </span><br><span class="line">    getNextToken();  <span class="comment">//“吃掉”标识符</span></span><br><span class="line">    <span class="keyword">if</span>(CurTok != <span class="string">&#x27;(&#x27;</span>) </span><br><span class="line">        <span class="keyword">return</span> llvm::make_unique&lt;VariableExprAST&gt;(IdName);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//函数调用</span></span><br><span class="line">    getNextToken();  <span class="comment">//吃掉左括号</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt;&gt; Args;</span><br><span class="line">    <span class="keyword">if</span>(CurTok!=<span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(<span class="keyword">auto</span> Arg = ParseExpression())</span><br><span class="line">                Args.push_back(<span class="built_in">std</span>::move(Arg));<span class="comment">//依次将解析表达式加入数组中</span></span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span>(CurTok==<span class="string">&#x27;)&#x27;</span>)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            </span><br><span class="line">            <span class="keyword">if</span>(CurTok!=<span class="string">&#x27;,&#x27;</span>)</span><br><span class="line">                <span class="keyword">return</span> LogError(<span class="string">&quot;Expected &#x27;)&#x27; or &#x27;,&#x27; in argument list&quot;</span>);</span><br><span class="line">            getNextToken();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//吃掉右括号</span></span><br><span class="line">    getNextToken();</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> llvm::make_unique&lt;CallExprAST&gt;(IdName,<span class="built_in">std</span>::move(Args));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>如果当前的token是tok_identifier，则希望调用这个函数。</p><h6 id="主表达式的解析-ParsePrimary">主表达式的解析 / ParsePrimary</h6><p>现在已经有了所有简答表达式的解析逻辑，可以定义一个辅助函数将它们组合成一个入口，称这类表达式为主要表达式。为了解析任意的<strong>主表达式</strong>，则需要确定它是什么类型的表达式：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// primary</span></span><br><span class="line"><span class="comment">///  ::= identifier</span></span><br><span class="line"><span class="comment">///  ::= numberexpr</span></span><br><span class="line"><span class="comment">///  ::= parenexpr</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; <span class="title">ParsePrimary</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">switch</span>(CurTok)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">default</span>:</span><br><span class="line">            <span class="keyword">return</span> LogError(<span class="string">&quot;unknown token when expecting an expression&quot;</span>);</span><br><span class="line">        <span class="keyword">case</span> tok_identifier:</span><br><span class="line">            <span class="keyword">return</span> ParseIdentifierExpr();</span><br><span class="line">        <span class="keyword">case</span> tok_number:</span><br><span class="line">            <span class="keyword">return</span> ParseNumberExpr();</span><br><span class="line">        <span class="keyword">case</span> <span class="string">&#x27;(&#x27;</span>:</span><br><span class="line">            <span class="keyword">return</span> ParseParenExpr();</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用这段程序就能通过预测来确定正在检查哪种表达式，然后使用函数调用对其进行解析。</p><h5 id="二元表达式的解析">二元表达式的解析</h5><p>二进制表达式很难解析，因为它们通常是模糊的。例如，当给定字符串“x + y <em>z”时，解析器可以选择将其解析为“（x + y）</em> z”或“x +（y *z）”。对于数学中的常见定义，我们期望后面的解析，因为<strong>乘法具有比加法更高的优先级</strong>。</p><h6 id="优先级表-GetTokPrecedence">优先级表 / GetTokPrecedence</h6><p>有很多方法可以解决这个问题，但优雅而有效的方法是使用Operator-Precedence Parsing。此解析技术使用二元运算符的优先级来指导递归。首先，我们需要一个<strong>优先级表</strong>：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///它保留已定义的每个二元运算符的优先级</span></span><br><span class="line"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">map</span>&lt;<span class="keyword">char</span>,<span class="keyword">int</span>&gt; BinopPrecedence;</span><br><span class="line"></span><br><span class="line"><span class="comment">//获取挂起的二元运算符token的优先级</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">GetTokPrecedence</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(!isascii(CurTok))</span><br><span class="line">        <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//要确保它是一个已经声明的二元运算符</span></span><br><span class="line">    <span class="keyword">int</span> TokPrec = BinopPrecedence[CurTok];</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span>(TokPrec&lt;=<span class="number">0</span>) <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> TokPrece;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">int</span> <span class="title">main</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//规定标准的二元运算符</span></span><br><span class="line">    <span class="comment">//最低的优先级是1</span></span><br><span class="line">    BinopPrecedence[<span class="string">&#x27;&lt;&#x27;</span>] = <span class="number">10</span>;</span><br><span class="line">    BinopPrecedence[<span class="string">&#x27;+&#x27;</span>] = <span class="number">20</span>;</span><br><span class="line">    BinopPrecedence[<span class="string">&#x27;-&#x27;</span>] = <span class="number">20</span>;</span><br><span class="line">    BinopPrecedence[<span class="string">&#x27;*&#x27;</span>] = <span class="number">40</span>;  <span class="comment">//最高优先级</span></span><br><span class="line">   </span><br><span class="line">    ......</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>目前对于万花筒的基本形式，只写了四个二元运算符。使用GetTokPrecedence函数返回当前token的优先级，如果token不是二元运算符，则返回-1。使用map可以随意添加二元运算符。</p><p>通过上面定义的程序，现在可以开始解析二元表达式，因为括号是<strong>主表达式</strong>，所以二元表达式解析器不需要担心嵌套的子表达式。</p><h6 id="解析表达式-ParseExpression">解析表达式 / ParseExpression</h6><p>运算符优先级解析的基本思想是将具有可能不明确的二元运算符的表达式分解为多个部分。</p><p>例如，考虑表达式“<strong>a + b +（c + d） *e* f + g</strong>”。</p><p>它将首先解析主要的主要表达式“a”，然后它将看到对**[+，b]    [+，（c + d）]    [*，e]   [*，f]和  [+，g]** 。请注意，因为括号是主表达式，所以二进制表达式解析器根本不需要担心嵌套的子表达式，如（c + d）。</p><p>首先，表达式是一个主表达式，可能后面跟一系列【binop，primaryepxr】对：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// expression</span></span><br><span class="line"><span class="comment">/// ::= primary binoprhs</span></span><br><span class="line"><span class="comment">///</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; <span class="title">ParseExpression</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">auto</span> LHS = ParsePrimary(); <span class="comment">//分析二元表达式左部的主表达式，见上方【主表达式的解析】</span></span><br><span class="line">    <span class="keyword">if</span>(!LHS)</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> ParseBinOpRHS(<span class="number">0</span>,<span class="built_in">std</span>::move(LHS));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ParseBinOpRHS是解析对序列的函数，它需要一个<strong>优先级</strong>和一个<strong>指向目前已解析的部分的表达式的指针</strong>。</p><h6 id="解析二元表达式的右部-ParseBinOpRHS">解析二元表达式的右部 / ParseBinOpRHS</h6><p>传入的优先级的值表示<strong>允许该函数吃掉的最小的运算符优先级</strong>。例如，如果当前的对是【+，x】，并且ParseBinOpRHS传入的是<strong>40的优先级</strong>，则该函数不会吃掉任何token（因为加号的优先级<strong>仅为20</strong>），考虑到这一点，则有：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// binoprhs</span></span><br><span class="line"><span class="comment">/// ::= (&#x27;+&#x27; primary)*</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; <span class="title">ParseBinOpRHS</span><span class="params">(<span class="keyword">int</span> ExprPrec,<span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;ExprAST&gt; LHS)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//如果这是一个二元运算符，就需要它的优先级</span></span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> TokPrec = GetTokPrecedence();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//这个（下一个）运算符的优先级大于当前的运算符，就消耗掉它，否则就结束程序</span></span><br><span class="line">        <span class="keyword">if</span>(TokPrec &lt; ExprPrec)</span><br><span class="line">            <span class="keyword">return</span> LHS;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//现在我们知道这是一个二元运算符了</span></span><br><span class="line">        <span class="keyword">int</span> BinOp = CurTok;</span><br><span class="line">        getNextToken();  <span class="comment">//吃掉这个运算符,将其包含在表达式中</span></span><br><span class="line">        </span><br><span class="line">        <span class="comment">//分析二元运算符之后的主表达式</span></span><br><span class="line">        <span class="keyword">auto</span> RHS = ParsePrimary();</span><br><span class="line">        <span class="keyword">if</span>(!RHS)</span><br><span class="line">            <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br></pre></td></tr></table></figure><p>此代码获取当前token的优先级，并检查它是否过低，因为我们将无效的<strong>即除了运算符以外的字符</strong>的优先级定义为-1，所以此检查知道标记流消耗掉二元运算符时，就停止<strong>对</strong>流，</p><p>这样一来，此程序吃掉并记住二元运算符，然后解析其右部的主表达式。</p><p>现在我们解析了<strong>运算符左部的表达式</strong>和<strong>一对RHS序列</strong>，接下来必须决定表达式关联的方式。特别是可能有“**（a+b） binop unparsed **” 或者“<strong>a+（b binop unparsed）</strong> ” 两种形式，为了确定这一点，我们预观测binop并确定其优先级，并将其与BinOp（当前是指a后面的加号）的优先级进行比较，<strong>显然binop是“+”，优先级小于等于BinOp，故选择第一种形式</strong>。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果预测运算符的优先级小于等于当前运算符，就把当前处理的对的右部与左侧关联，即 (a+b) binop unparsed</span></span><br><span class="line"><span class="comment">// 如果预测运算符的优先级大于当前运算符，就把当前处理的对的右部与右侧关联，即 a+(b binop unparsed)</span></span><br><span class="line"><span class="keyword">int</span> NextPrec= GetTokPrecedence();</span><br><span class="line"><span class="keyword">if</span>(TokPrec &lt; NextPrec)</span><br><span class="line">&#123;</span><br></pre></td></tr></table></figure><ul><li><p>考虑表达式“<strong>a + b +（c + d） *e* f + g</strong>”：</p><p>它将首先解析主要的主要表达式“a”，然后它将看到对**[+，b]    [+，（c + d）]    [*，e]   [*，f]和  [+，g]** 。</p></li></ul><p>如果binop在RHS右边的优先级<strong>小于等于</strong>当前运算符的优先级，那么就是**（a+b）binop**。当前运算符为**+<strong>，下一个运算符为</strong>+<strong>，他们有相同的优先级，在这种情况下，将为</strong>a+b**创建AST节点，然后继续解析：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">...省略<span class="keyword">if</span>体...</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//合并 LHS/RHS</span></span><br><span class="line">LHS = llvm::make_unique&lt;Binary&lt;ExprAST&gt;(BinOp,<span class="built_in">std</span>::move(LHS),<span class="built_in">std</span>::move(RHS));</span><br><span class="line"></span><br><span class="line"> &#125; <span class="comment">//跳转回顶部，继续执行while循环</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>上面的例子中，会把<strong>a+b+<strong>变成</strong>(a+b)<strong>进行下一次的循环，其中</strong>第二个+<strong>作为当前token，将上面代码消耗，存储并解析</strong>(c+d)<strong>作为主表达式，就使得当前的</strong>对</strong>变成了**[+,(c+d)]<strong>，然后它将使用</strong>下一个***作为主要右侧的二元运算符来进行上方if条件的判断，这种情况下，***<strong>的优先级大于</strong>+**的优先级，因此执行if体中的代码。</p><p>接下来的关键问题就是：如何在if里完全解析右部。特别是，要构建正确的AST，它需要将所有的**(c+d)*e*f**作为RHS表达式变量，执行此操作的代码：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 如果预测运算符的优先级小于等于当前运算符，就把当前处理的对的右部与左侧关联，即 (a+b) binop unparsed</span></span><br><span class="line"><span class="comment">// 如果预测运算符的优先级大于当前运算符，就把当前处理的对的右部与右侧关联，即 a+(b binop unparsed)</span></span><br><span class="line"><span class="keyword">int</span> NextPrec= GetTokPrecedence();</span><br><span class="line"><span class="keyword">if</span>(TokPrec &lt; NextPrec)</span><br><span class="line">&#123;</span><br><span class="line">            RHS = ParseBinOpRHS(TokPrec+<span class="number">1</span>,<span class="built_in">std</span>::move(RHS));</span><br><span class="line">            <span class="keyword">if</span>(!RHS)</span><br><span class="line">                <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//合并 LHS/RHS</span></span><br><span class="line">LHS = llvm::make_unique&lt;Binary&lt;ExprAST&gt;(BinOp,<span class="built_in">std</span>::move(LHS),<span class="built_in">std</span>::move(RHS));</span><br><span class="line"></span><br><span class="line">  &#125; <span class="comment">//跳转回顶部，继续执行while循环</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>此时，我们知道我们<strong>主要RHS的二元运算符</strong>的优先级高于<strong>当前的二元运算符</strong>，因此我们知道任何优先于**+<strong>的</strong>对<strong>的序列应该被</strong>一起解析<strong>并被返回为</strong>RHS**。为此，就要用递归方式调用ParseBinOpRHS，指定TokPrec+1作为其继续执行的最小优先级。在上面的例子中，这会导致它将**(c+d)*e*f<strong>的AST节点作为RHS返回，最后将其设置为</strong>第二个+**的RHS。</p><p>最后，在下一次的while循环中，解析**+g**并将其添加到AST。</p><p>此时，已经可以将解析器指向任意token流并从中构建表达式，并且会停止在不属于表达式的第一个token处。</p><h6 id="解析函数声明-ParsePrototype">解析函数声明 / ParsePrototype</h6><p>目前缺少功能原型的处理，在万花筒语言中，这些用于<strong>extren</strong>函数声明以及<strong>函数体定义</strong>。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// prototype</span></span><br><span class="line"><span class="comment">///  ::= id &#x27;(&#x27; id* &#x27;)&#x27;</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;PrototypeAST&gt; <span class="title">ParsePrototype</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="comment">//需要输入函数名（标识符）</span></span><br><span class="line">    <span class="keyword">if</span>(CurTok!=tok_identifier)</span><br><span class="line">        <span class="keyword">return</span> LogErrorP(<span class="string">&quot;Expected function name in prototype&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//使用全局变量IdentifierStr保存函数名</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">string</span> FnName = IdentifierStr;</span><br><span class="line">    getNextToken();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//函数需要输入括号</span></span><br><span class="line">    <span class="keyword">if</span>(CurTok!=<span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">        <span class="keyword">return</span> LogErrorP(<span class="string">&quot;Expected &#x27;(&#x27; in prototype&quot;</span>);</span><br><span class="line">    <span class="comment">//读取参数名列表</span></span><br><span class="line">    <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt; ArgNames;</span><br><span class="line">    <span class="comment">//如果下一个token是</span></span><br><span class="line">    <span class="keyword">while</span>(getNextToken() == tok_idenifier)</span><br><span class="line">        ArgNames.push_back(IdentifierStr);</span><br><span class="line">    <span class="comment">//函数需要定义</span></span><br><span class="line">    <span class="keyword">if</span>(CurTok!=<span class="string">&#x27;(&#x27;</span>)</span><br><span class="line">    <span class="keyword">return</span> LogErrorP(<span class="string">&quot;Expected &#x27;)&#x27; in prototype&quot;</span>);</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//完成 </span></span><br><span class="line">    getNextToken();  <span class="comment">//吃掉右括号</span></span><br><span class="line">    </span><br><span class="line">    <span class="comment">//构建函数AST</span></span><br><span class="line">    <span class="keyword">return</span> llvm::make_unique&lt;PrototypeAST&gt;(FnName,<span class="built_in">std</span>::move(ArgsNames));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h6 id="解析函数定义-ParseDefinition">解析函数定义 / ParseDefinition</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// definition ::= &#x27;def&#x27; prototype expression</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;FunctionAST&gt; <span class="title">ParseDeFinition</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    getNextToken();  <span class="comment">//吃掉def</span></span><br><span class="line">    <span class="keyword">auto</span> Proto = ParsePrototype();</span><br><span class="line">    <span class="keyword">if</span>(!Proto) <span class="keyword">return</span> <span class="literal">nullptr</span>;  <span class="comment">//如果函数声明是空</span></span><br><span class="line">    </span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">auto</span> E = ParseExpression())</span><br><span class="line">        <span class="keyword">return</span> llvm::make_unique&lt;FunctionAST&gt;(<span class="built_in">std</span>::move(Proto),<span class="built_in">std</span>::move(E));</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>另外，支持<strong>extern</strong>来声明<strong>sin</strong>和<strong>cos</strong>之类的函数，以及支持用户函数的向前声明，这些<strong>extern</strong>只是没有代码体的声明：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// external ::= &#x27;extern&#x27; prototype</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;PrototypeAST&gt; <span class="title">ParseExtern</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    getNextToken(); <span class="comment">//吃掉extern</span></span><br><span class="line">    <span class="keyword">return</span> ParsePrototype();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>最后，还让用户输入任意顶级表达式并动态评估它们，将通过为它们定义匿名的nullary（零参数）函数来处理这个问题：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// toplevelexpr ::=expression</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;FunctionAST&gt; <span class="title">ParseTopLevelExpr</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">if</span>(<span class="keyword">auto</span> E = ParseExpression())</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="comment">//创造一个匿名函数声明</span></span><br><span class="line">        <span class="keyword">auto</span> Proto = llvm::make_unique&lt;PrototypeAST&gt;(<span class="string">&quot;&quot;</span>,<span class="built_in">std</span>::<span class="built_in">vector</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>&gt;());</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> llvm::make_unique&lt;FunctionAST&gt;(<span class="built_in">std</span>::move(Proto),<span class="built_in">std</span>::move(E));</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>到现在，已经完成了所有部分。</p><h5 id="驱动程序">驱动程序</h5><p>下面的程序只是通过<strong>顶级调度循环调用所有解析的程序</strong>：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/// top ::= definition | external | expression | &#x27;;&#x27;</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">void</span> <span class="title">MainLoop</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    <span class="keyword">while</span>(<span class="number">1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        fprint(<span class="built_in">stderr</span>,<span class="string">&quot;ready&gt;&quot;</span>);</span><br><span class="line">        <span class="keyword">switch</span>(CurTok)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">case</span> tok_eof:</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            <span class="keyword">case</span> <span class="string">&#x27;;&#x27;</span>:  <span class="comment">//忽略顶级分号</span></span><br><span class="line">                getNextToken();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> tok_def:</span><br><span class="line">                HandleDefinition();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">case</span> tok_extern:</span><br><span class="line">                HandleExtern();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            <span class="keyword">default</span>:</span><br><span class="line">                HandleTopLevelExpression();</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>忽略顶级分号：</p><p>如果在命令行中输入<strong>4+5</strong>，解析器不能确定是不是已经结尾，第一种情况，在下一行可以继续输入<strong>def foo</strong>，在这种情况下，<strong>4+5</strong>是顶级表达式的结尾；或者可以继续输入***6**，这将继续表达式的输入。使用<strong>顶级分号</strong>将允许输入<strong>4+5</strong>，这样解析器就会知道已经输入完成。</p></li></ul><h4 id="代码生成LLVM-IR">代码生成LLVM IR</h4><h5 id="代码生成设置">代码生成设置</h5><p>为了生成LLVM IR，需要一些简单的设置才能开始。</p><p>首先，要在<strong>每个AST类</strong>中定义虚拟方法<strong>codegen</strong></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原代码见【表达式】</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ExprAST</span> </span></span><br><span class="line"><span class="class">&#123;</span></span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">  <span class="keyword">virtual</span> ~ExprAST() &#123;&#125;</span><br><span class="line">  <span class="function"><span class="keyword">virtual</span> Value *<span class="title">codegen</span><span class="params">()</span> </span>= <span class="number">0</span>;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">NumberExprAST</span> :</span> <span class="keyword">public</span> ExprAST </span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">double</span> Val;</span><br><span class="line">    </span><br><span class="line"><span class="keyword">public</span>:</span><br><span class="line">  NumberExprAST(<span class="keyword">double</span> Val) : Val(Val) &#123;&#125;</span><br><span class="line">  <span class="function"><span class="keyword">virtual</span> Value *<span class="title">codegen</span><span class="params">()</span></span>;</span><br><span class="line">    </span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><p>codegen方法表示为该AST节点<strong>发出IR及其依赖的所有内容</strong>，并且它们都返回一个<strong>LLVM Value</strong>对象，<strong>Value</strong>是用于表示LLVM中的<strong>静态单一分配（SSA）寄存器</strong>或<strong>SSA值</strong>。</p><p><strong>SSA值</strong>得最独特之处在于它们的值是在相关指令<strong>执行时</strong>计算的，并在指令<strong>重新执行之前</strong>不会获得新值。也就是说，<strong>没有办法改变SSA值</strong>。</p><p>接下来要完成像之前用于解析器的<strong>LogError</strong>的方法，它将用于报告<strong>在代码生成期间发现的错误</strong>：</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> LLVMContext TheContext;</span><br><span class="line"><span class="keyword">static</span> IRBuilder&lt;&gt; Builder(TheContext);</span><br><span class="line"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">unique_ptr</span>&lt;Module&gt; TheModule;</span><br><span class="line"><span class="keyword">static</span> <span class="built_in">std</span>::<span class="built_in">map</span>&lt;<span class="built_in">std</span>::<span class="built_in">string</span>,Value *&gt; NameValues;</span><br><span class="line"></span><br><span class="line"><span class="function">Value *<span class="title">LogErrorV</span><span class="params">(<span class="keyword">const</span> <span class="keyword">char</span> *Str)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    LogError(Str);</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>以上的静态变量将在<strong>代码生成</strong>期间使用。</p><p><strong>TheContext</strong>是一个不透明的对象，拥有许多核心LLVM数据结构。</p><p><strong>Builder对象</strong>是一个辅助对象，可以生成LLVM指令。<strong>IRBuilder类</strong>模板的实例可以跟踪插入指令的<strong>当前位置</strong>，并具有创建新指令的方法。</p><p><strong>TheModule</strong>是一个包含函数和全局变量的LLVM构造。</p><p><strong>NameValues图</strong>跟踪哪些值在当前范围内，是代码的符号表。在这种形式的万花筒语言中，唯一可以引用的是<strong>功能参数</strong>。</p><h5 id="表达式代码生成">表达式代码生成</h5><p>为表达式节点生成LLVM代码</p><h6 id="数字文本">数字文本</h6><p>首先编写<strong>数字文本</strong></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Value *<span class="title">NumberExprAST::codegen</span><span class="params">()</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="keyword">return</span> ConstantFP::get(TheContext, APFloat(Val));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在LLVM IR中，数字常量用<strong>ConstantFP类</strong>表示，它在<strong>APFloat</strong>内部保存数值（APFloat能够保持任意精度的浮点常量）。这段代码基本上只是创建并返回一个ConstantFP。</p><p>在LLVM IR中，常量都是<strong>唯一的</strong>并且<strong>共享</strong>，出于这个原因，API使用<strong>foo::get (…)</strong> 而不是<strong>new foo (…)<strong>或</strong>foo::Create (…)</strong>。（即不用再次创建）</p><h6 id="变量">变量</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Value *<span class="title">VariableExprAST::codegen</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="comment">// 在函数中查找此变量</span></span><br><span class="line">  Value *V = NamedValues[Name];</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">if</span> (!V)</span><br><span class="line">    LogErrorV(<span class="string">&quot;Unknown variable name&quot;</span>);</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">return</span> V;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在简单版的万花筒语言中，假设变量已经在某处发出并且其值可用。</p><p>实际上，<strong>NamedValues</strong>映射中唯一的值是函数参数，此程序只是检查指定的名称是否在映射中（如果没有，就引用未知变量）并返回其值。</p><h6 id="二元运算符">二元运算符</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Value *<span class="title">BinaryExprAST::codegen</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  Value *L = LHS-&gt;codegen();</span><br><span class="line">  Value *R = RHS-&gt;codegen();</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">if</span> (!L || !R)</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">switch</span> (Op) </span><br><span class="line">  &#123;</span><br><span class="line">  <span class="keyword">case</span> <span class="string">&#x27;+&#x27;</span>:</span><br><span class="line">    <span class="keyword">return</span> Builder.CreateFAdd(L, R, <span class="string">&quot;addtmp&quot;</span>);</span><br><span class="line">  <span class="keyword">case</span> <span class="string">&#x27;-&#x27;</span>:</span><br><span class="line">    <span class="keyword">return</span> Builder.CreateFSub(L, R, <span class="string">&quot;subtmp&quot;</span>);</span><br><span class="line">  <span class="keyword">case</span> <span class="string">&#x27;*&#x27;</span>:</span><br><span class="line">    <span class="keyword">return</span> Builder.CreateFMul(L, R, <span class="string">&quot;multmp&quot;</span>);</span><br><span class="line">  <span class="keyword">case</span> <span class="string">&#x27;&lt;&#x27;</span>:</span><br><span class="line">    L = Builder.CreateFCmpULT(L, R, <span class="string">&quot;cmptmp&quot;</span>);</span><br><span class="line">          </span><br><span class="line">    <span class="comment">//将布尔值0/1转化为双精度值0.0/1.0</span></span><br><span class="line">    <span class="keyword">return</span> Builder.CreateUIToFP(L, Type::getDoubleTy(TheContext),</span><br><span class="line">                                <span class="string">&quot;booltmp&quot;</span>);</span><br><span class="line">  <span class="keyword">default</span>:</span><br><span class="line">    <span class="keyword">return</span> LogErrorV(<span class="string">&quot;invalid binary operator&quot;</span>);</span><br><span class="line">  &#125;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h6 id="函数调用">函数调用</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Value *<span class="title">CallExprAST::codegen</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  </span><br><span class="line">  <span class="comment">//在全局模块表中查找名称</span></span><br><span class="line">  Function *CalleeF = TheModule-&gt;getFunction(Callee);</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">if</span> (!CalleeF)</span><br><span class="line">    <span class="keyword">return</span> LogErrorV(<span class="string">&quot;Unknown function referenced&quot;</span>);</span><br><span class="line">    </span><br><span class="line">  <span class="comment">// If argument mismatch error.</span></span><br><span class="line">  <span class="keyword">if</span> (CalleeF-&gt;arg_size() != Args.size())</span><br><span class="line">    <span class="keyword">return</span> LogErrorV(<span class="string">&quot;Incorrect # arguments passed&quot;</span>);</span><br><span class="line">    </span><br><span class="line">  <span class="built_in">std</span>::<span class="built_in">vector</span>&lt;Value *&gt; ArgsV;</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">for</span> (<span class="keyword">unsigned</span> i = <span class="number">0</span>, e = Args.size(); i != e; ++i)</span><br><span class="line">  &#123;</span><br><span class="line">    ArgsV.push_back(Args[i]-&gt;codegen());</span><br><span class="line">      </span><br><span class="line">    <span class="keyword">if</span> (!ArgsV.back())</span><br><span class="line">      <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">  &#125;</span><br><span class="line">    </span><br><span class="line">  <span class="keyword">return</span> Builder.CreateCall(CalleeF, ArgsV, <span class="string">&quot;calltmp&quot;</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="功能代码生成">功能代码生成</h5><h6 id="声明代码的生成">声明代码的生成</h6><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="function">Function *<span class="title">PrototypeAST::codegen</span><span class="params">()</span> </span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">  <span class="comment">// 创建函数类型:  double(double,double) etc.</span></span><br><span class="line">  <span class="function"><span class="built_in">std</span>::<span class="built_in">vector</span>&lt;Type*&gt; <span class="title">Doubles</span><span class="params">(Args.size(),</span></span></span><br><span class="line"><span class="function"><span class="params">                             Type::getDoubleTy(TheContext))</span></span>;</span><br><span class="line">    </span><br><span class="line">  FunctionType *FT =</span><br><span class="line">    FunctionType::get(Type::getDoubleTy(TheContext), Doubles, <span class="literal">false</span>);</span><br><span class="line">    </span><br><span class="line">  Function *F =</span><br><span class="line">    Function::Create(FT, Function::ExternalLinkage, Name, TheModule);</span><br></pre></td></tr></table></figure><p><strong>FunctionType::get</strong>创建FunctionType，它的调用应该用于<strong>给定的Prototype</strong>。由于Kaleidoscope中的所有函数参数都是<strong>double类型</strong>，因此第一行创建了一个“N”LLVM double类型的向量。然后它使用该Functiontype::get方法创建一个函数类型，该函数类型将“N”双精度作为参数，结果返回一个double，而不是vararg（false参数表示这一点）。请注意，LLVM中的类型与常量一样是唯一的，所以你不要<strong>new</strong>一个类型，你<strong>get</strong>它。</p><p>上面的最后一行实际上创建了<strong>与Prototype相对应的IR功能</strong>。这表示要使用的类型，链接和名称，以及要插入的模块。<strong>外部链接</strong>意味着该功能可以在当前模块外部定义,可以由模块外部的功能调用。传入的名称是用户指定的名称：由于指定了<strong>TheModule</strong>，因此该名称在<strong>TheModule符号表</strong>中注册。</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 设置所有参数的名称</span></span><br><span class="line"><span class="keyword">unsigned</span> Idx = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">auto</span> &amp;Arg : F-&gt;args())</span><br><span class="line">  Arg.setName(Args[Idx++]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">return</span> F;</span><br></pre></td></tr></table></figure><h4 id="我只能说，再往后我就不会了">我只能说，再往后我就不会了</h4><p><strong>就写到这里了，以后看情况可能会再写一点</strong></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;学习&lt;strong&gt;Kaleidoscope:Kaleidoscope Introduction and the Lexer（万花筒语言的简介和词法）&lt;/strong&gt;&lt;/p&gt;</summary>
    
    
    
    <category term="LLVM" scheme="http://example.com/categories/LLVM/"/>
    
    
    <category term="LLVM学习记录" scheme="http://example.com/tags/LLVM%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>【数据结构01】</title>
    <link href="http://example.com/2021/11/28/prim%E7%AE%97%E6%B3%95/"/>
    <id>http://example.com/2021/11/28/prim%E7%AE%97%E6%B3%95/</id>
    <published>2021-11-28T02:08:15.319Z</published>
    <updated>2021-11-28T09:44:57.843Z</updated>
    
    <content type="html"><![CDATA[<h3 id="迷宫生成算法">迷宫生成算法</h3><p>现在的迷宫生成主要有三种算法：DFS算法，随机Prim算法，Kruskal算法</p><ul><li>DFS算法：即深度优先搜索算法，这种方法生成的迷宫具有较为明显的通路</li><li>Prim算法：这种算法生成的迷宫岔路较多，整体看上去也较为自然</li><li>Kruskal算法：这种算法同样不会产生明显的主路，岔路也较多</li></ul><p>本篇主要采用Prim算法生成完美迷宫</p></br><h3 id="完美迷宫">完美迷宫</h3><p>完美迷宫是指符合以下条件的迷宫：</p><ul><li><p>迷宫中任意两点之间存在通路</p></li><li><p>任意两点见的通路是唯一的</p></li><li><p>迷宫中不存在回路</p></li></ul></br><h3 id="Prim完美迷宫生成算法">Prim完美迷宫生成算法</h3><p><em>以下讲述中使用的代码大部分是伪码</em></p><h4 id="主要步骤">主要步骤</h4><ul><li><p>设置一个二维数组，用于记录地图信息</p><ul><li>地图尺寸为 <strong>奇数×奇数</strong></li><li>地图最外侧一周是<strong>墙</strong></li><li>将坐标为（奇数，奇数）的点先看做路点（<strong>坐标从0开始</strong>）</li><li>这里用<strong>0表示路</strong>，用<strong>1表示墙</strong></li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="meta-keyword">define</span> Width 11</span></span><br><span class="line"><span class="meta">#<span class="meta-keyword">define</span> Height 11</span></span><br><span class="line"><span class="keyword">int</span> roadOrWall[Width][Height] = <span class="number">0</span>;</span><br></pre></td></tr></table></figure></li><li><p>初始化地图信息</p><ul><li>将所有地图块标记为墙，即数组所有元素的值都为1</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; Width; i++)</span><br><span class="line">&#123;</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">int</span> j = <span class="number">0</span>; j &lt; Height; j++)</span><br><span class="line">&#123;</span><br><span class="line">roadOrWall[i][j] = <span class="number">1</span>; </span><br><span class="line">&#125;</span><br><span class="line">&#125;</span><br><span class="line">或</span><br><span class="line">    <span class="built_in">memset</span>(roadOrWall,<span class="number">1</span>,<span class="keyword">sizeof</span>(roadOrWall));</span><br></pre></td></tr></table></figure><p>将路点用<strong>蓝色</strong>表示，墙用<strong>橙色</strong>表示，路用<strong>白色</strong>表示</p><p>会得到下面的“迷宫”（11×11）</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128105829668.png" alt="image-20211128105829668" style="zoom:67%;" /></li><li><p>现在在图的边缘部分选择一个路点，如图所示，选择（1,1）点，将其变成路</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">roadOrWall[<span class="number">1</span>][<span class="number">1</span>]=<span class="number">0</span>;</span><br></pre></td></tr></table></figure><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128110249416.png" alt="image-20211128110249416" style="zoom:67%;" /></li><li><p>再将其周围的两个路点，加入<strong>待选路点</strong>列表中</p><ul><li>待选路点用<strong>绿色</strong>表示，得到以下图像</li><li>待选路点在地图信息中<strong>用2表示</strong></li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">roadOrWall[<span class="number">1</span>][<span class="number">3</span>]=<span class="number">2</span>;</span><br><span class="line">roadOrWall[<span class="number">3</span>][<span class="number">1</span>]=<span class="number">2</span>;</span><br></pre></td></tr></table></figure><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128110314372.png" alt="image-20211128110314372" style="zoom:67%;" /></li><li><p>创建<strong>路线备选池</strong></p><ul><li>之后要变成<strong>路</strong>的地图块要从<strong>路线备选池</strong>中随机选择</li></ul></li><li><p>初始工作准备完毕，现在进入循环</p><ul><li>循环条件</li></ul><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(待选路点的个数&gt;<span class="number">0</span>)</span><br></pre></td></tr></table></figure><ul><li><p>在<strong>待选路点</strong>列表中<strong>随机</strong>选取一个路点A</p><ul><li>如选中了路点A(1,3)</li></ul></li><li><p>清空<strong>路线备选池</strong></p><ul><li>进入<strong>路线备选池</strong>的路点将会形成迷宫的路线</li></ul></li><li><p>将A周围的四个路点中的<strong>已经变成路的路点</strong>加入<strong>路线备选池</strong></p></li><li><p>从<strong>路线备选池</strong>中再<strong>随机</strong>选出一个路点B</p><ul><li>如选中了B(1,1)</li></ul></li><li><p>将AB两个路点打通，即路点A，路点B及其中间的部分全部变成路，得到下图迷宫</p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//将A变成路</span></span><br><span class="line">roadOrWall[A-&gt;y][A-&gt;x] = <span class="number">0</span>;</span><br><span class="line"><span class="comment">//将AB中间的方块变成路</span></span><br><span class="line">roadOrWall[(A-&gt;y + B-&gt;y) / <span class="number">2</span>][(A-&gt;x + B-&gt;x) / <span class="number">2</span>] = <span class="number">0</span>;</span><br></pre></td></tr></table></figure><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128111224766.png" alt="image-20211128111224766" style="zoom:67%;" /></li><li><p>将<strong>路点A</strong>周围<strong>还不是路的路点</strong>加入<strong>待选路点</strong>列表中（<strong>不可重复添加</strong>），如下图</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128111953464.png" alt="image-20211128111953464" style="zoom:67%;" /></li><li><p>从<strong>待选路点</strong>列表中删除<strong>路点A</strong></p></li><li><p>重复进行以上操作，直到<strong>待选路点</strong>列表为空为止</p></li></ul></li></ul></br><h4 id="循环步骤">循环步骤</h4><p>以下是第二到第十步循环，太长不想看可以跳到最终结果</p><ul><li><p>进行第二次循环：A(3,3)  B(1,3)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128112301289.png" alt="image-20211128112301289" style="zoom: 50%;" /></li><li><p>进行第三次循环:  A(5,3)  B(3,3)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128112421988.png" alt="image-20211128112421988" style="zoom: 50%;" /></li><li><p>进行第四次循环： A(1,3)  B(1,1)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128112659986.png" alt="image-20211128112659986" style="zoom: 50%;" /></li><li><p>进行第五次循环： A(3,5)  B(3,3)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128112921928.png" alt="image-20211128112921928" style="zoom:50%;" /></li><li><p>进行第六次循环： A(7,3)  B(5,3)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128113025254.png" alt="image-20211128113025254" style="zoom:50%;" /></li><li><p>进行第七次循环： A(5,1)  B(3,1)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128113133164.png" alt="image-20211128113133164" style="zoom:50%;" /></li><li><p>进行第八次循环： A(5,5)  B(5,3)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128113235303.png" alt="image-20211128113235303" style="zoom:50%;" /></li><li><p>进行第九次循环： A(3,7)  B(3,5)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128113451552.png" alt="image-20211128113451552" style="zoom:50%;" /></li><li><p>进行第十次循环： A(5,7)  B(3,7)</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128113617896.png" alt="image-20211128113617896" style="zoom:50%;" /></li></ul></br><h4 id="最终结果">最终结果</h4><ul><li><p>剩下的不一个一个写了，太累了，这里直接放最终结果</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128114006691.png" alt="image-20211128114006691" style="zoom: 80%;" /></li></ul></br><p><strong>这个迷宫尺寸不大，看起来似乎不是很像迷宫，下面是由我的数据结构课设的程序生成的大小为41×63的迷宫</strong></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20211128114359176.png" alt="image-20211128114359176"></p></br></br><h3 id="在实际Cpp实现中的一些问题">在实际Cpp实现中的一些问题</h3><p><strong>众所周知，在cpp中，如果要实现随机数，就需要用到rand()函数</strong></p><p><strong>但是如果要实现真随机数，则需要srand()函数，在函数中传入种子即可</strong></p><figure class="highlight c++"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">srand(<span class="number">114514</span>);</span><br><span class="line"><span class="keyword">int</span> range = rand() 待选路点的数量 + <span class="number">1</span> ;</span><br></pre></td></tr></table></figure><p>但是在实际测试中，有一些种子产生的随机数会导致<strong>链表结点</strong>无法添加的问题</p><p>即<strong>明明已经跑过了添加节点的代码，但是通过遍历得到的节点数量仍然不变</strong></p><p>经过一上午几个小时的Debug，没有检查到问题所在</p></br><p>于是我便添加了<strong>种子检测机制</strong>，即：</p><ul><li>在使用种子生成随机数用来绘制地图之前，首先测试该种子生成的随机数<strong>能否完整地添加所有待选路点链表的节点</strong></li><li>如果不能，就进行<strong>种子的偏移</strong>，会在输入种子的基础上进行微小的偏移，如果仍然不行，则继续偏移，直到种子正常位置，也叫作<strong>检测种子的合理性</strong></li></ul></br></br><h4 id="问题总结">问题总结</h4><p>目前<strong>待选路点</strong>的列表是由手写链表产生（经检查，代码在逻辑上没有问题）会产生这样的问题，除了使用<strong>种子检测机制</strong>避免使用不合理的种子进行绘图以外没有找到解决办法。使用其他数据结构如<strong>vector</strong>会不会产生一样的问题我还没有进行测试，等后续测试一波…</p>]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;迷宫生成算法&quot;&gt;迷宫生成算法&lt;/h3&gt;
&lt;p&gt;现在的迷宫生成主要有三种算法：DFS算法，随机Prim算法，Kruskal算法&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;DFS算法：即深度优先搜索算法，这种方法生成的迷宫具有较为明显的通路&lt;/li&gt;
&lt;li&gt;Prim算法：这种算法生</summary>
      
    
    
    
    <category term="数据结构" scheme="http://example.com/categories/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
    
    <category term="数据结构" scheme="http://example.com/tags/%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84/"/>
    
    <category term="Prim算法" scheme="http://example.com/tags/Prim%E7%AE%97%E6%B3%95/"/>
    
  </entry>
  
  <entry>
    <title>Java多线程</title>
    <link href="http://example.com/2021/11/27/W-Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
    <id>http://example.com/2021/11/27/W-Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/</id>
    <published>2021-11-27T06:50:30.135Z</published>
    <updated>2021-11-28T06:58:13.674Z</updated>
    
    <content type="html"><![CDATA[<h3 id="1-1-线程、进程、多线程">1.1   线程、进程、多线程</h3><h4 id="1-1-1-普通方法调用和多线程">1.1.1   普通方法调用和多线程</h4><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/001" style="zoom:80%;" /><h4 id="1-1-2-程序-进程-线程">1.1.2   程序 进程 线程</h4><p>一个进程可以有多个线程，比如视频中同时听到声音、看到图像和弹幕等等。</p><h4 id="1-1-3-Process与Thread">1.1.3   Process与Thread</h4><ul><li><p>说起进程，就不得不说下<strong>程序</strong>。程序是指令和数据的有序集合，其本身没有任何运行的含义，是一个静态的概念。</p></li><li><p>而<strong>进程</strong>则是执行程序的一次执行过程，它是一个动态的概念。是系统资源分配的单位。</p></li><li><p>通常在一个进程中可以包含若干个<strong>线程</strong>，当然一个进程中至少有一个线程，不然没有存在的意义。</p></li><li><p><strong>线程</strong>是CPU调度和执行的单位。</p><p><em>执行程序 -&gt; 进程 -&gt; 线程</em></p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">注意：很多多线程是模拟出来的，真正的多线程是指有多个cpu，即多核，如服务器。如果是模拟出来的多线程，即在只有一个cpu的情况下，在同一个时间点，cpu只能执行一个代码，因为切换的很快，所以就有同时执行的“感觉”。</span><br></pre></td></tr></table></figure><h4 id="1-1-4-总结">1.1.4   总结</h4><ul><li>线程就是独立的执行路径；</li><li>在程序运行时，即使没有自己创建线程，后台也会有多个线程，如主线程，gc线程（Java的垃圾回收机制）;</li><li>main()称为主线程，是系统的入口，用于执行整个程序；</li><li>在一个进程中，如果开辟了多个线程，线程的运行由调度器安排调度，调度器是与操作系统紧密相关的，先后顺序不能人为干预；</li><li>对同一份资源进行操作时，会存在资源抢夺的问题，需要加入并发控制；</li><li>线程会带来额外的开销，如cpu调度事件，并发控制开销；</li><li>每个线程在自己的工作内存交互，内存控制不当会造成数据不一致。</li></ul><h3 id="1-2-线程创建">1.2   线程创建</h3><ul><li>Thread 类-&gt;继承Thread类</li><li>Runnable 接口-&gt;实现Runnable接口（重点）</li><li>Callable 接口-&gt;实现Callable接口</li></ul><h4 id="1-2-1-Thread类">1.2.1   Thread类</h4><ul><li>自定义线程类继承Thread类</li><li>重写run()方法，编写线程执行体</li><li>创建线程对象，调用start()方法启动线程</li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.kibo24.demo1;</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建线程方式1--继承Thread类，重写run()方法，调用start()方法</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestThread</span> <span class="keyword">extends</span> <span class="title">Thread</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//run方法线程体</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">20</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;嘉然今天吃什么--&quot;</span>+i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//主方法main()线程</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建一个线程对象</span></span><br><span class="line">        TestThread testThread1=<span class="keyword">new</span> TestThread();</span><br><span class="line">        testThread1.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//调用start()方法</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">2000</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;海子姐ybb--&quot;</span>+i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/003" style="zoom: 80%;" /><p><em>线程交替执行，每次执行结果不同</em></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/002" style="zoom: 80%;" /><p><strong>总结</strong></p><ul><li>线程开启不一定立即执行，由cpu调度执行；</li></ul><h5 id="1-2-1-1-【使用多线程下载图片】">1.2.1.1   【使用多线程下载图片】</h5><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.kibo24.demo1;</span><br><span class="line"></span><br><span class="line"><span class="comment">//需要导入commons-io这个依赖</span></span><br><span class="line"><span class="keyword">import</span> org.apache.commons.io.FileUtils;</span><br><span class="line"><span class="keyword">import</span> java.io.File;</span><br><span class="line"><span class="keyword">import</span> java.io.IOException;</span><br><span class="line"><span class="keyword">import</span> java.net.URL;</span><br><span class="line"></span><br><span class="line"><span class="comment">//练习 实现多线程同步下载图片</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestThread2</span> <span class="keyword">extends</span> <span class="title">Thread</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String url;</span><br><span class="line">    <span class="keyword">private</span> String name;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">TestThread2</span><span class="params">(String url,String name)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.url=url;   <span class="comment">//网络图片地址</span></span><br><span class="line">        <span class="keyword">this</span>.name=name; <span class="comment">//保存的文件名</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">      <span class="comment">//重写run线程主体</span></span><br><span class="line">      WebDownloader wd=<span class="keyword">new</span> WebDownloader();</span><br><span class="line">      wd.downloader(url,name);</span><br><span class="line">      System.out.println(<span class="string">&quot;下载的文件名为&quot;</span>+name);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//构造函数</span></span><br><span class="line">        </span><br><span class="line">        TestThread2 testThread1=<span class="keyword">new</span> TestThread2(<span class="string">&quot;https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/QQ%E5%9B%BE%E7%89%8720210912143516.gif&quot;</span>,<span class="string">&quot;嘉然.gif&quot;</span>);</span><br><span class="line">        TestThread2 testThread2=<span class="keyword">new</span> TestThread2(<span class="string">&quot;https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/QQ%E5%9B%BE%E7%89%8720210912143700.jpg&quot;</span>,<span class="string">&quot;唐可可.gif&quot;</span>);</span><br><span class="line">        TestThread2 testThread3=<span class="keyword">new</span> TestThread2(<span class="string">&quot;https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/QQ%E5%9B%BE%E7%89%8720210912143743.jpg&quot;</span>,<span class="string">&quot;喜羊羊.gif&quot;</span>);</span><br><span class="line"></span><br><span class="line">        testThread1.run();</span><br><span class="line">        testThread2.run();</span><br><span class="line">        testThread3.run();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//下载器</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">WebDownloader</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">downloader</span><span class="params">(String url,String name)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">try</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//把url变成一个文件</span></span><br><span class="line">            <span class="comment">//使用commons-io中的一个类</span></span><br><span class="line">            FileUtils.copyURLToFile(<span class="keyword">new</span> URL(url),<span class="keyword">new</span> File(name));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (IOException e)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;IO异常，downloader方法出现异常&quot;</span>);</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/005" alt="image-20210912144554812" style="zoom:80%;" /><p><strong>运行结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/004" style="zoom:80%;" /><ul><li>运行结果不一定按顺序，由cpu调度安排</li><li>由于此次测试数量比较少，cpu可以按顺序安排线程</li></ul><h4 id="1-2-2-Runnable接口（重要）">1.2.2   Runnable接口（重要）</h4><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.kibo24.demo1;</span><br><span class="line"></span><br><span class="line"><span class="comment">//创建线程的第二个方法：Runnable接口，重写run()方法，</span></span><br><span class="line"><span class="comment">//执行线程需要丢入Runnable接口的实现类，调用start()方法</span></span><br><span class="line"><span class="comment">//注意这次没有继承Thread接口</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestThread3</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//run方法线程体</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">20</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;嘉然今天吃什么--&quot;</span>+i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//创建Runnable接口的实现类对象</span></span><br><span class="line">        TestThread3 testThread3=<span class="keyword">new</span> TestThread3();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建线程对象，通过线程对象来来开启线程</span></span><br><span class="line">        Thread thread=<span class="keyword">new</span> Thread(testThread3);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//通过Thread开启线程</span></span><br><span class="line">        thread.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//以上代码同下</span></span><br><span class="line">        <span class="keyword">new</span> Thread(testThread3).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//调用start()方法</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">2000</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;海子姐ybb--&quot;</span>+i);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/006" alt="image-20210912151032864" style="zoom:80%;" /><p><strong>注意</strong></p><ul><li>创建“TestThread”类，implements Runnable；</li><li>在TestThread类中重写（因为此时不继承Thread，所以严格来说不算是重写）run()方法（即编写需要执行的代码）；</li><li>创建TestThread类的对象和Thread的对象；</li><li>创建Thread类对象时把TestThread的对象放进其构造函数中；</li><li>通过Thread的对象启动线程；</li></ul><p><code>或者</code></p><ul><li>直接使用  new Thread(TestThread的对象).start() 开启线程;</li></ul><p><strong>运行结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/007" alt="image-20210912151109293" style="zoom:80%;" /><h4 id="1-2-3-实现Callable接口（暂时不进行详解）">1.2.3   实现Callable接口（暂时不进行详解）</h4><h4 id="1-2-4-总结">1.2.4   总结</h4><p><strong>继承Thread类</strong></p><ul><li>子类继承Thread类，具备多线程能力；</li><li>启动线程：子类对象，start()方法；</li><li>不建议使用：避免OOP单继承局限性（详见：<a href="https://www.cnblogs.com/smuxiaolei/p/9240871.html%EF%BC%89">https://www.cnblogs.com/smuxiaolei/p/9240871.html）</a></li></ul><p><strong>实现Runnable接口（重要）</strong></p><ul><li>实现Runnable接口，具有多线程能力；</li><li>启动线程：传入目标线程对象+Thread对象.start()；</li><li>推荐使用：避免单继承局限性(接口相对于继承的优点)，灵活方便，便于同一个对象被多个线程使用</li></ul><h5 id="1-2-3-1-【买火车票】">1.2.3.1   【买火车票】</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.kibo24.demo1;</span><br><span class="line"></span><br><span class="line"><span class="comment">//多个线程同时操作同一个对象</span></span><br><span class="line"><span class="comment">//买火车票的例子</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestThread4</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//票数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> ticketNum=<span class="number">10</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//重写run()方法</span></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(ticketNum&lt;=<span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//Thread.currentThread().getName()可以得到当前线程的名字</span></span><br><span class="line">            System.out.println(Thread.currentThread().getName()+<span class="string">&quot;买到了&quot;</span>+ticketNum--+<span class="string">&quot;号票&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        TestThread4 testThread4=<span class="keyword">new</span> TestThread4();</span><br><span class="line">        <span class="keyword">new</span> Thread(testThread4,<span class="string">&quot;嘉然&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(testThread4,<span class="string">&quot;七海&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(testThread4,<span class="string">&quot;二十四&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li>每次执行的结果不同；</li><li>多个线程操作同一个资源的情况下可能会出现“两个人拿到同一张票”的错误（当然本次没有发生这种数据并发的问题）</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/008" alt="image-20210912153532205" style="zoom:80%;" /><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/009" alt="image-20210912153750706" style="zoom:80%;" /><h5 id="1-2-3-2-【龟兔赛跑-并发问题】">1.2.3.2   【龟兔赛跑-并发问题】</h5><p><strong>情景设定</strong></p><ul><li>存在准确赛道长度；</li><li>判断比赛是否结束；</li><li>判断谁是胜者；</li><li>龟兔赛跑开始执行；</li><li>胜负结果以故事原文的乌龟获胜为准；</li><li>兔子会睡觉。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">package</span> com.kibo24.demo1;</span><br><span class="line"></span><br><span class="line"><span class="comment">//模拟龟兔赛跑</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Race</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> String winner;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">int</span>  m=<span class="number">0</span>;</span><br><span class="line">        <span class="keyword">if</span>(Thread.currentThread().getName().equals(<span class="string">&quot;兔子&quot;</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            m=<span class="number">2</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span>(Thread.currentThread().getName().equals(<span class="string">&quot;乌龟&quot;</span>))</span><br><span class="line">        &#123;</span><br><span class="line">            m=<span class="number">1</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt;= <span class="number">100</span>; i+=m)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//限制当前线程是兔子才可以进入if结构</span></span><br><span class="line">            <span class="keyword">if</span>(Thread.currentThread().getName().equals(<span class="string">&quot;兔子&quot;</span>)&amp;&amp; (i==<span class="number">50</span>))</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">try</span></span><br><span class="line">                &#123;   <span class="comment">//当前线程延迟1ms</span></span><br><span class="line">                    Thread.sleep(<span class="number">1</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">                &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">//如果比赛结束就停止循环</span></span><br><span class="line">            <span class="keyword">boolean</span> flag=gameOver(i);</span><br><span class="line">            <span class="keyword">if</span>(flag)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(Thread.currentThread().getName()+<span class="string">&quot;跑了&quot;</span>+i+<span class="string">&quot;步&quot;</span>);</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//判断i是不是已经到达100</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">gameOver</span><span class="params">(<span class="keyword">int</span> steps)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(winner!=<span class="keyword">null</span>)</span><br><span class="line">        &#123;<span class="comment">//已经存在胜者</span></span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(steps&gt;=<span class="number">100</span>)</span><br><span class="line">            &#123;</span><br><span class="line"></span><br><span class="line">                winner=Thread.currentThread().getName();</span><br><span class="line">                System.out.println(winner+<span class="string">&quot;赢了！&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Race race=<span class="keyword">new</span> Race();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//理解为龟兔两条赛道，两条线程</span></span><br><span class="line">        <span class="keyword">new</span> Thread(race,<span class="string">&quot;兔子&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(race,<span class="string">&quot;乌龟&quot;</span>).start();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li>最后可能会出现赢了以后乌龟仍然向前跑的情况，这种情况较难避免，可能是因为虚拟机停止需要时间</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/010" style="zoom:80%;" /><h3 id="1-3-静态代理模式">1.3   静态代理模式</h3><ul><li>用以下例子与多线程接口作比较</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">StaticPolicy</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        You you=<span class="keyword">new</span> You();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//二者都实现了Runnable接口</span></span><br><span class="line">        <span class="keyword">new</span> Thread(()-&gt;System.out.println(<span class="string">&quot;嘉然你带我走吧&quot;</span>)).start();</span><br><span class="line">        <span class="comment">//代理对象（真实对象）</span></span><br><span class="line">        <span class="keyword">new</span> WeddingCompany(<span class="keyword">new</span> You()).HappyMarry();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">Marry</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//结婚函数（抽象类，只能有声明）</span></span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">HappyMarry</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//真实角色</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">You</span> <span class="keyword">implements</span> <span class="title">Marry</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">HappyMarry</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;嘉然结婚了&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//代理角色</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">WeddingCompany</span> <span class="keyword">implements</span> <span class="title">Marry</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//代理真实目标角色，通过下方的构造函数传入</span></span><br><span class="line">    <span class="keyword">private</span>  Marry target;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">WeddingCompany</span><span class="params">(Marry target)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.target = target;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">HappyMarry</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        before();</span><br><span class="line">        <span class="comment">//这就是真实对象</span></span><br><span class="line">        <span class="keyword">this</span>.target.HappyMarry();</span><br><span class="line">        after();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">before</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;结婚之前布置现场&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">after</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;结婚之后收尾款&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>总结</strong></p><ul><li>真实对象和代理对象都要实现同一个接口（Marry/Runnable）；</li><li>代理对象（WeddingCompany/Thread）要代理真实角色（You/TestThread）；</li><li>代理对象可以做到许多真实对象做不到的事情；</li><li>真实对象专注于做自己的事情；</li></ul><h3 id="1-4-Lambda表达式">1.4   Lambda表达式</h3><ul><li><p>理解Functional Interface（函数式接口）是学习Java8 lambda的关键所在；</p></li><li><p>函数式接口的定义</p><ul><li>任何接口，如果只包含<strong>唯一一个</strong>抽象方法，那么他就是一个函数式接口；</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">fun</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>对于函数式接口，可以通过lambda表达式来创建该接口的对象。</li></ul></li></ul><p><strong>作用</strong></p><ul><li>避免匿名内部类定义过多；</li><li>可以让你的代码看起来很简洁；</li><li>去掉无意义的代码，保留核心逻辑。</li></ul><h4 id="1-4-1-Lambda表达式的演化过程">1.4.1   Lambda表达式的演化过程</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Lambda表达式</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestLambda1</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">//3.静态内部类</span></span><br><span class="line">    <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Like2</span> <span class="keyword">implements</span> <span class="title">ILike</span></span></span><br><span class="line"><span class="class">    </span>&#123;</span><br><span class="line">        <span class="meta">@Override</span></span><br><span class="line">        <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Lambda</span><span class="params">()</span></span></span><br><span class="line"><span class="function">        </span>&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;ybbb&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        ILike like=<span class="keyword">new</span> Like();</span><br><span class="line">        like.Lambda();</span><br><span class="line"></span><br><span class="line">        like=<span class="keyword">new</span> Like2();</span><br><span class="line">        like.Lambda();</span><br><span class="line"></span><br><span class="line">        </span><br><span class="line">        <span class="comment">//4.局部内部类</span></span><br><span class="line">        <span class="class"><span class="keyword">class</span> <span class="title">Like3</span> <span class="keyword">implements</span> <span class="title">ILike</span></span></span><br><span class="line"><span class="class">        </span>&#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Lambda</span><span class="params">()</span></span></span><br><span class="line"><span class="function">            </span>&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;ybbbb&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        like=<span class="keyword">new</span> Like3();</span><br><span class="line">        like.Lambda();</span><br><span class="line"></span><br><span class="line">        </span><br><span class="line">        <span class="comment">//5.匿名内部类</span></span><br><span class="line">        <span class="comment">//没有类的名称，必须有接口或者父类</span></span><br><span class="line">        like=<span class="keyword">new</span> ILike()</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Lambda</span><span class="params">()</span></span></span><br><span class="line"><span class="function">            </span>&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;ybbbbb&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;;</span><br><span class="line"></span><br><span class="line">        </span><br><span class="line">        <span class="comment">//6.Lambda表达式</span></span><br><span class="line">        like=()-&gt; System.out.println(<span class="string">&quot;ybbbbbbbbb&quot;</span>);</span><br><span class="line">        like.Lambda();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//1.定义一个函数式接口</span></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">ILike</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Lambda</span><span class="params">()</span></span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//2.实现类</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Like</span> <span class="keyword">implements</span> <span class="title">ILike</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">Lambda</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">&quot;ybb&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="1-4-2-Lambda表达式的简化">1.4.2   Lambda表达式的简化</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestLambda22</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//接口名 对象名=(参数)-&gt;&#123;代码&#125;;</span></span><br><span class="line">        ILove love=(<span class="keyword">int</span> a)-&gt; &#123;System.out.println(<span class="string">&quot;ybb&quot;</span>+<span class="number">24</span>);&#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//简化1：去除参数类型</span></span><br><span class="line">        love=(a)-&gt; &#123;System.out.println(<span class="string">&quot;ybb&quot;</span>+<span class="number">24</span>);&#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//简化2：去除花括号</span></span><br><span class="line">        love=(a)-&gt; System.out.println(<span class="string">&quot;ybb&quot;</span>+<span class="number">24</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//简化3：去除小括号</span></span><br><span class="line">        love=a-&gt; System.out.println(<span class="string">&quot;ybb&quot;</span>+<span class="number">24</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//总结</span></span><br><span class="line">        <span class="comment">//在只有一行代码的情况下才能去掉花括号</span></span><br><span class="line">        <span class="comment">//在只有一个参数的情况下才能去掉小括号</span></span><br><span class="line">        <span class="comment">//接口为函数式接口</span></span><br><span class="line">        <span class="comment">//多个参数也可以去掉参数类型，如果去掉必须全部去掉</span></span><br><span class="line">        love.Love(<span class="number">24</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">interface</span> <span class="title">ILove</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">Love</span><span class="params">(<span class="keyword">int</span> a)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="1-5-线程状态">1.5   线程状态</h3><p><strong>五大状态：</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/011" style="zoom: 80%;" /><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/012" style="zoom: 150%;" /><table><thead><tr><th style="text-align:center">方法</th><th style="text-align:center">说明</th></tr></thead><tbody><tr><td style="text-align:center">setPriority(int newPriority)</td><td style="text-align:center">更改线程的优先级</td></tr><tr><td style="text-align:center">static void sleep(long millis)</td><td style="text-align:center">在指定的毫秒数内让当前正在执行的线程休眠</td></tr><tr><td style="text-align:center">void join()</td><td style="text-align:center">等待该线程终止</td></tr><tr><td style="text-align:center">static void yield()</td><td style="text-align:center">暂停当前正在执行的线程对象，并执行其他线程</td></tr><tr><td style="text-align:center">void interrupt()</td><td style="text-align:center">中断线程（）</td></tr><tr><td style="text-align:center">boolean isAlive()</td><td style="text-align:center">测试线程是否处于活动状态</td></tr></tbody></table><h4 id="1-5-1-停止线程">1.5.1   停止线程</h4><ul><li>不推荐使用JDK提供的stop()、destroy()等方法；</li><li>推荐线程自己终止；</li><li>建立使用一个标志位进行终止变量，当flag==false，线程终止。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//测试stop</span></span><br><span class="line"><span class="comment">//1.建议线程正常停止--&gt;利用有限次数，不建议使用死循环</span></span><br><span class="line"><span class="comment">//2.设置一个标志位</span></span><br><span class="line"><span class="comment">//3.不要使用JDK提供的过时的方法</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestStop</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//设置标志位</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> flag=<span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> i=<span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span>(flag)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;线程正在运行--&quot;</span>+i++);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="comment">//自定义方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">stop</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.flag=<span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        TestStop testStop=<span class="keyword">new</span> TestStop();</span><br><span class="line">        <span class="keyword">new</span> Thread(testStop).start();</span><br><span class="line"></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;main线程--&quot;</span>+i);</span><br><span class="line">            <span class="keyword">if</span>(i==<span class="number">900</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//调用stop方法，切换标志位，停止线程</span></span><br><span class="line">                testStop.stop();</span><br><span class="line">                System.out.println(<span class="string">&quot;线程停止了&quot;</span>);</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/013" style="zoom:80%;" /><p><strong>一些问题</strong></p><ul><li>使用标志位不保证线程一定立刻停止，只是暂时不打印日志了而已；</li><li>当删掉代码“System.out.println(“main线程–”+i);”后，Thread线程也不输出数据，原因未知。</li></ul><h4 id="1-5-2-线程休眠">1.5.2   线程休眠</h4><ul><li>sleep()指定当前线程阻塞的毫秒数；</li><li>sleep存在异常InterruptedException；</li><li>sleep事件达到后线程进入就绪状态；</li><li>sleep可以模拟网络延时，倒计时等；</li><li>每个对象都有一个锁，sleep<strong>不会释放锁</strong>。</li></ul><h5 id="1-5-2-1-模拟网络延时">1.5.2.1   模拟网络延时</h5><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//模拟网络延时:放大问题的发生性</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestSleep</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> count=<span class="number">10</span>;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">if</span>(count&lt;=<span class="number">0</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                Thread.sleep(<span class="number">100</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;买到了&quot;</span>+count--+<span class="string">&quot;号票&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        TestThread4 testThread4=<span class="keyword">new</span> TestThread4();</span><br><span class="line">        <span class="keyword">new</span> Thread(testThread4,<span class="string">&quot;嘉然&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(testThread4,<span class="string">&quot;七海&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(testThread4,<span class="string">&quot;二十四&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="1-5-2-2-倒计时-获取系统时间">1.5.2.2   倒计时/获取系统时间</h5><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.text.SimpleDateFormat;</span><br><span class="line"><span class="keyword">import</span> java.util.Date;</span><br><span class="line"></span><br><span class="line"><span class="comment">//模拟倒计时：sleep()+循环即可</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestCountDown</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">countDown</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">int</span> num=<span class="number">10</span>;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            System.out.println(num--);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span>(num&lt;=<span class="number">0</span>)<span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//输出倒计时</span></span><br><span class="line">        countDown();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//打印当前系统时间</span></span><br><span class="line">        <span class="comment">//使用System.currentTimeMillis()获取当前系统时间</span></span><br><span class="line">        Date stime=<span class="keyword">new</span> Date(System.currentTimeMillis());</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//打印当前时间</span></span><br><span class="line">                <span class="comment">//使用 new SimpleDateFormat(格式).format(stime)</span></span><br><span class="line">                System.out.println(<span class="keyword">new</span> SimpleDateFormat(<span class="string">&quot;HH:mm:ss&quot;</span>).format(stime));</span><br><span class="line">                <span class="comment">//暂停1秒</span></span><br><span class="line">                Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">                <span class="comment">//重新获取当前时间</span></span><br><span class="line">                stime=<span class="keyword">new</span> Date(System.currentTimeMillis());</span><br><span class="line"></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="1-5-3-线程礼让">1.5.3   线程礼让</h4><ul><li>礼让线程，让当前正在执行的线程暂停，但是不阻塞；</li><li>让线程从运行状态转为就绪状态；</li><li>让cpu重新调度，礼让不一定成功，全看cpu的意愿。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//测试礼让线程：不一定成功</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestYield</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        MyYield myYield=<span class="keyword">new</span> MyYield();</span><br><span class="line">        <span class="keyword">new</span> Thread(myYield,<span class="string">&quot;A&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(myYield,<span class="string">&quot;B&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyYield</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;线程开始执行&quot;</span>);</span><br><span class="line">        Thread.yield(); <span class="comment">//线程礼让</span></span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;线程停止执行&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/014" style="zoom:80%;" /><p><em>yield是让线程从运行状态转换到就绪状态，让其他线程执行（相当于<strong>让其他线程插队，自己则不阻塞队伍</strong>），cpu调度执行依然是随机的；</em></p><p><em>sleep是让线程从运行状态转为阻塞状态，阻塞结束后转为就绪状态</em></p><p><strong>也就是说，在这个例子中，不管写不写yield()，都会出现4种情况</strong></p><h4 id="1-5-4-线程合并">1.5.4   线程合并</h4><ul><li>Join合并线程，等待此线程执行完后，再执行其他线程，其他线程阻塞；</li><li>相当于强行插队，让自己先走完（<strong>并且会阻塞队伍，使其他线程无法执行</strong>）。</li></ul><p><strong>代码</strong></p><ul><li>思路：先执行main线程，等到i达到200时执行vip线程并让其join（阻塞式插队），等待vip线程执行完毕后，main线程继续执行</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//测试Join方法：插队并阻塞</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestJoin</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;第&quot;</span>+i+<span class="string">&quot;号vip来力&quot;</span>);</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//启动线程</span></span><br><span class="line">        TestJoin testJoin=<span class="keyword">new</span> TestJoin();</span><br><span class="line">        Thread thread=<span class="keyword">new</span> Thread(testJoin);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//主线程</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">500</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;你main爷爷来了&quot;</span>+i);</span><br><span class="line">            <span class="keyword">if</span>(i==<span class="number">200</span>)</span><br><span class="line">            &#123;</span><br><span class="line">                thread.start();</span><br><span class="line">                <span class="keyword">try</span></span><br><span class="line">                &#123;</span><br><span class="line">                    thread.join();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">                &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/015" style="zoom:80%;" /><h4 id="1-5-5-线程状态观测">1.5.5   线程状态观测</h4><p><strong>Thread.State</strong></p><p>线程状态，可以处于以下状态之一：</p><ul><li><p>New</p><p>尚未启动的线程处于此状态</p></li><li><p>RUNNABLE</p><p>在Java虚拟机中的线程处于此状态</p></li><li><p>BLOCKED</p><p>被阻塞等待监视器锁定的线程处于此状态</p></li><li><p>WAITTING</p><p>正在等待另一个线程执行特定动作的线程处于此状态</p></li><li><p>TIMED_WAITTING</p><p>正在等待另一个线程执行动作达到指定等待时间的线程处于此状态</p></li><li><p>TERMINATED</p><p>已退出的线程处于此状态</p></li></ul><p><em>一个线程可以在给定时间处于一个状态，这些状态是不反映任何操作系统线程状态的虚拟机状态</em></p><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> com.kibo24.demo1.TestThread;</span><br><span class="line"></span><br><span class="line"><span class="comment">//观察测试线程状态</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestState</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Thread thread=<span class="keyword">new</span> Thread(()-&gt;&#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">5</span>; i++)</span><br><span class="line">            &#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;ybb&quot;</span>);</span><br><span class="line">                <span class="keyword">try</span></span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">//每秒输出一次ybb</span></span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">                &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">            &#125;</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//观察状态</span></span><br><span class="line">        Thread.State state=thread.getState();</span><br><span class="line">        System.out.println(state);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//观察启动后状态</span></span><br><span class="line">        thread.start(); <span class="comment">//启动线程</span></span><br><span class="line">        state=thread.getState();</span><br><span class="line">        System.out.println(state);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果线程不终止就循环监测</span></span><br><span class="line">        <span class="keyword">while</span>(state!= Thread.State.TERMINATED)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                Thread.sleep(<span class="number">300</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">//更新线程状态</span></span><br><span class="line">            state=thread.getState();</span><br><span class="line">            System.out.println(state);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//终止的线程不能复活</span></span><br><span class="line">        <span class="comment">//再次start()会报错</span></span><br><span class="line">        <span class="comment">//thread.start();</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/016" style="zoom:80%;" /><h4 id="1-5-6-线程的优先级">1.5.6   线程的优先级</h4><ul><li>Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程，线程调度器按照优先级决定应该调度哪个线程来执行。</li><li>线程优先级用数字来表示，范围[1，10]。<ul><li>Thread.MIN_PRIORITY=1</li><li>Thread.MAX_PRIORITY=10</li><li>Thread.NORM_PRIORITY=5</li></ul></li><li>使用以下方式改变优先级<ul><li>getPriority(). setPriority (int priority)</li></ul></li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">///进程优先级：超出范围会报错</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestPriority</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//主线程默认优先级5</span></span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;优先级：&quot;</span>+Thread.currentThread().getPriority());</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="comment">//创建线程</span></span><br><span class="line">        Priority priority=<span class="keyword">new</span> Priority();</span><br><span class="line">        Thread t1=<span class="keyword">new</span> Thread(priority,<span class="string">&quot;1&quot;</span>);</span><br><span class="line">        Thread t2=<span class="keyword">new</span> Thread(priority,<span class="string">&quot;2&quot;</span>);</span><br><span class="line">        Thread t3=<span class="keyword">new</span> Thread(priority,<span class="string">&quot;3&quot;</span>);</span><br><span class="line">        Thread t4=<span class="keyword">new</span> Thread(priority,<span class="string">&quot;4&quot;</span>);</span><br><span class="line">        Thread t5=<span class="keyword">new</span> Thread(priority,<span class="string">&quot;5&quot;</span>);</span><br><span class="line">        Thread t6=<span class="keyword">new</span> Thread(priority,<span class="string">&quot;6&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//先设置优先级，再使用</span></span><br><span class="line">        t1.start();</span><br><span class="line">        t2.setPriority(<span class="number">2</span>);</span><br><span class="line">        t2.start();</span><br><span class="line">        t3.setPriority(<span class="number">4</span>);</span><br><span class="line">        t3.start();</span><br><span class="line">        t4.setPriority(<span class="number">7</span>);</span><br><span class="line">        t4.start();</span><br><span class="line">        t5.setPriority(<span class="number">1</span>);</span><br><span class="line">        t5.start();</span><br><span class="line">        t6.setPriority(<span class="number">10</span>);</span><br><span class="line">        t6.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Priority</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;优先级：&quot;</span>+Thread.currentThread().getPriority());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li>优先级高的线程不一定就会跑在优先级低的线程前面；</li><li>自己设置的优先级相当于“<strong>一个建议</strong>”，最终依然要服从cpu调度；</li><li>当有网络延迟的时候优先级的作用会更好的体现。</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/017" style="zoom:80%;" /><h4 id="1-5-7-守护线程">1.5.7   守护线程</h4><ul><li>线程分为<strong>用户线程</strong>和<strong>守护线程</strong>；</li><li>虚拟机必须确保用户线程（如main线程）执行完毕；</li><li>虚拟机不用等待守护线程（gc线程）执行完毕；</li><li>例如后台记录操作日志，监控内存，垃圾回收等。</li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//测试守护线程</span></span><br><span class="line"><span class="comment">//嘉然(守护线程)守护你（用户线程）</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestDaemon</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Diane diane=<span class="keyword">new</span> Diane();</span><br><span class="line">        You you=<span class="keyword">new</span> You();</span><br><span class="line"></span><br><span class="line">        Thread thread=<span class="keyword">new</span> Thread(diane);</span><br><span class="line">        <span class="comment">//默认false,表示守护线程</span></span><br><span class="line">        thread.setDaemon(<span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//嘉然守护线程启动</span></span><br><span class="line">        thread.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//你的线程启动</span></span><br><span class="line">        <span class="keyword">new</span> Thread(you).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//嘉然</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Diane</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;嘉然守护着你&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//你</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">You</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3650</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;第&quot;</span>+i+<span class="string">&quot;天，你活的好好的&quot;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;你终于死了，准备重开吧&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li>在用户线程达到3649，即执行完毕后，虚拟机停止；</li><li>守护线程（嘉然）在用户线程结束后依然跑了一会儿，是因为虚拟机停止需要时间。</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/018" style="zoom:80%;" /><h3 id="1-6-线程同步机制">1.6   线程同步机制</h3><ul><li>多个线程操作同一个资源</li></ul><h4 id="1-6-1-并发">1.6.1   并发</h4><ul><li>同一个对象被多个线程同时操作<ul><li>如抢课，抢票，抢六级名额等</li></ul></li></ul><h4 id="1-6-2-线程同步">1.6.2   线程同步</h4><ul><li>处理多线程问题时，多个线程访问同一个对象（并发问题），并且某些线程还想要修改这个对象，这时候我们就需要线程同步。<ul><li>线程同步其实就是一个等待机制，多个需要同时访问此对象的线程进入这个<strong>对象等待池</strong>形成队列，等待前面线程使用完毕，下一个线程再使用。</li></ul></li></ul><h4 id="1-6-3-队列和锁">1.6.3   队列和锁</h4><ul><li>由于同一进程的多个线程共享同一块存储空间，存在访问冲突的问题，为了保证数据在方法中被访问的正确性，在访问时加入<strong>锁机制 synchronized</strong>，<strong>当一个线程获得对象的排他锁，就独占资源，其他线程必须等待，使用后释放锁即可继续执行</strong>，锁机制存在的问题：<ul><li>一个线程持有锁会导致其他所有需要此锁的线程挂起；</li><li>在多线程竞争下，加锁或释放锁会导致比较多的上下文切换和调度延时，引发性能问题；</li><li>如果一个高优先级线程等待一个低优先级线程释放锁，会导致优先级倒置，引发性能问题。</li></ul></li></ul><h5 id="1-6-3-1-【不安全的买票】">1.6.3.1   【不安全的买票】</h5><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//不安全的买票</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UnsafeBuyTickets</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        BuyTickets buyTickets=<span class="keyword">new</span> BuyTickets();</span><br><span class="line">        <span class="keyword">new</span> Thread(buyTickets,<span class="string">&quot;嘉然&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(buyTickets,<span class="string">&quot;七海&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(buyTickets,<span class="string">&quot;二十四&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">BuyTickets</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> count=<span class="number">0</span>;</span><br><span class="line">    <span class="keyword">boolean</span> flag=<span class="keyword">true</span>;<span class="comment">//外部停止方式</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//买票</span></span><br><span class="line">        <span class="keyword">while</span>(flag)</span><br><span class="line">        &#123;</span><br><span class="line">            buy();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">buy</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//判断是否有票</span></span><br><span class="line">        <span class="keyword">if</span>(count&gt;=<span class="number">10</span>)flag=<span class="keyword">false</span>;</span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;买到了第&quot;</span>+(count++)+<span class="string">&quot;号票&quot;</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li>因为没有线程同步（排队），所以有可能出现多个线程争抢同一份资源引发的错误。</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/019" style="zoom:80%;" /><h5 id="1-6-3-2-【不安全的取款】">1.6.3.2   【不安全的取款】</h5><ul><li>两个人同时去银行，从同一个账户取钱。</li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//不安全的取款</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UnsafeBank</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Account account=<span class="keyword">new</span> Account();</span><br><span class="line">        account.money=<span class="number">114514</span>;</span><br><span class="line">        account.name=<span class="string">&quot;七海的礼物&quot;</span>;</span><br><span class="line"></span><br><span class="line">        Bank bank=<span class="keyword">new</span> Bank(account,<span class="number">50000</span>,<span class="string">&quot;嘉然&quot;</span>);</span><br><span class="line">        Bank bank2=<span class="keyword">new</span> Bank(account,<span class="number">70000</span>,<span class="string">&quot;七海&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//这里依然是两个线程操作同一个资源啊</span></span><br><span class="line">        bank.start();</span><br><span class="line">        bank2.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Account</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> money;<span class="comment">//余额</span></span><br><span class="line">    String name;<span class="comment">//卡名</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//银行</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bank</span> <span class="keyword">extends</span> <span class="title">Thread</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//账户</span></span><br><span class="line">    Account account;</span><br><span class="line">    <span class="comment">//取钱数目</span></span><br><span class="line">    <span class="keyword">int</span> drawingMoney;</span><br><span class="line">    <span class="comment">//手里的钱</span></span><br><span class="line">    <span class="keyword">int</span> nowMoney=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Bank</span><span class="params">(Account account, <span class="keyword">int</span> drawingMoney, String name)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);<span class="comment">//调用父类的有参构造，传入name</span></span><br><span class="line">        <span class="keyword">this</span>.account = account;</span><br><span class="line">        <span class="keyword">this</span>.drawingMoney = drawingMoney;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//判断钱够不够</span></span><br><span class="line">        <span class="keyword">if</span>(account.money&lt;drawingMoney)</span><br><span class="line">        &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName()+<span class="string">&quot;想取&quot;</span>+drawingMoney+<span class="string">&quot;,但是没钱了&quot;</span>);</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span></span><br><span class="line">        &#123;</span><br><span class="line">            Thread.sleep(<span class="number">100</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">        &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        account.money-=drawingMoney;</span><br><span class="line">        nowMoney+=drawingMoney;</span><br><span class="line">        System.out.println(Thread.currentThread().getName()+<span class="string">&quot;取走了&quot;</span>+drawingMoney+<span class="string">&quot;,还剩&quot;</span>+account.money+<span class="string">&quot;,手里现在有&quot;</span>+nowMoney);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li>两个线程都先判断余额足够，然后等待一下开始取钱（所以代码中的if判断余额的机制拦不住两个线程）</li><li>使用队列和锁让他们排队可以解决问题。</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/020" style="zoom:80%;" /><h5 id="1-6-3-3-【不安全的List】">1.6.3.3   【不安全的List】</h5><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">//线程不安全的集合</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UnsafeList</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        List&lt;String&gt; list=<span class="keyword">new</span> ArrayList&lt;String&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">10000</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(()-&gt;&#123;</span><br><span class="line">                list.add(Thread.currentThread().getName());</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;list的大小是(理想是10000)：&quot;</span>+list.size());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li><p>add命令可能会使多个元素去抢同一块内存，导致list的最终大小减小；</p></li><li><p>当循环数量更大的时候，丢失的数量也会更大。</p></li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/021" style="zoom:80%;" /><h3 id="1-7-同步方法">1.7   同步方法</h3><ul><li>由于我们可以通过private关键字来保证数据对象只能被方法访问，所以我们只需要针对方法提出一套机制，这套机制就是synchronized关键字，它有两种用法：<strong>synchronized方法</strong>和<strong>synchronized方法块</strong>。</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">同步方法：<span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">method</span><span class="params">(<span class="keyword">int</span> args)</span></span>&#123;&#125;</span><br></pre></td></tr></table></figure><ul><li>synchronized方法控制对“对象”的访问，每个对象对应一把锁，每个synchronized方法都必须获得调用该方法的对象的锁才能执行，否则线层会阻塞，方法一旦执行，就独占该锁，知道该方法返回才释放锁，后面被阻塞的线程才会获得这个锁，继续执行；</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">缺点：将一个大的方法申明为synchronized方法会影响效率</span><br></pre></td></tr></table></figure><h4 id="1-7-1-同步方法的弊端">1.7.1   同步方法的弊端</h4><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/022" style="zoom:80%;" /><ul><li>方法里面需要修改的内容才需要锁，锁的太多会浪费资源；</li></ul><h4 id="1-7-2-同步块">1.7.2   同步块</h4><ul><li><p>同步块：</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">synchronized</span>(Obj)&#123;&#125;</span><br></pre></td></tr></table></figure></li><li><p>Obj称为<strong>同步监视器</strong>：</p><ul><li>Obj可以是任何对象，但是推荐使用<strong>共享资源</strong>作为同步监视器；</li><li>同步方法中无需指定同步监视器，因为同步方法的同步监视器就是<strong>this</strong>，即这个对象本身，或者是class</li></ul></li><li><p>同步监视器的执行过程：</p><ul><li>第一个线程访问，锁定同步监视器，执行其中的代码；</li><li>第二个线程访问，发现同步监视器被锁定，无法访问；</li><li>第一个线程访问完毕，解锁同步监视器；</li><li>第二个线程访问，发现同步监视器没有锁，进行锁定并开始执行代码。</li></ul></li></ul><h5 id="1-7-2-1-【经过修改的取钱代码】">1.7.2.1   【经过修改的取钱代码】</h5><p><strong>代码</strong></p><ul><li>在run方法中加入了同步块</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//不安全的取款</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UnsafeBank</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Account account=<span class="keyword">new</span> Account();</span><br><span class="line">        account.money=<span class="number">114514</span>;</span><br><span class="line">        account.name=<span class="string">&quot;七海的礼物&quot;</span>;</span><br><span class="line"></span><br><span class="line">        Bank bank=<span class="keyword">new</span> Bank(account,<span class="number">50000</span>,<span class="string">&quot;嘉然&quot;</span>);</span><br><span class="line">        Bank bank2=<span class="keyword">new</span> Bank(account,<span class="number">70000</span>,<span class="string">&quot;七海&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//这里依然是两个线程操作同一个资源啊</span></span><br><span class="line">        bank.start();</span><br><span class="line">        bank2.start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Account</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">int</span> money;<span class="comment">//余额</span></span><br><span class="line">    String name;<span class="comment">//卡名</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//银行</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bank</span> <span class="keyword">extends</span> <span class="title">Thread</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//账户</span></span><br><span class="line">    Account account;</span><br><span class="line">    <span class="comment">//取钱数目</span></span><br><span class="line">    <span class="keyword">int</span> drawingMoney;</span><br><span class="line">    <span class="comment">//手里的钱</span></span><br><span class="line">    <span class="keyword">int</span> nowMoney=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Bank</span><span class="params">(Account account, <span class="keyword">int</span> drawingMoney, String name)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);<span class="comment">//调用父类的有参构造，传入name</span></span><br><span class="line">        <span class="keyword">this</span>.account = account;</span><br><span class="line">        <span class="keyword">this</span>.drawingMoney = drawingMoney;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">synchronized</span> (account)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//判断钱够不够</span></span><br><span class="line">            <span class="keyword">if</span>(account.money&lt;drawingMoney)</span><br><span class="line">            &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName()+<span class="string">&quot;想取&quot;</span>+drawingMoney+<span class="string">&quot;,但是没钱了&quot;</span>);</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                Thread.sleep(<span class="number">100</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            account.money-=drawingMoney;</span><br><span class="line">            nowMoney+=drawingMoney;</span><br><span class="line">            System.out.println(Thread.currentThread().getName()+<span class="string">&quot;取走了&quot;</span>+drawingMoney+<span class="string">&quot;,还剩&quot;</span>+account.money+<span class="string">&quot;,手里现在有&quot;</span>+nowMoney);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>将run方法用synchronized锁住；</li><li>但是因为run中要修改的是account而不是默认的额this（Bank的对象），所以要使用代码块<strong>synchronized(account)</strong>，而不能直接在方法前加上synchronized；</li><li>锁的对象就是**变化（增删改）**的对象。</li></ul><p><strong>结果</strong></p><ul><li>给我排队取钱（<strong>锁住之后的排队顺序取决于线程启动(start)的顺序，即先来后到</strong>）</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/023" style="zoom:80%;" /><h5 id="1-7-2-2-【经过修改的List】">1.7.2.2   【经过修改的List】</h5><p><strong>代码</strong></p><ul><li>在Lambda表达式中加入了同步块</li><li>在输出结果之前</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.ArrayList;</span><br><span class="line"><span class="keyword">import</span> java.util.List;</span><br><span class="line"></span><br><span class="line"><span class="comment">//线程不安全的集合</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">UnsafeList</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        List&lt;String&gt; list=<span class="keyword">new</span> ArrayList&lt;String&gt;();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">10000</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(()-&gt;&#123;</span><br><span class="line">                <span class="keyword">synchronized</span> (list)</span><br><span class="line">                &#123;</span><br><span class="line">                    list.add(Thread.currentThread().getName());</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span></span><br><span class="line">        &#123;</span><br><span class="line">            Thread.sleep(<span class="number">300</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">        &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;list的大小是(理想是10000)：&quot;</span>+list.size());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><ul><li>上面的代码中加了Thread.sleep()，如果不加的话，可能会出现大小仍然小于10000的情况；</li><li>原因分析：for循环执行很快，add执行也很快，但是等for循环结束之后，还有一些线程没有跑完，这时候延迟显示结果就是在等待所有线程结束；</li><li>不同的是，不加synchronized之前减少的原因是<strong>多个线程争抢同一块资源+线程没跑完之前就显示list大小</strong>，而加上synchronized之后的原因就只有<strong>线程没跑完之前就显示list大小</strong>这一点了；</li><li>所以如果sleep时间过短，理论上显示的数值也有可能小于10000。</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/024" style="zoom:80%;" /><ul><li>这两种结果的区别：<ul><li><strong>多个线程争抢同一块资源+线程没跑完之前就显示list大小</strong><ul><li>最终结果，list的大小也不一定会到达10000，因为存在多抢一的操作；</li></ul></li><li><strong>线程没跑完之前就显示list大小</strong><ul><li>最终结果，虽然不加sleep也会显示不到10000，但是最终list的大小实际上是达到10000了的，多sleep一会儿必定能显示list大小为10000。</li></ul></li></ul></li></ul><h4 id="1-7-3-JUC安全集合">1.7.3   JUC安全集合</h4><p><strong>代码</strong></p><ul><li>java.util.concurrent包，是一个安全的包，相当于加上了synchronized</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.CopyOnWriteArrayList;</span><br><span class="line"></span><br><span class="line"><span class="comment">//测试JUC安全类型的集合</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestJUC</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        CopyOnWriteArrayList&lt;String&gt; copyOnWriteArrayList=<span class="keyword">new</span> CopyOnWriteArrayList();</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10000</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">new</span> Thread(()-&gt;&#123;</span><br><span class="line">              copyOnWriteArrayList.add(Thread.currentThread().getName());</span><br><span class="line">            &#125;).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;list的大小是：&quot;</span>+copyOnWriteArrayList.size());</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">try</span></span><br><span class="line">        &#123;</span><br><span class="line">            Thread.sleep(<span class="number">300</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">        &#123;</span><br><span class="line">            e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;list的大小是：&quot;</span>+copyOnWriteArrayList.size());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><p><em>具体原因上面分析过了</em></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/025" style="zoom:80%;" /><h4 id="1-7-4-死锁">1.7.4   死锁</h4><ul><li>多个线程各自占有一些共享资源，并且互相等待其他线程占有的资源才能运行，而导致两个或以上的线程都在等待对方释放资源而停止执行的情况，某一个同步块同时拥有<strong>两个或以上个对象的锁</strong>时，就可能会发生死锁的问题。</li></ul><p><strong>代码</strong></p><ul><li>双层嵌套锁</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//死锁：多个线程抱着对方需要的资源形成僵持的状态</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DeadLock</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Kibo24 k1=<span class="keyword">new</span> Kibo24(<span class="number">0</span>,<span class="string">&quot;二十四&quot;</span>);</span><br><span class="line">        Kibo24 k2=<span class="keyword">new</span> Kibo24(<span class="number">1</span>,<span class="string">&quot;王富贵&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(k1).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(k2).start();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Jiaran</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Nanami</span></span>&#123;&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Kibo24</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="comment">//需要的资源只有一份，用static声明</span></span><br><span class="line">    <span class="keyword">static</span> Jiaran dIane=<span class="keyword">new</span> Jiaran();</span><br><span class="line">    <span class="keyword">static</span> Nanami nanami=<span class="keyword">new</span> Nanami();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">int</span> choice;</span><br><span class="line">    String name;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//构造函数</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Kibo24</span><span class="params">(<span class="keyword">int</span> choice, String name)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.choice = choice;</span><br><span class="line">        <span class="keyword">this</span>.name = name;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//抢人!</span></span><br><span class="line">        rob();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">rob</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//相当于互相持有对方的锁</span></span><br><span class="line">        <span class="keyword">if</span>(choice==<span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (dIane)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//获得嘉然的锁</span></span><br><span class="line">                System.out.println(<span class="keyword">this</span>.name+<span class="string">&quot;要把嘉然抢走了！&quot;</span>);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span></span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">//等待一秒钟</span></span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">                &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                System.out.println(<span class="keyword">this</span>.name+<span class="string">&quot;还想要抢走七海！&quot;</span>);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">synchronized</span> (nanami)</span><br><span class="line">                &#123;<span class="comment">//获得七海的锁</span></span><br><span class="line">                    System.out.println(<span class="keyword">this</span>.name+<span class="string">&quot;要把七海抢走了！&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (nanami)</span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//获得七海的锁</span></span><br><span class="line">                System.out.println(<span class="keyword">this</span>.name+<span class="string">&quot;要把七海抢走了！&quot;</span>);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span></span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="comment">//等待一秒钟</span></span><br><span class="line">                    Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">                &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125;</span><br><span class="line"></span><br><span class="line">                System.out.println(<span class="keyword">this</span>.name+<span class="string">&quot;还想要抢走嘉然！&quot;</span>);</span><br><span class="line"></span><br><span class="line">                <span class="keyword">synchronized</span> (dIane)</span><br><span class="line">                &#123;<span class="comment">//获得嘉然的锁</span></span><br><span class="line">                    System.out.println(<span class="keyword">this</span>.name+<span class="string">&quot;要把嘉然抢走了！&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/026" style="zoom:80%;" /><ul><li>双方都想争夺对方手里的资源，然后就会卡住不动</li></ul><h5 id="1-7-4-1-总结">1.7.4.1   总结</h5><ul><li>产生死锁的必要条件：<ul><li><strong>互斥</strong>：一个资源每次只能被一个进程使用；</li><li><strong>请求与保持条件</strong>：一个进程因请求资源而阻塞时，对已经获得的资源保持不放；</li><li><strong>不剥夺条件</strong>：进程已经获得的资源，在未使用完之前，不能强行剥夺；</li><li><strong>循环等待条件</strong>：若干进程之间形成一种头尾相接的循环等待资源的关系。</li></ul></li></ul><p><em>只要避免一个或多个条件就可以避免死锁的发生</em></p><h4 id="1-7-5-Lock锁">1.7.5   Lock锁</h4><ul><li>从JDK5.0开始，Java提供了更强大的线程同步机制——通过显式定义同步锁对象来实现同步，同步锁使用Lock对象充当；</li><li>Lock接口是控制多个线程对资源共享进行访问的工具，锁提供了对共享资源的独占访问，每次只能有一个线程对Lock对象加锁，线程开始访问共享资源之前应先获得Lock对象；</li><li>ReentrantLock（<strong>可重入锁</strong>）类实现了Lock，他拥有和synchrd相同的并发性和内存语义，在实现线程安全的控制中，比较常用的私ReentrantLock，可以显式加锁，释放锁。</li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.locks.ReentrantLock;</span><br><span class="line"></span><br><span class="line"><span class="comment">//测试Lock锁</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestLock</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//典中典之多个线程操作一个资源</span></span><br><span class="line">        TestLocker t1=<span class="keyword">new</span> TestLocker();</span><br><span class="line">        <span class="keyword">new</span> Thread(t1,<span class="string">&quot;嘉然&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(t1,<span class="string">&quot;七海&quot;</span>).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(t1,<span class="string">&quot;二十四&quot;</span>).start();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">TestLocker</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> count=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//定义可重入锁Lock</span></span><br><span class="line">    <span class="keyword">private</span> ReentrantLock lock=<span class="keyword">new</span> ReentrantLock();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">while</span>(<span class="keyword">true</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                Thread.sleep(<span class="number">300</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//加锁</span></span><br><span class="line">                lock.lock();<span class="comment">//建议使用try-finally</span></span><br><span class="line">                <span class="keyword">if</span>(count&lt;=<span class="number">10</span>)</span><br><span class="line">                &#123;</span><br><span class="line"></span><br><span class="line">                    System.out.println(Thread.currentThread().getName()+<span class="string">&quot;抢到了第&quot;</span>+count+++<span class="string">&quot;号票&quot;</span>);</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span></span><br><span class="line">                &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">finally</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="comment">//解锁</span></span><br><span class="line">                lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>注意sleep的位置，如果在lock()内，就会只连续地执行一个或几个线程；如果在lock()外，多个线程才会基本全部执行；</li><li>原因分析：这是一个<strong>先等再锁</strong>还是<strong>先锁再等</strong>的问题。<ul><li>先等再锁：在线程上锁之前的等待过程中，任何一个线程率先执行都是有可能的</li><li>先锁再等：由于调度很迅速，于是第一个启动的线程马上被上锁，在其解锁后依旧马上被上锁，使其他线程阻塞，其他线程很难执行（并不是不可能执行）</li></ul></li><li>综上所述，建议<strong>先等再锁</strong>。</li></ul><p><strong>结果</strong></p><ul><li>先等再锁</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/027" style="zoom:80%;" /><ul><li>先锁再等</li></ul><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/028" style="zoom:80%;" /><h5 id="1-7-5-1-总结">1.7.5.1   总结</h5><ul><li>Lock是显式锁，手动开启和关闭，synchronized是隐式锁，执行出了作用域自动释放；</li><li>Lock只有代码块锁，synchrond有代码块锁和方法锁；</li><li>使用Lock锁，JVM将花费较少的时间来调度线程，性能更好，并且具有良好的扩展性（有更多的子类）；</li><li>优先使用顺序：<ul><li><strong>Lock &gt; 同步代码块（已经进入了方法体，分配了相应资源） &gt; 同步资源（在方法体之外）</strong></li></ul></li></ul><h3 id="1-8-线程协作">1.8   线程协作</h3><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/029" style="zoom:80%;" /><ul><li>这是一个线程同步问题，生产者和消费者共享同一个资源，并且生产者和消费者之间相互依赖，互为条件。<ul><li>对于生产者，没有生产产品之前，要通知消费者等待；在生产了产品之后，又需要马上通知消费者；</li><li>对于消费者，在消费之后，要通知生产者已经结束消费，需要生产新的产品以供消费；</li><li>在生产消费者问题中，仅有synchronized是不够的<ul><li>synchronized可阻止并发更新用一个资源共享，实现了同步；</li><li>synchronized不能用来实现不同线程之间的消息传递（通信）。</li></ul></li></ul></li></ul><h4 id="1-8-1-线程通信">1.8.1   线程通信</h4><ul><li><p>Java提供了几个方法解决线程之间的通信问题：</p><table><thead><tr><th style="text-align:center">方法名</th><th style="text-align:center">作用</th></tr></thead><tbody><tr><td style="text-align:center">wait()</td><td style="text-align:center">表示线程一直等待，直到其他线程通知，与sleep不同，会释放锁</td></tr><tr><td style="text-align:center">wait(long time)</td><td style="text-align:center">指定等待的时间</td></tr><tr><td style="text-align:center">notify()</td><td style="text-align:center">唤醒一个处于等待状态的线程</td></tr><tr><td style="text-align:center">notifyAll()</td><td style="text-align:center">唤醒同一个对象上所有调用wait()方法的线程，优先级别高的线程优先调度</td></tr></tbody></table></li></ul><p><strong>均是Object类的方法，只能在同步方法或者同步代码块中使用，否则会抛出异常IIIegalMonitorStateException</strong></p><h5 id="1-8-1-1-管程法">1.8.1.1   管程法</h5><p><strong>并发协作模型“生产者/消费者”—&gt;管程法</strong></p><ul><li>生产者：负责产生数据的模块（可能是方法，对象，线程，进程）</li><li>消费者：负责处理数据的模块（可能是方法，对象，线程，进程）</li><li>缓冲区：消费者不能直接使用生产者数据，<strong>生产者将数据放入缓冲区，消费者从缓冲区拿出数据</strong></li><li>notify和notifyAll<ul><li>使用notify()时，<strong>ThreadScheduler将从等待该监视器上的线程的池中选择一个随机线程，这取决于线程调度器，但是会保证只有一个线程会被通知:（随机性）</strong></li><li>notifyAll方法将唤醒<strong>所有线程</strong>。</li></ul></li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//测试生产者消费者模型：利用缓冲区解决：管程法</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestPC</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Container container=<span class="keyword">new</span> Container();</span><br><span class="line">        Productor productor=<span class="keyword">new</span> Productor(container);</span><br><span class="line">        Consumer consumer=<span class="keyword">new</span> Consumer(container);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//开启线程</span></span><br><span class="line">        <span class="keyword">new</span> Thread(productor).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(consumer).start();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Productor</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Container container;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span>  <span class="title">Productor</span><span class="params">(Container container)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.container=container;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            container.push(<span class="keyword">new</span> Product(i));</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Consumer</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> Container container;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span>  <span class="title">Consumer</span><span class="params">(Container container)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.container=container;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">100</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            container.pop(<span class="keyword">new</span> Product(i));</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Product</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> id;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Product</span><span class="params">(<span class="keyword">int</span> id)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.id=id;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">getId</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">this</span>.id;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Container</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    Product[] products=<span class="keyword">new</span> Product[<span class="number">10</span>];</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> count=<span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//需要生产者放入产品</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">push</span><span class="params">(Product product)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//判断容器是否已满</span></span><br><span class="line">        <span class="comment">//如果满了</span></span><br><span class="line">        <span class="keyword">while</span>(count==products.length)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//进入睡眠状态，等待唤醒</span></span><br><span class="line">            <span class="comment">//生产者等待消费者拿走产品</span></span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;<span class="comment">//等待的同时解锁，这时之前被阻塞的消费者pop方法会拿到锁</span></span><br><span class="line">                <span class="keyword">this</span>.wait();</span><br><span class="line"></span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果没有满，生产者放入产品</span></span><br><span class="line">        <span class="comment">//并通知消费者来拿走产品</span></span><br><span class="line">        products[count++]=product;</span><br><span class="line">        System.out.println(<span class="string">&quot;生产者生产了第&quot;</span>+product.getId()+<span class="string">&quot;个产品&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//生产之后唤醒消费者的线程，但是只是唤醒，还是被阻塞，因为没有拿到锁</span></span><br><span class="line">        <span class="keyword">this</span>.notifyAll();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//消费者拿走产品</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> Product <span class="title">pop</span><span class="params">(Product product)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        Product finalProduct=<span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">while</span>(count==<span class="number">0</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="comment">//如果没有产品了，就等待生产者放入</span></span><br><span class="line">            <span class="comment">//进入睡眠状态，等待唤醒</span></span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;<span class="comment">//等待的同时解锁，此时之前被阻塞的生产者push方法拿到锁</span></span><br><span class="line">                <span class="keyword">this</span>.wait();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//如果有，就拿走产品</span></span><br><span class="line">        finalProduct=products[--count];</span><br><span class="line">        System.out.println(<span class="string">&quot;消费了第&quot;</span>+product.getId()+<span class="string">&quot;个产品&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//拿走之后唤醒生产者的线程，但是只是唤醒，还是被阻塞，因为没有拿到锁</span></span><br><span class="line">        <span class="keyword">this</span>.notifyAll();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> finalProduct;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><strong>注意使用while循环判断条件，使用if可能会出现假唤醒问题</strong></p><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/030" style="zoom:80%;" /><h5 id="1-8-1-2-信号灯法">1.8.1.2   信号灯法</h5><ul><li>标志位flag</li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//测试生产者消费者问题：信号灯法：标志位</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestPCTwo</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        PlayStation playStation=<span class="keyword">new</span> PlayStation(<span class="string">&quot;Apex&quot;</span>);</span><br><span class="line">        Player player=<span class="keyword">new</span> Player(playStation);</span><br><span class="line">        Fixer fixer=<span class="keyword">new</span> Fixer(playStation);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">new</span> Thread(player).start();</span><br><span class="line">        <span class="keyword">new</span> Thread(fixer).start();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">//生产者--&gt;七海</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Player</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> PlayStation playStatio;</span><br><span class="line">    <span class="comment">//构造函数</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Player</span><span class="params">(PlayStation playStatio)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.playStatio = playStatio;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">20</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">                <span class="keyword">this</span>.playStatio.Play();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">//消费者--&gt;修理工</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Fixer</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> PlayStation playStatio;</span><br><span class="line">    <span class="comment">//构造函数</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Fixer</span><span class="params">(PlayStation playStatio)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.playStatio = playStatio;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i &lt;= <span class="number">20</span>; i++)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">this</span>.playStatio.fix();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">PlayStation</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String game;</span><br><span class="line">    <span class="comment">//游戏</span></span><br><span class="line">    <span class="comment">//玩家游戏，修理工等待</span></span><br><span class="line">    <span class="comment">//当为真时玩家游戏，为假时修理工修理</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">boolean</span> flag=<span class="keyword">true</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">PlayStation</span><span class="params">(String game)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.game = game;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span>  <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">Play</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//如果flag是false，说明修理工正在修理bug，要等待</span></span><br><span class="line">        <span class="keyword">while</span> (flag == <span class="keyword">false</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;</span><br><span class="line">                <span class="keyword">this</span>.wait();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//七海正在玩游戏</span></span><br><span class="line">        System.out.println(<span class="string">&quot;七海正在玩&quot;</span>+<span class="keyword">this</span>.game+<span class="string">&quot;,并出了bug&quot;</span>);</span><br><span class="line">        <span class="keyword">this</span>.flag=!<span class="keyword">this</span>.flag;</span><br><span class="line">        <span class="comment">//唤醒修理工</span></span><br><span class="line">        <span class="keyword">this</span>.notifyAll();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//修理</span></span><br><span class="line">    <span class="comment">//修理工修理游戏bug，玩家等待</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">fix</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//如果flag为true，说明七海还没玩游戏，要等待</span></span><br><span class="line">        <span class="keyword">while</span>(flag==<span class="keyword">true</span>)</span><br><span class="line">        &#123;</span><br><span class="line">            <span class="keyword">try</span></span><br><span class="line">            &#123;<span class="comment">//睡眠状态，但是释放锁</span></span><br><span class="line">                <span class="keyword">this</span>.wait();</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e)</span><br><span class="line">            &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//修理bug完成</span></span><br><span class="line">        System.out.println(<span class="string">&quot;修理工修理了&quot;</span>+<span class="string">&quot;的bug&quot;</span>);</span><br><span class="line">        <span class="keyword">this</span>.flag=!<span class="keyword">this</span>.flag;</span><br><span class="line">        <span class="comment">//唤醒七海继续玩游戏</span></span><br><span class="line">        <span class="keyword">this</span>.notifyAll();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>如果两个for循环的次数不一样的话会卡住，因为该代码中两个线程交替执行，执行过程中会进入等待状态，等待另一个线程的唤醒，如果循环次数不一样的话，说明其中一个线程会先结束，此时另一个进入等待状态的线程无法再被唤醒，就会导致进程被卡住</li></ul><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/031" style="zoom:80%;" /><h4 id="1-8-2-线程池">1.8.2   线程池</h4><ul><li><p>问题：经常创建和销毁，使用量特别大的资源，比如并发情况下的线程，对性能影响很大；</p></li><li><p>思路：提前创造好多个线程，放入线程池中，使用时直接获取，使用完放回线程池中。可以避免频繁的创建销毁，可以实现线程的重复利用；</p></li><li><p>好处：</p><ul><li>提高响应速度（减少创建新线程的事件）</li><li>降低资源消耗（重复利用线程池中的线程，不需要每次都创建新线程）</li><li>便于线程管理：<ul><li>corePoolSize：核心池的大小</li><li>maximumPoolSize：最大线程数</li><li>keepAliveTime：线程没有任务时最多保持多久后会终止</li></ul></li></ul></li><li><p>JDK5.0开始提供线程池相关API：<strong>ExecutorService</strong>和<strong>Executors</strong></p></li><li><p>ExecutorService：线程池接口，常见子类ThreadPoolExecutor</p><ul><li>void executor(Runnable command)：执行任务/命令，没有返回值，一般用来执行Runnable；</li><li><T>Future<T>submit(Callable<T> task)：执行任务，有返回值，一般用来执行Callable；</li><li>void shutdown()：关闭连接池</li></ul></li><li><p>Executors：工具类、线程池的工厂类，用于创建并返回不同类型的线程池</p></li></ul><p><strong>代码</strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> java.util.concurrent.Executor;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.ExecutorService;</span><br><span class="line"><span class="keyword">import</span> java.util.concurrent.Executors;</span><br><span class="line"></span><br><span class="line"><span class="comment">//测试线程池</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">TestPool</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">        <span class="comment">//1.创建服务，创建线程池</span></span><br><span class="line">        ExecutorService service= Executors.newFixedThreadPool(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">        service.execute(<span class="keyword">new</span> MyThread());</span><br><span class="line">        service.execute(<span class="keyword">new</span> MyThread());</span><br><span class="line">        service.execute(<span class="keyword">new</span> MyThread());</span><br><span class="line">        service.execute(<span class="keyword">new</span> MyThread());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//2.关闭连接</span></span><br><span class="line">        service.shutdown();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword">implements</span> <span class="title">Runnable</span></span></span><br><span class="line"><span class="class"></span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span></span></span><br><span class="line"><span class="function">    </span>&#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName());</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>进入线程池的线程名称：pool-编号-thread-编号</li></ul><p><strong>结果</strong></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/032" style="zoom:80%;" />]]></content>
    
    
      
      
    <summary type="html">&lt;h3 id=&quot;1-1-线程、进程、多线程&quot;&gt;1.1   线程、进程、多线程&lt;/h3&gt;
&lt;h4 id=&quot;1-1-1-普通方法调用和多线程&quot;&gt;1.1.1   普通方法调用和多线程&lt;/h4&gt;
&lt;img src=&quot;https://kibo24-1305312055.cos.ap-bei</summary>
      
    
    
    
    <category term="Java" scheme="http://example.com/categories/Java/"/>
    
    
    <category term="Java多线程学习记录" scheme="http://example.com/tags/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>初识MySQL07 ☆ 插入修改和删除语句详解</title>
    <link href="http://example.com/2021/04/15/MySQL07-Insert%E8%AF%AD%E5%8F%A5%E8%AF%A6%E8%A7%A3/"/>
    <id>http://example.com/2021/04/15/MySQL07-Insert%E8%AF%AD%E5%8F%A5%E8%AF%A6%E8%A7%A3/</id>
    <published>2021-04-15T11:25:57.984Z</published>
    <updated>2021-11-28T06:58:01.463Z</updated>
    
    <content type="html"><![CDATA[<p>数据库意义：数据存储和管理</p><p>DML语言：数据操作语言</p><ul><li>Insert</li><li>Update</li><li>Delete</li></ul><span id="more"></span><h4 id="7-1-插入">7.1 插入</h4><blockquote><p>Insert</p></blockquote><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 插入语句</span></span><br><span class="line"><span class="comment">-- insert into 表名 ([字段1，字段2，...]) values(&#x27;值1&#x27;)，(&#x27;值2&#x27;)，(&#x27;...&#x27;)</span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> `student` (`name`) <span class="keyword">VALUES</span>(<span class="string">&#x27;好耶&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 由于主键自增 可以省略主键</span></span><br><span class="line"><span class="comment">-- 如果不写表的字段 就会一一匹配</span></span><br><span class="line"><span class="comment">-- 一般写插入语句 数据和字段要一一对应</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 插入多个字段 </span></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> `student` (`name`) <span class="keyword">VALUES</span>(<span class="string">&#x27;二十四&#x27;</span>),(<span class="string">&#x27;二十五&#x27;</span>),(<span class="string">&#x27;二十六&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> `student` (`name`,`pwd`,`sex`) </span><br><span class="line"><span class="keyword">VALUES</span>(<span class="string">&#x27;超高校级的二十四&#x27;</span>,<span class="string">&#x27;24233&#x27;</span>,<span class="string">&#x27;男&#x27;</span>),(<span class="string">&#x27;超高校级的二十五&#x27;</span>,<span class="string">&#x27;23244&#x27;</span>,<span class="string">&#x27;女&#x27;</span>)</span><br></pre></td></tr></table></figure><p>语法：<code>insert into 表名 ([字段1，字段2，...]) values('值1')，('值2')，('...')</code></p><p>注意事项：</p><ul><li>字段和字段之间使用英文逗号隔开</li><li>字段是可以省略的，但是后面的值必须要一一对应</li><li>可以同时插入多条数据，VALUES后的值需要使用逗号隔开</li></ul><h4 id="7-2-修改">7.2 修改</h4><blockquote><p>Update</p></blockquote><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 修改</span></span><br><span class="line"><span class="comment">-- update 表名 set 字段名=要修改成为的内容 where [筛选条件]</span></span><br><span class="line"><span class="comment">-- 一般情况下都要加限制条件</span></span><br><span class="line">UPDATE `cbddl` <span class="keyword">SET</span> `name`<span class="operator">=</span><span class="string">&#x27;二十四桥明月夜&#x27;</span> <span class="keyword">WHERE</span> `name`<span class="operator">=</span><span class="string">&#x27;二十四枫桥别雨&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">--修改多个属性 使用逗号隔开</span></span><br><span class="line">UPDATE `cbddl` <span class="keyword">SET</span> `name`<span class="operator">=</span><span class="string">&#x27;二十四桥明月夜&#x27;</span>,`level`<span class="operator">=</span><span class="string">&#x27;24&#x27;</span> <span class="keyword">WHERE</span> `name`<span class="operator">=</span><span class="string">&#x27;二十四枫桥别雨&#x27;</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 通过多个条件定位数据</span></span><br><span class="line"><span class="keyword">WHERE</span> 条件<span class="number">1</span> <span class="keyword">AND</span> 条件<span class="number">2</span> ...</span><br></pre></td></tr></table></figure><p>条件：where子句-&gt;运算符     字段&gt;=&lt;或者在某个区间</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210415174157094.png" alt="image-20210415174157094"></p><ul><li>操作符会返回布尔值</li></ul><table><thead><tr><th>操作符</th><th>意义</th><th>表达式例子</th></tr></thead><tbody><tr><td>=</td><td>等于</td><td>5=6</td></tr><tr><td>&lt;&gt;      !=</td><td>不等于</td><td>5!=6</td></tr><tr><td>&gt;</td><td>大于</td><td>5&gt;4</td></tr><tr><td>&lt;</td><td>小于</td><td>5&lt;4</td></tr><tr><td>&gt;=</td><td>大于等于</td><td>5&gt;=4</td></tr><tr><td>&lt;=</td><td>小于等于</td><td>5&lt;=4</td></tr><tr><td>BETWEEN  AND</td><td>闭区间</td><td>[2,5]</td></tr><tr><td>AND</td><td>且</td><td>5&gt;1 AND 2&gt;1</td></tr><tr><td>OR</td><td>或</td><td>5&gt;1 OR 2&lt;1</td></tr></tbody></table><p>语法：<code>update 表名 set 字段名=要修改成为的内容 where 筛选条件</code></p><p>注意事项：</p><ul><li>colnum_name 是数据库的列，尽量用``包括</li><li>筛选条件如果不写就会对所有的列进行操作</li><li>可以把值改为变量(如CURRENT_TIME)，不一定是具体的值</li><li>多个设置的属性之间使用英文逗号隔开</li></ul><h4 id="7-3-删除">7.3 删除</h4><blockquote><p>Delete</p></blockquote><p>语法:<code>delete from 表名 [where 筛选条件]</code></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 删除数据</span></span><br><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> `cbddl` <span class="keyword">WHERE</span> `name`<span class="operator">=</span><span class="string">&#x27;二十四桥明月夜&#x27;</span></span><br></pre></td></tr></table></figure><blockquote><p>TRUNCATE</p></blockquote><ul><li>完全清空一个数据库表，表的结构和索引约束不会变</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 清空表</span></span><br><span class="line"><span class="keyword">TRUNCATE</span> <span class="keyword">TABLE</span> `student`</span><br></pre></td></tr></table></figure><blockquote><p>Delete 和 Truncate区别</p></blockquote><ul><li>相同点：都能删除数据，都不会删除表结构</li><li>不同：<ul><li>TRUNCATE       重新设置 自增列 计数器归零       Delete不会重置自增量</li><li>TRUNCATE       不会影响事务</li></ul></li></ul><p><strong>Delete删除之后重启数据库：</strong></p><ul><li>InnoDB自增列会从1开始（存在内存中，断电就丢失）<strong>貌似是计数器的问题，mysql8.0已修复</strong></li><li>MYISAM   继续从上一个自增量开始（存在文件中，不会断电丢失）</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;数据库意义：数据存储和管理&lt;/p&gt;
&lt;p&gt;DML语言：数据操作语言&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Insert&lt;/li&gt;
&lt;li&gt;Update&lt;/li&gt;
&lt;li&gt;Delete&lt;/li&gt;
&lt;/ul&gt;</summary>
    
    
    
    <category term="MySQL" scheme="http://example.com/categories/MySQL/"/>
    
    
    <category term="MySQL学习记录" scheme="http://example.com/tags/MySQL%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>初识MySQL06 ☆ 修改和删除数据表字段</title>
    <link href="http://example.com/2021/04/09/MySQL06-%E4%BF%AE%E6%94%B9%E5%92%8C%E5%88%A0%E9%99%A4%E6%95%B0%E6%8D%AE%E8%A1%A8%E5%AD%97%E6%AE%B5/"/>
    <id>http://example.com/2021/04/09/MySQL06-%E4%BF%AE%E6%94%B9%E5%92%8C%E5%88%A0%E9%99%A4%E6%95%B0%E6%8D%AE%E8%A1%A8%E5%AD%97%E6%AE%B5/</id>
    <published>2021-04-09T10:04:49.867Z</published>
    <updated>2021-11-28T06:57:54.129Z</updated>
    
    <content type="html"><![CDATA[<p>使用SQL命令对表进行修改和删除</p><span id="more"></span><h4 id="6-1-修改表">6.1 修改表</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 修改表</span></span><br><span class="line"><span class="comment">-- ALTER TABLE 旧表名 RENAME AS 新表名</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> teacher RENAME <span class="keyword">AS</span> teacher_max</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 增加表的字段</span></span><br><span class="line"><span class="comment">-- ALTER TABLE 表名 ADD 字段名 列属性</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> teacher_max <span class="keyword">ADD</span> age <span class="type">INT</span>(<span class="number">10</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">-- 修改表的字段（重命名&amp;修改约束）</span></span><br><span class="line"><span class="comment">-- ALTER TABLE 表名 CHANGE 旧字段名 新字段名 [列属性]</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> teacher_max CHANGE age age1 <span class="type">INT</span>(<span class="number">1</span>)  <span class="comment">-- 字段重命名</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- ALTER TABLE 表名 MODIFY 字段名 [列属性]</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> teacher_max MODIFY age <span class="type">VARCHAR</span>(<span class="number">10</span>)  <span class="comment">-- 修改约束</span></span><br><span class="line"></span><br><span class="line"><span class="comment">-- 删除表的字段</span></span><br><span class="line"><span class="comment">-- ALTER TABLE 表名 DROP 字段名</span></span><br><span class="line"><span class="keyword">ALTER</span> <span class="keyword">TABLE</span> teacher_max <span class="keyword">DROP</span> age</span><br></pre></td></tr></table></figure><br/><br/><h4 id="6-2-删除表">6.2 删除表</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 删除表</span></span><br><span class="line"><span class="comment">-- DROP TABLE [IF EXISTS] teacher_max</span></span><br><span class="line"><span class="keyword">DROP</span> <span class="keyword">TABLE</span> IF <span class="keyword">EXISTS</span> teacher_max</span><br></pre></td></tr></table></figure><p><strong>所有的创建和删除操作尽量加上判断是否存在  以免报错</strong></p><p>注意：</p><ul><li>字段名一般用``包括</li><li>注释使用 –   /**/</li><li>SQL关键字大小写不敏感  建议写小写字母</li><li>所有符号用英文(这还用说吗？)</li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;使用SQL命令对表进行修改和删除&lt;/p&gt;</summary>
    
    
    
    <category term="MySQL" scheme="http://example.com/categories/MySQL/"/>
    
    
    <category term="MySQL学习记录" scheme="http://example.com/tags/MySQL%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>初识MySQL05 ☆ MyISAM和InnoDB</title>
    <link href="http://example.com/2021/04/09/MySQL05-MyISAM%E5%92%8CInnoDB/"/>
    <id>http://example.com/2021/04/09/MySQL05-MyISAM%E5%92%8CInnoDB/</id>
    <published>2021-04-09T10:04:47.940Z</published>
    <updated>2021-11-28T06:57:48.211Z</updated>
    
    <content type="html"><![CDATA[<p>MyISAM和InnoDB的区别</p><span id="more"></span><h4 id="5-1-数据表的类型">5.1 数据表的类型</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 关于数据库引擎</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">INNODB 默认使用</span></span><br><span class="line"><span class="comment">MYISAM 早期使用</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><table><thead><tr><th></th><th>MYISAM</th><th>INNODB</th></tr></thead><tbody><tr><td>事务支持</td><td>不支持</td><td>支持</td></tr><tr><td>数据行锁定</td><td>不支持</td><td>支持</td></tr><tr><td>外键约束</td><td>不支持</td><td>支持</td></tr><tr><td>全文索引</td><td>支持</td><td>不支持</td></tr><tr><td>表空间大小</td><td>较小</td><td>较大，约为前者两倍</td></tr></tbody></table><p><strong>常规使用操作</strong></p><ul><li>MYISAM     节约空间，速度较快</li><li>INNODB     安全性较高，支持事务处理，多表多用户操作</li></ul><br/><blockquote><p>物理空间存在的位置</p></blockquote><ul><li>所有数据库文件都存在(Environment)data目录下</li><li>本质还是文件的存储</li></ul><br/><blockquote><p>MySQL引擎在文件上的区别</p></blockquote><ul><li>InnoDB 在数据库表中之后一个*.frm文件，以及上级目录下的ibdata1文件</li><li>MYISAM 对应的文件<ul><li>*.frm      表结构的定义文件</li><li>*.MYD    数据文件(data)</li><li>*.MYI      索引文件(index)</li></ul></li></ul><br/><blockquote><p>设置数据库表的字符集编码</p></blockquote><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">CHARSET<span class="operator">=</span>utf8</span><br></pre></td></tr></table></figure><p>不设置的话，会是mysql默认的字符集编码(Latin1，不支持中文)</p><br/><p>在my.ini中配置默认编码（8.0版本的文件在C盘某位置）</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="type">character</span><span class="operator">-</span><span class="keyword">set</span><span class="operator">-</span>server<span class="operator">=</span>utf8</span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;MyISAM和InnoDB的区别&lt;/p&gt;</summary>
    
    
    
    <category term="MySQL" scheme="http://example.com/categories/MySQL/"/>
    
    
    <category term="MySQL学习记录" scheme="http://example.com/tags/MySQL%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>初识MySQL04 ☆ 创建数据表</title>
    <link href="http://example.com/2021/04/08/MySQL04-%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <id>http://example.com/2021/04/08/MySQL04-%E5%88%9B%E5%BB%BA%E6%95%B0%E6%8D%AE%E5%BA%93/</id>
    <published>2021-04-08T14:24:34.914Z</published>
    <updated>2021-11-28T06:57:42.319Z</updated>
    
    <content type="html"><![CDATA[<p>这是用SQL命令创建一个表的范例</p><span id="more"></span><br/><h4 id="4-1-创建一张表">4.1 创建一张表</h4><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- 目标：</span></span><br><span class="line"><span class="comment">-- 创建学生表  使用SQL</span></span><br><span class="line"><span class="comment">-- 学号int(4) 姓名 登录密码varchar(20) 性别varchar(2)</span></span><br><span class="line"><span class="comment">-- 出生日期datetime 家庭住址 email</span></span><br></pre></td></tr></table></figure><br/><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">-- AUTO_INCREMENT  自增</span></span><br><span class="line"><span class="comment">-- 字符串使用单引号引起来</span></span><br><span class="line"><span class="comment">-- 表名和字段最好用``括起来</span></span><br><span class="line"><span class="comment">-- 所有语句后面加逗号 最后一个字段不用加</span></span><br><span class="line"><span class="comment">-- 一般一个表只有唯一的主键</span></span><br></pre></td></tr></table></figure><br/><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span> `student` (</span><br><span class="line">  `id` <span class="type">INT</span>(<span class="number">4</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> AUTO_INCREMENT COMMENT <span class="string">&#x27;学号&#x27;</span>,</span><br><span class="line">  `name` <span class="type">VARCHAR</span>(<span class="number">30</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;匿名&#x27;</span> COMMENT <span class="string">&#x27;姓名&#x27;</span>,</span><br><span class="line">  `pwd` <span class="type">VARCHAR</span>(<span class="number">20</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;123456&#x27;</span> COMMENT <span class="string">&#x27;密码&#x27;</span>,</span><br><span class="line">  `sex` <span class="type">VARCHAR</span>(<span class="number">2</span>) <span class="keyword">NOT</span> <span class="keyword">NULL</span> <span class="keyword">DEFAULT</span> <span class="string">&#x27;女&#x27;</span> COMMENT <span class="string">&#x27;性别&#x27;</span>,</span><br><span class="line">  `birthday` DATETIME <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;出生日期&#x27;</span>,</span><br><span class="line">  `address` <span class="type">VARCHAR</span>(<span class="number">100</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;家庭住址&#x27;</span>,</span><br><span class="line">  `email` <span class="type">VARCHAR</span>(<span class="number">80</span>) <span class="keyword">DEFAULT</span> <span class="keyword">NULL</span> COMMENT <span class="string">&#x27;邮箱&#x27;</span>,</span><br><span class="line">  </span><br><span class="line">  <span class="keyword">PRIMARY</span> KEY (`id`)</span><br><span class="line">  </span><br><span class="line">)ENGINE <span class="operator">=</span> INNODB <span class="keyword">DEFAULT</span> CHARSET<span class="operator">=</span>utf8</span><br></pre></td></tr></table></figure><p>执行成功，效果如下</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210408221713750.png" alt="image-20210408221713750"></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210408221844755.png" alt="image-20210408221844755"></p><br/><p><strong>格式</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> <span class="keyword">TABLE</span> [IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span>] `表名` (</span><br><span class="line">`字段名` 列类型 [属性] [索引] [注释] ,</span><br><span class="line">    `字段名` 列类型 [属性] [索引] [注释] ,</span><br><span class="line">    `字段名` 列类型 [属性] [索引] [注释] ,</span><br><span class="line">    ...</span><br><span class="line">    `字段名` 列类型 [属性] [索引] [注释]</span><br><span class="line">    <span class="keyword">PRIMARY</span> KEY (`字段名`)</span><br><span class="line">)[表类型][字符集设置][注释]</span><br></pre></td></tr></table></figure><p><strong>常用命令</strong></p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SHOW</span> <span class="keyword">CREATE</span> DATABASE school <span class="comment">-- 查看创建数据库的语句</span></span><br><span class="line"><span class="keyword">SHOW</span> <span class="keyword">CREATE</span> <span class="keyword">TABLE</span> student <span class="comment">-- 查看数据表的定义语句</span></span><br><span class="line"><span class="keyword">DESC</span> student <span class="comment">-- 显示表的结构</span></span><br></pre></td></tr></table></figure>]]></content>
    
    
    <summary type="html">&lt;p&gt;这是用SQL命令创建一个表的范例&lt;/p&gt;</summary>
    
    
    
    <category term="MySQL" scheme="http://example.com/categories/MySQL/"/>
    
    
    <category term="MySQL学习记录" scheme="http://example.com/tags/MySQL%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>初识MySQL03 ☆ 操作数据库</title>
    <link href="http://example.com/2021/04/08/MySQL03-%E6%93%8D%E4%BD%9C%E6%95%B0%E6%8D%AE%E5%BA%93/"/>
    <id>http://example.com/2021/04/08/MySQL03-%E6%93%8D%E4%BD%9C%E6%95%B0%E6%8D%AE%E5%BA%93/</id>
    <published>2021-04-08T14:24:31.852Z</published>
    <updated>2021-11-28T06:57:35.612Z</updated>
    
    <content type="html"><![CDATA[<br/><p>操作数据库–&gt;操作数据库中的表–&gt;操作数据库中表的数据</p><p>数据库的字段属性讲解</p><span id="more"></span><h4 id="3-1-命令操作数据库">3.1 命令操作数据库</h4><br/><p><strong>mysql的关键字不区分大小写</strong></p><ul><li>创建数据库</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">CREATE</span> DATABASE [IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span>] 数据库名</span><br></pre></td></tr></table></figure><ul><li>删除数据库</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">DROP</span> DATABASE [IF <span class="keyword">NOT</span> <span class="keyword">EXISTS</span>] 数据库名</span><br></pre></td></tr></table></figure><ul><li>使用数据库</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">--如果名称或字段是一个关键字，就需要加 `` (反引号).</span></span><br><span class="line">USE `数据库名`</span><br></pre></td></tr></table></figure><ul><li>查看数据库</li></ul><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">SHOW</span> DATABASES  <span class="comment">--查看所有数据库</span></span><br></pre></td></tr></table></figure><br/><p><strong>学习思路：</strong></p><ul><li>对比SQLYog的可视化历史记录查看sql</li><li>记忆固定语法或关键字</li></ul><br/><br/><h4 id="3-2-数据库的列类型">3.2 数据库的列类型</h4><blockquote><p>数值</p></blockquote><ul><li>tinyint            十分小的数据              1 字节</li><li>smallint            较小的数据               2 字节</li><li>mediumint     中等大小的数据         3 字节</li><li><strong>int                       标准整数                 4 字节</strong>      --&gt;常用的int</li><li>bigint                 较大的数据               8 字节</li><li>float                 单精度浮点数             4 字节</li><li>double             双精度浮点数             8 字节</li><li>decimal      字符串形式的浮点数                       --&gt;金融计算时使用</li></ul><br/><blockquote><p>字符串</p></blockquote><ul><li>char                 固定大小字符串(0~255)</li><li><strong>varchar            可变字符串(0~65535)</strong>             --&gt;常用的String</li><li>tinytext                微型文本(2^8-1)</li><li>**text                        文本串(2^16-1)  **                  --&gt;保存大文本</li></ul> <br/><blockquote><p>时间和日期</p></blockquote><p>java.util.Date</p><br/><ul><li>date                              YYYY-MM-DD</li><li>time                                HH:MM:SS</li><li><strong>datetime            YYYY-MM-DD    HH:MM:SS</strong></li><li>**timestamp                        时间戳                       **                1970.1.1至今的毫秒数</li><li>year                                   表示年份</li></ul><br/><blockquote><p>null</p></blockquote><ul><li>没有值，未知</li><li><strong>不要使用null进行运算，运算之后结果还是null</strong></li></ul><br/><br/><h4 id="3-3-数据库的字段属性">3.3 数据库的字段属性</h4><p>unsigned：</p><ul><li>无符号整数</li><li>不能声明为负数</li></ul><br/><p>zerofill：</p><ul><li>0填充</li><li>不足的位数在左侧用0填充</li></ul><br/><p>自增：</p><ul><li>通常理解为自增，自动在上一条的额基础上+1</li><li>通常用来设置唯一的主键  index 必须是整数类型</li><li>可以自定义主键自增的起始值和步长</li></ul><br/><p>非空 NULL   Not NULL：</p><ul><li>插入数据时不能设置为NULL，会报错</li><li>不填写值，默认为NULL</li></ul><br/><p>默认：</p><ul><li>设置默认值</li></ul><br/><p>拓展：</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">id           主键</span></span><br><span class="line"><span class="comment">`version`   乐观锁</span></span><br><span class="line"><span class="comment">is_delete   伪删除</span></span><br><span class="line"><span class="comment">gmt_create  创建时间</span></span><br><span class="line"><span class="comment">gmt_update  修改时间</span></span><br><span class="line"><span class="comment"></span></span><br><span class="line"><span class="comment">每一个表都必须存在以上五个字段</span></span><br><span class="line"><span class="comment">未来做项目使用  </span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><br/><br/><p><strong>一些效果图</strong></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210408215124234.png" alt="image-20210408215124234"></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210408215134914.png" alt="image-20210408215134914"></p>]]></content>
    
    
    <summary type="html">&lt;br/&gt;
&lt;p&gt;操作数据库–&amp;gt;操作数据库中的表–&amp;gt;操作数据库中表的数据&lt;/p&gt;
&lt;p&gt;数据库的字段属性讲解&lt;/p&gt;</summary>
    
    
    
    <category term="MySQL" scheme="http://example.com/categories/MySQL/"/>
    
    
    <category term="MySQL学习记录" scheme="http://example.com/tags/MySQL%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>初识MySQL02 ☆ SQLYog的安装和使用</title>
    <link href="http://example.com/2021/04/08/MySQL02-SQLYog/"/>
    <id>http://example.com/2021/04/08/MySQL02-SQLYog/</id>
    <published>2021-04-08T02:49:21.881Z</published>
    <updated>2021-11-28T06:57:27.946Z</updated>
    
    <content type="html"><![CDATA[<p>主要是SQLYog的安装和基本操作</p><p>以及一些常用的sql命令</p><span id="more"></span><br/><h4 id="2-1-安装SQLyog">2.1 安装SQLyog</h4><ul><li><p>1.安装不用我多说了吧</p></li><li><p>2.点击连接</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405163816995.png" alt="image-20210405163816995"></p></li></ul><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405163842031.png" alt="image-20210405163842031"></p><ul><li>3.新建一个数据库</li></ul><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164214313.png" alt="image-20210405164214313"></p><p><strong>每一个SQLyog的操作，本质就是对应了一个SQL，可以在历史记录中查看</strong></p><ul><li>4.新建一张表</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">字段：id,name,age.</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164437605.png" alt="image-20210405164437605"></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164709454.png" alt="image-20210405164709454"></p><p><em>主键：唯一性</em></p><p><strong>记得输入注释</strong></p><ul><li>5.查看表</li></ul><p>鼠标右键–&gt;打开表（或者查看表数据）</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164817085.png" alt="image-20210405164817085"></p><ul><li>6.添加记录、刷新并保存</li></ul><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164953972.png" alt="image-20210405164953972"></p><p>点击刷新–&gt;保存表</p><br/><br/><h4 id="2-2-连接数据库">2.2 连接数据库</h4><p>命令行连接数据库</p><figure class="highlight sql"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line">mysql <span class="operator">-</span>uroot <span class="operator">-</span>p  <span class="comment">--连接数据库</span></span><br><span class="line"></span><br><span class="line">update mysql.user <span class="keyword">set</span> authentication_string<span class="operator">=</span>password(<span class="string">&#x27;123456&#x27;</span>) <span class="keyword">where</span> <span class="keyword">user</span><span class="operator">=</span><span class="string">&#x27;root&#x27;</span> <span class="keyword">and</span> Host <span class="operator">=</span> <span class="string">&#x27;localhost&#x27;</span>;  <span class="comment">--修改用户密码</span></span><br><span class="line"></span><br><span class="line">flush privileges;  <span class="comment">--刷新权限</span></span><br><span class="line"></span><br><span class="line"><span class="comment">------------------------------------</span></span><br><span class="line"><span class="comment">--所有语句使用分号结束</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">show</span> databases;  <span class="comment">--查看所有数据库</span></span><br><span class="line"></span><br><span class="line">use 数据库名称  <span class="comment">--切换数据库</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">show</span> tables;  <span class="comment">--查看当前数据库中所有的表</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">desc</span> 表名称;</span><br><span class="line"><span class="keyword">describe</span> 表名称;  <span class="comment">--查看表的数据</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">create</span> database 数据库名称;  <span class="comment">--创建数据库</span></span><br><span class="line"></span><br><span class="line">exit;  <span class="comment">--退出连接</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="comment">--  SQL的单行注释</span></span><br><span class="line"><span class="comment">/* */</span>  <span class="comment">--多行注释</span></span><br></pre></td></tr></table></figure><p><strong>CRUD 增删改查</strong></p><p>DML：数据库<strong>管理</strong>语言</p><p>DDL：数据库<strong>定义</strong>语言</p><p>DQL：数据库<strong>查询</strong>语言</p><p>DCL：数据库<strong>控制</strong>语言</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210408104600886.png" alt="image-20210408104600886"></p>]]></content>
    
    
    <summary type="html">&lt;p&gt;主要是SQLYog的安装和基本操作&lt;/p&gt;
&lt;p&gt;以及一些常用的sql命令&lt;/p&gt;</summary>
    
    
    
    <category term="MySQL" scheme="http://example.com/categories/MySQL/"/>
    
    
    <category term="MySQL学习记录" scheme="http://example.com/tags/MySQL%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>初识MySQL01 ☆ MySql的安装和配置</title>
    <link href="http://example.com/2021/04/04/MySql01/"/>
    <id>http://example.com/2021/04/04/MySql01/</id>
    <published>2021-04-04T11:27:02.170Z</published>
    <updated>2021-11-28T06:57:19.689Z</updated>
    
    <content type="html"><![CDATA[<p>用SQL命令语句进行MySql的安装和配置</p><span id="more"></span><p>JavaEE：企业级Java开发  Web</p><ul><li><p>前端 (页面：展示  数据)</p></li><li><p>后台(连接点 ：连接数据库JDBC  连接前端–&gt;控制视图跳转)</p></li><li><p>数据库(存取数据，.txt  .xls  .docx)</p></li></ul><br/><blockquote><p>只会写代码，学好数据库，Level 0；</p><p>操作系统，数据结构与算法，Level 1；</p><p>离散数学，数字电路，体系结构，编译原理 +实战经验，Level 114514.</p></blockquote><br/><h4 id="1-1-为什么要学数据库">1.1 为什么要学数据库</h4><ul><li>岗位需求</li><li>大数据时代</li><li>被迫需求：存数据</li><li>数据库是所有软件体系中最核心的存在  DBA（DataBaseAdministrator）</li></ul><br/><br/><h4 id="1-2-什么是数据库">1.2 什么是数据库</h4><p>数据库（DB）</p><p>概念：数据仓库，软件（安装在操作系上，如windows，linux，mac…），SQL，可以存储大量数据，500万以下</p><p>作用：存储数据，管理数据</p><br/><br/><h4 id="1-3-数据库分类">1.3 数据库分类</h4><p>关系型数据库（SQL）：<strong>MySQL</strong>，Oracle，SQL Server，DB2，SQLlite</p><ul><li>通过表和表之间，行和列之间的关系进行存储</li></ul><p>非关系型数据库（NoSQL）：Redis，MongDB</p><ul><li>以对象形式存储，通过对象自身属性来决定</li></ul><br/><p>DBMS（DataBase Management System）</p><ul><li>数据库的管理软件，科学有效地管理和操作数据</li><li>MySQL，数据库管理系统</li><li>一个数据库可以管理好多软件的数据</li></ul><br/><br/><h4 id="1-4-MySQL">1.4 MySQL</h4><ul><li>使用SQL语言</li><li>关系型数据库管理系统</li><li>瑞典MySQL AB公司----&gt;Oracle旗下产品</li><li>MySQL是最好的RDBMS（Related DataBase Management System关系型数据库管理系统）应用软件之一</li><li>体积小，速度快，总体拥有成本低，招人成本低，是人就会(((</li><li>官网：<a href="https://www.mysql.com/">https://www.mysql.com/</a></li></ul><br/><br/><h4 id="1-5-安装MySQL">1.5 安装MySQL</h4><ul><li><p>1.解压啊</p></li><li><p>2.配置环境变量Path 定位至bin目录</p></li><li><p>3.Mysql目录下新建MySQL配置文件<strong>my.ini</strong></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">[mysqld]</span><br><span class="line">basedir&#x3D;E:\Environment\mysql-8.0.23\</span><br><span class="line">datadir&#x3D;E:\Environment\mysql-8.0.23\data\</span><br><span class="line">port&#x3D;3306</span><br><span class="line">skip-grant-tables</span><br></pre></td></tr></table></figure></li><li><p>4.管理员模式下启动cmd，切换路径至mysql的bin目录下，执行指令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqld -install</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404183429794.png" alt="image-20210404183429794"></p></li><li><p>5.初始化数据文件</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysqld --initialize-insecure --user&#x3D;mysql</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404183616771.png" alt="image-20210404183616771"></p></li><li><p>6.重新启动MySQL</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net start mysql</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404183846697.png" alt="image-20210404183846697"></p></li><li><p>7.用命令进入mysql管理界面</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql -u root -p</span><br></pre></td></tr></table></figure><br/><p>这里如果报错的话，执行命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mysql --remove mysql</span><br></pre></td></tr></table></figure><p>并且删除data和my.ini，重新从第4步开始吧</p></li><li><p>8.进入界面后，执行指令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">update mysql.user set authentication_string&#x3D;password(&#39;123456&#39;) where user&#x3D;&#39;root&#39; and Host &#x3D; &#39;localhost&#39;;</span><br></pre></td></tr></table></figure><br/><p>mysql8.0或以上版本如果报错的话，则可以执行如下指令来设置密码</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">set password &#x3D; &#39;123456&#39;;</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404190713640.png" alt="image-20210404190713640"></p></li><li><p>9.刷新权限</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">flush privileges；</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404190657427.png" alt="image-20210404190657427"></p></li><li><p>10.删除/注释掉my.ini里面的代码</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">skip-grant-tables</span><br></pre></td></tr></table></figure></li><li><p>11.重新启动Mysql服务</p><p>退出并关闭服务</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">exit</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404191408022.png" alt="image-20210404191408022"></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net stop mysql</span><br></pre></td></tr></table></figure><br/><p>启动服务</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">net start mysql</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404191109154.png" alt="image-20210404191109154"></p></li><li><p>12.输入密码进入mysql</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210404191201916.png" alt="image-20210404191201916"></p></li></ul><p>大功告成🌶！</p><h4 id="1-6-安装SQLyog">1.6 安装SQLyog</h4><ul><li><p>1.安装不用我多说了吧</p></li><li><p>2.点击连接</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405163816995.png" alt="image-20210405163816995"></p></li></ul><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405163842031.png" alt="image-20210405163842031"></p><ul><li>3.新建一个数据库</li></ul><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164214313.png" alt="image-20210405164214313"></p><p><strong>每一个SQLyog的操作，本质就是对应了一个SQL，可以在历史记录中查看</strong></p><ul><li>4.新建一张表</li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">字段：id,name,age.</span><br></pre></td></tr></table></figure><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164437605.png" alt="image-20210405164437605"></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164709454.png" alt="image-20210405164709454"></p><ul><li>5.查看表</li></ul><p>鼠标右键–&gt;打开表</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164817085.png" alt="image-20210405164817085"></p><ul><li>6.添加记录并保存</li></ul><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210405164953972.png" alt="image-20210405164953972"></p><p>点击刷新–&gt;保存表</p><h4 id="1-7-连接数据库">1.7 连接数据库</h4>]]></content>
    
    
    <summary type="html">&lt;p&gt;用SQL命令语句进行MySql的安装和配置&lt;/p&gt;</summary>
    
    
    
    <category term="MySQL" scheme="http://example.com/categories/MySQL/"/>
    
    
    <category term="MySQL学习记录" scheme="http://example.com/tags/MySQL%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>安装Tomcat遇到的问题</title>
    <link href="http://example.com/2021/04/03/Tomcat%E5%AE%89%E8%A3%85/"/>
    <id>http://example.com/2021/04/03/Tomcat%E5%AE%89%E8%A3%85/</id>
    <published>2021-04-03T08:28:56.913Z</published>
    <updated>2021-11-28T06:59:01.800Z</updated>
    
    <content type="html"><![CDATA[<p>根本上还是没有配置环境变量的问题</p><span id="more"></span><h3 id="1-setup安装文件">1.setup安装文件</h3><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210403160157337.png" alt="image-20210403160157337"></p><p>这里只要选择到自己的jdk目录就可以了，不用定位到 <em>/bin</em>，它会自己定位到需要的资源</p><br/><br/><h3 id="2-startup-bat一闪而过">2.startup.bat一闪而过</h3><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210403160359965.png" alt="image-20210403160359965"></p><p>启动startup.bat文件时可能会出现一闪而过的现象</p><p>原因是没有配置好环境变量</p><br/><ul><li><p>鼠标右击<strong>此电脑</strong>，打开<strong>高级系统设置</strong></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210403160546846.png" alt="image-20210403160546846"></p><br/></li><li><p>打开<strong>环境变量</strong></p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210403160611159.png" alt="image-20210403160611159"></p><br/></li><li><p>添加如图中的环境变量，同样只定位到jdk目录即可</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210403160659490.png" alt="image-20210403160659490"></p></li></ul><br/><ul><li><p>最后再运行startup.bat</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210403160807032.png" alt="image-20210403160807032"></p></li></ul><br/><ul><li><p>打开浏览器，访问 localhost:8080，大功告成！</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/image-20210403160841151.png" alt="image-20210403160841151" style="zoom:50%;" /></li></ul>]]></content>
    
    
    <summary type="html">&lt;p&gt;根本上还是没有配置环境变量的问题&lt;/p&gt;</summary>
    
    
    
    <category term="Tomcat" scheme="http://example.com/categories/Tomcat/"/>
    
    
    <category term="学习记录" scheme="http://example.com/tags/%E5%AD%A6%E4%B9%A0%E8%AE%B0%E5%BD%95/"/>
    
  </entry>
  
  <entry>
    <title>如何使用小爱同学导入超级课程表的数据</title>
    <link href="http://example.com/2021/03/27/%E8%AF%BE%E7%A8%8B%E8%A1%A8/"/>
    <id>http://example.com/2021/03/27/%E8%AF%BE%E7%A8%8B%E8%A1%A8/</id>
    <published>2021-03-27T01:28:45.982Z</published>
    <updated>2021-11-28T06:58:45.160Z</updated>
    
    <content type="html"><![CDATA[<p>​在我们使用小爱同学的课程表导入功能时，可能会由于学校未适配或者直接提示导入失败等原因无法正常使用小爱课程表，这时候我们可以通过获取超级课程表的数据来导入课程信息。</p><span id="more"></span><h2 id="1-下载超级课程表">1.下载超级课程表</h2><p>下载超级课程表并导入课程数据，这一步不用我多说了8</p><h2 id="2-下载小爱课程表导入工具">2.下载<a href="https://pan.baidu.com/s/190IozZFhYdckzu9TaI580Q">小爱课程表导入工具</a></h2><h5 id="提取码：2423">提取码：2423</h5><p><em>工具非本人开发，来自酷安@liuwenkiii</em></p><h2 id="3-启用vConsole">3.启用vConsole</h2><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/D8876FE57FA84E7DEAEEA6632BED5BC4.jpg" style="zoom:50%;" /><p>多次点击开发者卡片下方的空白处进入开发者选项界面</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/01D04C11A18E9E7D3C03CCDE0D72A245.jpg" style="zoom:50%;" /><h2 id="4-获取deviceID">4.获取deviceID</h2><p>从 <code>vConsole--&gt;Storage--&gt;LocalStorage </code>  中查找到deviceid（字母和数字混合的一串字符）</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/7C0DF6B587C8C1D1E95342885A919072.jpg" style="zoom:50%;" /><p><em>由于这里是不可以直接选中复制的，因此之后只能对照着输入，注意输入的正确性</em></p><h2 id="5-导入课程表数据">5.导入课程表数据</h2><p>打开小爱课程表数据导入工具，输入超级课程表账号(手机号)和密码，以及上一步获取的deviceid，点击导入。</p><p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/FBD8B33C6144B30F4A29805726B4BDF8.jpg" alt=""></p><p>成功之后重新打开小爱课程表，就可以看到课程数据出现了</p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/35071D3661D0F38910DBDBD3FBFDF022.jpg" style="zoom:50%;" /><p><s>说到可以利用超级课程表的课程数据的app，这里还推荐ScheduleX，比较简洁</s></p><img src="https://kibo24-1305312055.cos.ap-beijing.myqcloud.com/896D4D66A8A85A46DADD85BDAA4A4E87.jpg" style="zoom: 25%;" />]]></content>
    
    
    <summary type="html">&lt;p&gt;​		在我们使用小爱同学的课程表导入功能时，可能会由于学校未适配或者直接提示导入失败等原因无法正常使用小爱课程表，这时候我们可以通过获取超级课程表的数据来导入课程信息。&lt;/p&gt;</summary>
    
    
    
    <category term="小技巧" scheme="http://example.com/categories/%E5%B0%8F%E6%8A%80%E5%B7%A7/"/>
    
    
    <category term="课程表" scheme="http://example.com/tags/%E8%AF%BE%E7%A8%8B%E8%A1%A8/"/>
    
  </entry>
  
</feed>
