<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>沃特陌 | Wotemo</title>
  <icon>https://www.wotemo.com/img/wotemo.jpg</icon>
  <subtitle>后端攻城狮</subtitle>
  <link href="https://www.wotemo.com/atom.xml" rel="self"/>
  
  <link href="https://www.wotemo.com/"/>
  <updated>2025-06-18T15:10:05.000Z</updated>
  <id>https://www.wotemo.com/</id>
  
  <author>
    <name>Wotemo</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>解决Spring AI 1.0.0版本的ElasticsearchVectorStore不能正常序列化LocalDate问题</title>
    <link href="https://www.wotemo.com/posts/25184.html"/>
    <id>https://www.wotemo.com/posts/25184.html</id>
    <published>2025-06-18T15:10:05.000Z</published>
    <updated>2025-06-18T15:10:05.000Z</updated>
    
    <content type="html"><![CDATA[<p>博主在使用Spring AI的ElasticsearchVectorStore实现ES向量存储的时候，想在metadata中存一个date字段，实体代码如下：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">KnowledgeBase</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> String id;</span><br><span class="line">    <span class="keyword">private</span> String title;</span><br><span class="line">    <span class="keyword">private</span> String content;</span><br><span class="line">    <span class="keyword">private</span> String url;</span><br><span class="line">    <span class="keyword">private</span> LocalDate date;</span><br><span class="line">    <span class="keyword">private</span> Map&lt;String, Object&gt; metadata;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>众所周知，Jackson序列化Java 8时间（Local家族）需要导入jackson-datatype-jsr310模块，博主也是导入了，可是还是报错，尝试了各种方法，都无果后，博主跑去仔细研读了Spring AI源码，总算是发现了罪魁祸首：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">protected</span> <span class="title function_">ElasticsearchVectorStore</span><span class="params">(Builder builder)</span> &#123;</span><br><span class="line">    <span class="built_in">super</span>(builder);</span><br><span class="line">    Assert.notNull(builder.restClient, <span class="string">&quot;RestClient must not be null&quot;</span>);</span><br><span class="line">    <span class="built_in">this</span>.initializeSchema = builder.initializeSchema;</span><br><span class="line">    <span class="built_in">this</span>.options = builder.options;</span><br><span class="line">    <span class="built_in">this</span>.filterExpressionConverter = builder.filterExpressionConverter;</span><br><span class="line">    <span class="type">String</span> <span class="variable">version</span> <span class="operator">=</span> Version.VERSION == <span class="literal">null</span> ? <span class="string">&quot;Unknown&quot;</span> : Version.VERSION.toString();</span><br><span class="line">    <span class="built_in">this</span>.elasticsearchClient = (ElasticsearchClient)(<span class="keyword">new</span> <span class="title class_">ElasticsearchClient</span>(<span class="keyword">new</span> <span class="title class_">RestClientTransport</span>(builder.restClient, <span class="keyword">new</span> <span class="title class_">JacksonJsonpMapper</span>((<span class="keyword">new</span> <span class="title class_">ObjectMapper</span>()).configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, <span class="literal">false</span>))))).withTransportOptions((t) -&gt; t.addHeader(<span class="string">&quot;user-agent&quot;</span>, <span class="string">&quot;spring-ai elastic-java/&quot;</span> + version));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>主要看ElasticsearchClient的创建这一部分，Spring AI直接new JacksonJsonpMapper()，导致博主无论怎么配置Bean都无效，那怎么办，Spring AI设计太不灵活，博主就只能”越狱“了</p><h4 id="使用Java的”不讲武德“工具——反射！！！"><a href="#使用Java的”不讲武德“工具——反射！！！" class="headerlink" title="使用Java的”不讲武德“工具——反射！！！"></a>使用Java的”不讲武德“工具——反射！！！</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ElasticsearchVectorStore</span>(ElasticsearchVectorStore</span><br><span class="line">        .builder(elasticsearchRestClient, embeddingModel)</span><br><span class="line">        .initializeSchema(<span class="literal">true</span>)</span><br><span class="line">        .options(vectorStoreOptions)</span><br><span class="line">        .batchingStrategy(batchingStrategy)) &#123;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 使用反射注入替换 final 字段</span></span><br><span class="line">            <span class="type">Field</span> <span class="variable">field</span> <span class="operator">=</span> ElasticsearchVectorStore.class.getDeclaredField(<span class="string">&quot;elasticsearchClient&quot;</span>);</span><br><span class="line">            field.setAccessible(<span class="literal">true</span>);</span><br><span class="line">            field.set(<span class="built_in">this</span>, elasticsearchClient);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">RuntimeException</span>(<span class="string">&quot;Failed to override elasticsearchClient&quot;</span>, e);</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>直接new一个匿名的ElasticsearchVectorStore类，然后在匿名类中声明一个静态代码块强制覆盖ElasticsearchClient，直接美美解决序列化异常问题啦~</p><p>就看Spring AI团段后续会不会改成传入ElasticsearchRestClient而不是RestClient，或者其他更为灵活的方法。</p><p>目前博主这种方式是最为有效的方式了！</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;博主在使用Spring AI的ElasticsearchVectorStore实现ES向量存储的时候，想在metadata中存一个date字段，实体代码如下：&lt;/p&gt;
&lt;figure class=&quot;highlight java&quot;&gt;&lt;table&gt;&lt;tr&gt;&lt;td</summary>
        
      
    
    
    
    <category term="SpringAI" scheme="https://www.wotemo.com/categories/SpringAI/"/>
    
    
    <category term="SpringAI" scheme="https://www.wotemo.com/tags/SpringAI/"/>
    
    <category term="Elasticsearch" scheme="https://www.wotemo.com/tags/Elasticsearch/"/>
    
    <category term="Jackson" scheme="https://www.wotemo.com/tags/Jackson/"/>
    
  </entry>
  
  <entry>
    <title>基于Redisson实现任务的多实例竞争控制与重试机制</title>
    <link href="https://www.wotemo.com/posts/11620.html"/>
    <id>https://www.wotemo.com/posts/11620.html</id>
    <published>2025-03-15T12:59:03.000Z</published>
    <updated>2025-03-15T12:59:03.000Z</updated>
    
    <content type="html"><![CDATA[<p>在分布式系统中，多个实例可能会同时尝试执行某些任务，例如监听数据库变更、执行定时任务等。为了避免任务的重复执行，我们通常需要一种竞争控制机制，以确保只有一个实例能够执行任务，同时在失败时支持自动重试。本文介绍一种基于 Redis 分布式锁的解决方案，结合 Redisson 和 MongoDB Change Streams，实现任务的多实例竞争控制与自动重试。</p><h2 id="方案背景"><a href="#方案背景" class="headerlink" title="方案背景"></a>方案背景</h2><p>在 <strong>矿小圈</strong> 中，需要从 MongoDB 同步数据到 Elasticsearch，以实现高效的搜索功能。由于该项目是 <strong>微服务架构</strong>，但 <strong>MongoDB Change Streams 监听只能由一个实例执行</strong>，因此需要一种机制来确保在多个实例部署的情况下，<strong>只有一个实例</strong> 监听 MongoDB 的变更，同时为了 <strong>避免单实例负载过高</strong>，需要 <strong>定期释放锁</strong>，让其他实例有机会获取锁并执行任务。这就形成了 <strong>多实例竞争唯一任务的机制</strong>。</p><h2 id="方案概述"><a href="#方案概述" class="headerlink" title="方案概述"></a>方案概述</h2><p>本方案基于 Redis 的分布式锁来确保 <strong>多个实例竞争一个任务</strong>，即 <strong>MongoDB Change Streams 监听</strong>。</p><ul><li><strong>单实例监听</strong>：同一时间只能有一个实例在监听 MongoDB。</li><li><strong>定期释放锁</strong>：避免某个实例长期持有任务，导致压力过大。</li><li><strong>自动重试</strong>：监听失败或实例崩溃时，其他实例会重新竞争获取锁。</li><li><strong>任务高可用</strong>：防止某个实例崩溃导致任务丢失。</li></ul><h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><h3 id="抽象监听器类"><a href="#抽象监听器类" class="headerlink" title="抽象监听器类"></a>抽象监听器类</h3><p>我们定义一个 <code>AbstractMongoChangeStreamsListener</code> 抽象类，封装 Redis 锁的获取、释放及 MongoDB 监听的核心逻辑。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">class</span> <span class="title class_">AbstractMongoChangeStreamsListener</span> &#123;</span><br><span class="line">    <span class="keyword">protected</span> MongoTemplate mongoTemplate;</span><br><span class="line">    <span class="keyword">protected</span> RedisTemplate&lt;String, String&gt; redisTemplate;</span><br><span class="line">    <span class="keyword">protected</span> RedissonClient redissonClient;</span><br><span class="line">    <span class="keyword">private</span> RLock lock;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ScheduledExecutorService</span> <span class="variable">listeningScheduler</span> <span class="operator">=</span> Executors.newSingleThreadScheduledExecutor();</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">ExecutorService</span> <span class="variable">restartExecutor</span> <span class="operator">=</span> Executors.newSingleThreadExecutor();</span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">locked</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> <span class="type">AtomicBoolean</span> <span class="variable">isRestarting</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicBoolean</span>(<span class="literal">false</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">long</span> requestId;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="keyword">final</span> String COLLECTION;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> String LOCK_KEY;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">TRY_LOCK_TIME</span> <span class="operator">=</span> <span class="number">5</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">LOCK_EXPIRE_TIME</span> <span class="operator">=</span> <span class="number">30</span>;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">LOCK_RENEW_INTERVAL</span> <span class="operator">=</span> LOCK_EXPIRE_TIME / <span class="number">3</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">protected</span> <span class="title function_">AbstractMongoChangeStreamsListener</span><span class="params">(</span></span><br><span class="line"><span class="params">            MongoTemplate mongoTemplate,</span></span><br><span class="line"><span class="params">            RedisTemplate&lt;String, String&gt; redisTemplate,</span></span><br><span class="line"><span class="params">            RedissonClient redissonClient,</span></span><br><span class="line"><span class="params">            String collection</span></span><br><span class="line"><span class="params">    )</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.mongoTemplate = mongoTemplate;</span><br><span class="line">        <span class="built_in">this</span>.redisTemplate = redisTemplate;</span><br><span class="line">        <span class="built_in">this</span>.redissonClient = redissonClient;</span><br><span class="line">        <span class="built_in">this</span>.COLLECTION = collection;</span><br><span class="line">        <span class="built_in">this</span>.LOCK_KEY = <span class="string">&quot;mongo:change_streams_lock:&quot;</span> + collection;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@PostConstruct</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">startListening</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.lock = redissonClient.getLock(LOCK_KEY);</span><br><span class="line">        tryToAcquireLockAndListen();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">tryToAcquireLockAndListen</span><span class="params">()</span> &#123;</span><br><span class="line">        listeningScheduler.scheduleWithFixedDelay(() -&gt; &#123;</span><br><span class="line">            requestId = Thread.currentThread().threadId();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (lock.tryLock(TRY_LOCK_TIME, LOCK_EXPIRE_TIME, TimeUnit.SECONDS)) &#123;</span><br><span class="line">                    log.info(<span class="string">&quot;[&#123;&#125;] 🔓 获取到 Redis 锁，开始监听 MongoDB Change Streams&quot;</span>, COLLECTION);</span><br><span class="line">                    locked = <span class="literal">true</span>;</span><br><span class="line">                    watchMongoChangeStreams();</span><br><span class="line">                    lock.unlock();</span><br><span class="line">                    locked = <span class="literal">false</span>;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    log.info(<span class="string">&quot;[&#123;&#125;] 🔒 未获取到 Redis 锁，等待下次尝试&quot;</span>, COLLECTION);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                log.error(<span class="string">&quot;[&#123;&#125;] Redis 锁获取 &amp; MongoDB Change Streams 监听异常，准备重启 | &quot;</span>, COLLECTION, e);</span><br><span class="line">                stopListeningAndRestart();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;, TRY_LOCK_TIME, TRY_LOCK_TIME, TimeUnit.SECONDS);</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"><span class="comment">     */</span></span><br><span class="line">    <span class="meta">@Scheduled(cron = &quot;*/30 * * * * *&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">void</span> <span class="title function_">releaseLock</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (lock.isLocked()) &#123;</span><br><span class="line">            log.info(<span class="string">&quot;[&#123;&#125;] 🔓 定时任务释放 Redis 锁&quot;</span>, COLLECTION);</span><br><span class="line">            lock.forceUnlock();</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">private</span> <span class="keyword">void</span> <span class="title function_">stopListeningAndRestart</span><span class="params">()</span> &#123;</span><br><span class="line">        restartExecutor.submit(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">if</span> (!isRestarting.compareAndSet(<span class="literal">false</span>, <span class="literal">true</span>)) &#123;</span><br><span class="line">                <span class="keyword">return</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            locked = <span class="literal">false</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                log.info(<span class="string">&quot;[&#123;&#125;] 🔄 重新启动监听&quot;</span>, COLLECTION);</span><br><span class="line">                tryToAcquireLockAndListen();</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                isRestarting.set(<span class="literal">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="keyword">public</span> <span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title function_">watchMongoChangeStreams</span><span class="params">()</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="具体监听器实现"><a href="#具体监听器实现" class="headerlink" title="具体监听器实现"></a>具体监听器实现</h3><p><code>TagChangeStreamsListener</code> 继承 <code>AbstractMongoChangeStreamsListener</code>，监听 <code>tag</code> 集合的变更。</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TagChangeStreamsListener</span> <span class="keyword">extends</span> <span class="title class_">AbstractMongoChangeStreamsListener</span> &#123;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    TagChangeStreamsListener(</span><br><span class="line">            MongoTemplate mongoTemplate,</span><br><span class="line">            RedisTemplate&lt;String, String&gt; redisTemplate,</span><br><span class="line">            RedissonClient redissonClient</span><br><span class="line">    ) &#123;</span><br><span class="line">        <span class="built_in">super</span>(mongoTemplate, redisTemplate, redissonClient, <span class="string">&quot;tag&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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">watchMongoChangeStreams</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="type">var</span> <span class="variable">cursor</span> <span class="operator">=</span> mongoTemplate.getCollection(COLLECTION)</span><br><span class="line">                .watch()</span><br><span class="line">                .cursor();</span><br><span class="line">        <span class="keyword">while</span> (locked &amp;&amp; cursor.hasNext()) &#123;</span><br><span class="line">            ChangeStreamDocument&lt;Document&gt; next = cursor.next();</span><br><span class="line">            log.info(<span class="string">&quot;[&#123;&#125;] 🔍 ResumeToken: &#123;&#125;&quot;</span>, COLLECTION, next.getResumeToken());</span><br><span class="line">            log.info(<span class="string">&quot;[&#123;&#125;] 🔍 MongoDB Change Streams 数据: &#123;&#125;&quot;</span>, COLLECTION, next.getDocumentKey());</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>完整代码可在<a href="https://github.com/wtmxxx/flying-forum">FlyingForum</a>获取：</p><p>search模块 com.atcumt.search.listener.mongo.template包 AbstractMongoChangeStreamsListener抽象类</p><h2 id="方案优势"><a href="#方案优势" class="headerlink" title="方案优势"></a>方案优势</h2><ul><li><strong>单实例监听，避免重复消费</strong>：确保 MongoDB Change Streams 只有一个实例在监听。</li><li><strong>多实例竞争，均衡负载</strong>：定期释放锁，使不同实例都有机会监听，防止单实例压力过大。</li><li><strong>自动重试，提升可用性</strong>：任务异常时，其他实例可以接管任务，防止数据丢失。</li></ul><h2 id="适用场景"><a href="#适用场景" class="headerlink" title="适用场景"></a>适用场景</h2><ul><li><strong>MongoDB 数据同步到 Elasticsearch</strong>：确保搜索数据的实时性。</li><li><strong>定时任务调度</strong>：多实例情况下避免任务重复执行。</li></ul><h2 id="结论"><a href="#结论" class="headerlink" title="结论"></a>结论</h2><p>本文详细介绍了 <strong>矿小圈</strong> 如何基于 Redis 分布式锁，实现 <strong>MongoDB Change Streams 监听的多实例竞争</strong>，并通过定期释放锁的方式，实现 <strong>任务均衡分配</strong>，确保任务高可用、高可靠性。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;在分布式系统中，多个实例可能会同时尝试执行某些任务，例如监听数据库变更、执行定时任务等。为了避免任务的重复执行，我们通常需要一种竞争控制机制，以确保只有一个实例能够执行任务，同时在失败时支持自动重试。本文介绍一种基于 Redis 分布式锁的解决方案，结合 Redisson</summary>
        
      
    
    
    
    <category term="Redis" scheme="https://www.wotemo.com/categories/Redis/"/>
    
    <category term="分布式" scheme="https://www.wotemo.com/categories/Redis/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    
    
    <category term="Redis" scheme="https://www.wotemo.com/tags/Redis/"/>
    
    <category term="分布式锁" scheme="https://www.wotemo.com/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/"/>
    
    <category term="多实例竞争" scheme="https://www.wotemo.com/tags/%E5%A4%9A%E5%AE%9E%E4%BE%8B%E7%AB%9E%E4%BA%89/"/>
    
    <category term="重试机制" scheme="https://www.wotemo.com/tags/%E9%87%8D%E8%AF%95%E6%9C%BA%E5%88%B6/"/>
    
  </entry>
  
  <entry>
    <title>SpringBoot多种配置导入方式</title>
    <link href="https://www.wotemo.com/posts/16386.html"/>
    <id>https://www.wotemo.com/posts/16386.html</id>
    <published>2025-03-11T12:01:01.000Z</published>
    <updated>2025-03-11T12:01:01.000Z</updated>
    
    <content type="html"><![CDATA[<p>在 Spring Boot 应用开发中，合理管理和加载配置是非常重要的。以往，我们通常使用 <code>bootstrap.yaml/properties</code> 方式来提前导入外部配置。但在 Spring Boot 2.4 及以上版本中，引入了 <code>spring.config.import</code> 机制，提供了更灵活的配置加载方式。</p><h2 id="1-传统的-bootstrap-yaml-properties-方式（Spring-Boot-2-3-及以下）"><a href="#1-传统的-bootstrap-yaml-properties-方式（Spring-Boot-2-3-及以下）" class="headerlink" title="1. 传统的 bootstrap.yaml/properties 方式（Spring Boot 2.3 及以下）"></a>1. 传统的 <code>bootstrap.yaml/properties</code> 方式（Spring Boot 2.3 及以下）</h2><p>在 Spring Boot 2.3 及以下版本，<code>bootstrap.yaml</code>（或 <code>bootstrap.properties</code>）主要用于加载外部配置，例如 Nacos。这种方式通常用于 Spring Cloud 组件初始化阶段。</p><p>示例：</p><figure class="highlight yaml"><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"># bootstrap.yaml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">config:</span></span><br><span class="line">        <span class="attr">name:</span> <span class="string">test.yml</span></span><br><span class="line">        <span class="attr">group:</span> <span class="string">DEFAULT_GROUP</span></span><br><span class="line">        <span class="attr">server-addr:</span> <span class="number">127.0</span><span class="number">.0</span><span class="number">.1</span><span class="string">:8848</span></span><br><span class="line">        <span class="attr">extension-configs:</span></span><br><span class="line">          <span class="bullet">-</span> <span class="attr">dataId:</span> <span class="string">test01.yml</span></span><br><span class="line">            <span class="attr">group:</span> <span class="string">group_01</span></span><br><span class="line">          <span class="bullet">-</span> <span class="attr">dataId:</span> <span class="string">test02.yml</span></span><br><span class="line">            <span class="attr">group:</span> <span class="string">group_02</span></span><br><span class="line">            <span class="attr">refresh:</span> <span class="literal">false</span></span><br></pre></td></tr></table></figure><p><strong>缺点：</strong></p><ul><li><code>bootstrap.yaml</code> 与 <code>application.yaml</code> 配置优先级不同，容易混淆。</li><li>在 Spring Boot 2.4 及以上版本中已被 <code>spring.config.import</code> 取代。</li></ul><h2 id="2-spring-config-import-方式（Spring-Boot-2-4-）"><a href="#2-spring-config-import-方式（Spring-Boot-2-4-）" class="headerlink" title="2. spring.config.import 方式（Spring Boot 2.4+）"></a>2. <code>spring.config.import</code> 方式（Spring Boot 2.4+）</h2><p>从 Spring Boot 2.4 开始，官方推荐使用 <code>spring.config.import</code> 来导入额外的配置源。</p><h3 id="示例："><a href="#示例：" class="headerlink" title="示例："></a>示例：</h3><figure class="highlight yaml"><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"># application.yaml</span></span><br><span class="line"><span class="attr">spring:</span></span><br><span class="line">  <span class="attr">application:</span></span><br><span class="line">    <span class="attr">name:</span> <span class="string">auth-service</span></span><br><span class="line">  <span class="attr">profiles:</span></span><br><span class="line">    <span class="attr">active:</span> <span class="string">dev</span></span><br><span class="line">  <span class="attr">cloud:</span></span><br><span class="line">    <span class="attr">nacos:</span></span><br><span class="line">      <span class="attr">server-addr:</span> <span class="string">$&#123;flying-forum.nacos.server-addr&#125;</span></span><br><span class="line">      <span class="attr">username:</span> <span class="string">$&#123;flying-forum.nacos.username&#125;</span></span><br><span class="line">      <span class="attr">password:</span> <span class="string">$&#123;flying-forum.nacos.password&#125;</span></span><br><span class="line">  <span class="attr">config:</span></span><br><span class="line">    <span class="attr">import:</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">optional:classpath:application-$&#123;spring.profiles.active&#125;.yaml</span> <span class="comment"># 导入所在环境对应配置</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">optional:nacos:shared-sentinel.yaml</span> <span class="comment"># 示例：导入Sentinel，如果配置不存在不会报错</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">optional:nacos:test01.yml?group=group_01</span> <span class="comment"># 覆盖默认 group，监听 group_01:test01.yml</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">optional:nacos:test02.yml?group=group_02&amp;refreshEnabled=false</span> <span class="comment"># 不开启动态刷新</span></span><br><span class="line">      <span class="bullet">-</span> <span class="string">nacos:test03.yml</span> <span class="comment"># 在拉取nacos配置异常时会快速失败，会导致 spring 容器启动失败</span></span><br></pre></td></tr></table></figure><p><strong>注意事项：</strong></p><ol><li>如果使用 <code>spring.config.import</code>，就不能使用 <code>bootstrap.yaml/properties</code> 方式了；</li><li>如果引入 <code>spring-cloud-starter-alibaba-nacos-config</code>，但未使用 <code>spring.config.import</code> 方式导入 Nacos 配置，Spring Boot 启动时会报错；</li><li>可以通过 <code>spring.cloud.nacos.config.import-check.enabled=false</code> 关闭 Nacos 配置检查，但不推荐；</li><li>想保留 <code>bootstrap.yaml</code> 方式，可以添加 <code>spring-cloud-starter-bootstrap</code> 依赖。</li></ol><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></pre></td><td class="code"><pre><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>org.springframework.cloud<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>spring-cloud-starter-bootstrap<span class="tag">&lt;/<span class="name">artifactId</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">dependency</span>&gt;</span></span><br></pre></td></tr></table></figure><h2 id="3-其他配置加载方式"><a href="#3-其他配置加载方式" class="headerlink" title="3. 其他配置加载方式"></a>3. 其他配置加载方式</h2><h3 id="通过-EnvironmentPostProcessor-代码方式动态加载配置"><a href="#通过-EnvironmentPostProcessor-代码方式动态加载配置" class="headerlink" title="通过 EnvironmentPostProcessor 代码方式动态加载配置"></a>通过 <code>EnvironmentPostProcessor</code> 代码方式动态加载配置</h3><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"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomConfigLoader</span> <span class="keyword">implements</span> <span class="title class_">EnvironmentPostProcessor</span> &#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">postProcessEnvironment</span><span class="params">(ConfigurableEnvironment environment, SpringApplication application)</span> &#123;</span><br><span class="line">        <span class="type">MapPropertySource</span> <span class="variable">customSource</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">MapPropertySource</span>(<span class="string">&quot;customConfig&quot;</span>,</span><br><span class="line">                Collections.singletonMap(<span class="string">&quot;custom.property&quot;</span>, <span class="string">&quot;customValue&quot;</span>));</span><br><span class="line">        environment.getPropertySources().addLast(customSource);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>然后在 <code>META-INF/spring.factories</code> 或 <code>META-INF/spring/org.springframework.boot.env.EnvironmentPostProcessor</code> 中注册：</p><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">org.springframework.boot.env.EnvironmentPostProcessor=com.example.config.CustomConfigLoader</span><br></pre></td></tr></table></figure><h3 id="通过-PropertySource-读取自定义配置"><a href="#通过-PropertySource-读取自定义配置" class="headerlink" title="通过 @PropertySource 读取自定义配置"></a>通过 <code>@PropertySource</code> 读取自定义配置</h3><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"><span class="meta">@Configuration</span></span><br><span class="line"><span class="meta">@PropertySource(&quot;classpath:custom.properties&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CustomConfig</span> &#123;</span><br><span class="line">    <span class="meta">@Value(&quot;$&#123;custom.property&#125;&quot;)</span></span><br><span class="line">    <span class="keyword">private</span> String customProperty;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="通过命令行参数导入配置"><a href="#通过命令行参数导入配置" class="headerlink" title="通过命令行参数导入配置"></a>通过命令行参数导入配置</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">java -jar my-app.jar --spring.config.location=file:./config/custom-config.yaml</span><br></pre></td></tr></table></figure><h2 id="4-总结"><a href="#4-总结" class="headerlink" title="4. 总结"></a>4. 总结</h2><table><thead><tr><th>方式</th><th>适用场景</th><th>适用版本</th></tr></thead><tbody><tr><td><code>bootstrap.yaml</code></td><td>旧版本 Spring Cloud 配置加载</td><td>2.3 及以下</td></tr><tr><td><code>spring.config.import</code></td><td>远程&#x2F;本地额外配置导入</td><td>2.4+</td></tr><tr><td><code>EnvironmentPostProcessor</code></td><td>动态加载配置</td><td>任意版本</td></tr><tr><td><code>@PropertySource</code></td><td>读取额外配置文件</td><td>任意版本</td></tr><tr><td>命令行参数</td><td>临时指定配置路径</td><td>任意版本</td></tr></tbody></table><p>从 Spring Boot 2.4 开始，<code>spring.config.import</code> 方式是官方推荐的配置加载方式，它比 <code>bootstrap.yaml</code> 更加灵活，也更容易理解。在不同的应用场景下，我们可以选择合适的方式来管理 Spring Boot 配置。</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;在 Spring Boot 应用开发中，合理管理和加载配置是非常重要的。以往，我们通常使用 &lt;code&gt;bootstrap.yaml/properties&lt;/code&gt; 方式来提前导入外部配置。但在 Spring Boot 2.4 及以上版本中，引入了</summary>
        
      
    
    
    
    <category term="SpringBoot" scheme="https://www.wotemo.com/categories/SpringBoot/"/>
    
    <category term="配置" scheme="https://www.wotemo.com/categories/SpringBoot/%E9%85%8D%E7%BD%AE/"/>
    
    
    <category term="SpringBoot" scheme="https://www.wotemo.com/tags/SpringBoot/"/>
    
    <category term="配置" scheme="https://www.wotemo.com/tags/%E9%85%8D%E7%BD%AE/"/>
    
  </entry>
  
  <entry>
    <title>基于Redis分布式锁实现分布式定时任务调度</title>
    <link href="https://www.wotemo.com/posts/42850.html"/>
    <id>https://www.wotemo.com/posts/42850.html</id>
    <published>2025-03-03T13:21:21.000Z</published>
    <updated>2025-03-03T13:21:21.000Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前情提要"><a href="#前情提要" class="headerlink" title="前情提要"></a>前情提要</h2><p>众所不周知：矿小圈使用Sa-Token进行鉴权服务，为了防止”黑客”绕过Gateway网关直接访问内部微服务，使用Same-Token进行网关请求鉴定，拿到相同的Same-Token才能请求非网关服务</p><p>为了保证Same-Token不被”非法分子”破解，Same-Token采用定时刷新策略，每隔10分钟就会更新一次，因此，想要获取Same-Token并不是一件容易的事情</p><h2 id="BUG现形"><a href="#BUG现形" class="headerlink" title="BUG现形"></a>BUG现形</h2><p>单例模式下，使用SpringBoot的<code>@Scheduled</code>是一件非常”爽”的事情，如下很容易定义了一个定时任务每隔10分钟刷新一次Same-Token</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"><span class="meta">@Scheduled(cron = &quot;0 0/10 * * * ?&quot;)</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">refreshToken</span><span class="params">()</span> &#123;</span><br><span class="line">SaSameUtil.refreshToken();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>但是，在分布式、微服务场景下，同一个类型的服务可能会部署多个实例以达到高可用，于此情景下，再直接使用<code>@Scheduled</code>会导致同一个任务被重复多次调用</p><h2 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h2><p>如果我们不希望同一任务重复调用，就要用到分布式任务调度技术，市面上常见的有XXL-JOB、Quartz、ElasticJob、scheduleX，不过这些都需要引入新的服务，目前矿小圈就仅有这一个分布式任务，所以我手动实现了一个简易的类似于ShedLock的Redis分布式锁，实现分布式定时任务调度</p><p><strong>RedisLockUtil是我自定义的Redis分布式锁工具类，核心代码如下：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">RedisLockUtil</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RedisTemplate&lt;String, String&gt; redisTemplate;</span><br><span class="line">    <span class="keyword">private</span> <span class="type">long</span> LOCK_EXPIRE_TIME;</span><br><span class="line">    <span class="keyword">private</span> TimeUnit LOCK_EXPIRE_TIME_UNIT;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="title function_">RedisLockUtil</span><span class="params">(RedisTemplate&lt;String, String&gt; redisTemplate, <span class="type">long</span> expireTime, TimeUnit expireTimeUnit)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.redisTemplate = redisTemplate;</span><br><span class="line">        <span class="built_in">this</span>.LOCK_EXPIRE_TIME = expireTime;</span><br><span class="line">        <span class="built_in">this</span>.LOCK_EXPIRE_TIME_UNIT = expireTimeUnit;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> RedisLockUtil <span class="title function_">create</span><span class="params">(RedisTemplate&lt;String, String&gt; redisTemplate, <span class="type">long</span> expireTime, TimeUnit expireTimeUnit)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RedisLockUtil</span>(redisTemplate, expireTime, expireTimeUnit);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> RedisLockUtil <span class="title function_">create</span><span class="params">(RedisTemplate&lt;String, String&gt; redisTemplate)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">RedisLockUtil</span>(redisTemplate, <span class="number">30</span>, TimeUnit.SECONDS);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setLockExpireTime</span><span class="params">(<span class="type">long</span> expireTime, TimeUnit expireTimeUnit)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.LOCK_EXPIRE_TIME = expireTime;</span><br><span class="line">        <span class="built_in">this</span>.LOCK_EXPIRE_TIME_UNIT = expireTimeUnit;</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"><span class="comment">     * <span class="doctag">@param</span> lockKey 锁的 Key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> requestId 请求标识（用于区分不同实例线程）</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> expireTime 过期时间（秒）</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 是否加锁成功</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">acquireLock</span><span class="params">(String lockKey, String requestId, <span class="type">long</span> expireTime, TimeUnit expireTimeUnit)</span> &#123;</span><br><span class="line">        <span class="type">Boolean</span> <span class="variable">success</span> <span class="operator">=</span> redisTemplate.opsForValue().setIfAbsent(lockKey, requestId, expireTime, expireTimeUnit);</span><br><span class="line">        <span class="keyword">return</span> Boolean.TRUE.equals(success);</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"><span class="comment">     * <span class="doctag">@param</span> lockKey 锁的 Key</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> expireTime 过期时间（秒）</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> 是否加锁成功</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">tryLock</span><span class="params">(String lockKey)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> acquireLock(lockKey, <span class="string">&quot;1&quot;</span>, LOCK_EXPIRE_TIME, LOCK_EXPIRE_TIME_UNIT);</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>此工具类需要用户自己传入RedisTemplate，acquireLock方法通过Redis原子操作setIfAbsent实现锁的获取，如果锁不存在并设置成功就拿到了锁，然后可以执行任务，如果锁存在设置不会成功，获取锁失败，就不能执行任务，requestId是请求标识，可以实现锁的续期与释放，防止其他实例（线程）偷锁，目前Same-Token的刷新用不到锁的续期与释放，故使用tryLock将requestId,设置为固定的”1”，相当于Boolean的True，也就是有实例（线程）拿到锁了</p><p><strong>以下是改进后的SameToken刷新任务：</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></pre></td><td class="code"><pre><span class="line"><span class="meta">@Component</span></span><br><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SaSameTokenRefreshTask</span> &#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> RedisLockUtil redisLockUtil;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">String</span> <span class="variable">LOCK_KEY</span> <span class="operator">=</span> <span class="string">&quot;Authorization:var:same-token-refresh-lock&quot;</span>;</span><br><span class="line">    <span class="meta">@Autowired</span></span><br><span class="line">    SaSameTokenRefreshTask(<span class="meta">@Qualifier(&quot;saRedisTemplate&quot;)</span> RedisTemplate&lt;String, String&gt; saRedisTemplate) &#123;</span><br><span class="line">        <span class="built_in">this</span>.redisLockUtil = RedisLockUtil.create(saRedisTemplate, <span class="number">5</span>, TimeUnit.MINUTES);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 每隔 10 分钟刷新一次 Same-Token</span></span><br><span class="line">    <span class="meta">@Scheduled(cron = &quot;0 0/10 * * * ?&quot;)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">refreshToken</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (redisLockUtil.tryLock(LOCK_KEY)) &#123;</span><br><span class="line">            log.info(<span class="string">&quot;🔒 拿到刷新锁，开始刷新 Same-Token&quot;</span>);</span><br><span class="line">            SaSameUtil.refreshToken();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            log.info(<span class="string">&quot;🔒 Same-Token 刷新锁已被占用，跳过刷新&quot;</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><p>每隔10分钟，各个服务会争夺Redis分布式锁，拿到锁的才可以执行刷新操作，如此就只会有一个服务执行刷新任务，不会重复刷新Same-Token，拿到锁5分钟后，锁会被自动释放，不会导致死锁发生</p><p><strong>实测如下：</strong></p><p>[scheduling-1] c.a.auth.task.SaSameTokenRefreshTask     : 🔒 Same-Token 刷新锁已被占用，跳过刷新<br>[scheduling-1] c.a.auth.task.SaSameTokenRefreshTask     : 🔒 拿到刷新锁，开始刷新 Same-Token<br>[scheduling-1] c.a.auth.task.SaSameTokenRefreshTask     : 🔒 Same-Token 刷新锁已被占用，跳过刷新</p><h2 id="源码获取"><a href="#源码获取" class="headerlink" title="源码获取"></a>源码获取</h2><p>此文章用到的所有代码及案例均可从Github开源项目<a href="https://github.com/wtmxxx/flying-forum">FlyingForum（矿小圈后端）</a>获取，如果对你有帮助，劳烦您为此项目点一个Star支持作者，如果您发现此项目有任何Bug或者改进之处，欢迎提Issue和PR</p><p>RedisLockUtil工具类：项目common模块下的utils包</p><p>Same-Token定时任务：项目auth模块下的task包</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h2 id=&quot;前情提要&quot;&gt;&lt;a href=&quot;#前情提要&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="Redis" scheme="https://www.wotemo.com/categories/Redis/"/>
    
    <category term="分布式" scheme="https://www.wotemo.com/categories/Redis/%E5%88%86%E5%B8%83%E5%BC%8F/"/>
    
    
    <category term="Redis" scheme="https://www.wotemo.com/tags/Redis/"/>
    
    <category term="分布式锁" scheme="https://www.wotemo.com/tags/%E5%88%86%E5%B8%83%E5%BC%8F%E9%94%81/"/>
    
    <category term="定时任务" scheme="https://www.wotemo.com/tags/%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1/"/>
    
  </entry>
  
  <entry>
    <title>MongoDB踩坑日志</title>
    <link href="https://www.wotemo.com/posts/15102.html"/>
    <id>https://www.wotemo.com/posts/15102.html</id>
    <published>2024-12-22T15:24:55.000Z</published>
    <updated>2024-12-22T15:24:55.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>搞了老长时间，MongoDB真的我哭死🥹</p></blockquote><h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>想着MongoDB不是支持事务了嘛，我就想在服务里面加上MongoDB的事务功能，然后百度到使用MongoDB事务，必须要开启副本集才行，然后顺藤摸瓜，就跑去弄了三个副本集，以下主要是配置<strong>副本集</strong>的踩坑日志</p><h3 id="踩坑一：副本集鉴权"><a href="#踩坑一：副本集鉴权" class="headerlink" title="踩坑一：副本集鉴权"></a>踩坑一：副本集鉴权</h3><p>MongoDB如果配置用户名密码鉴权，就必须要在各个服务之间加上公用的<code>keyfile</code></p><p>自己生成一个随机的<code>keyfile</code></p><figure class="highlight bash"><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">openssl rand -<span class="built_in">base64</span> 756 &gt; &lt;path-to-keyfile&gt;</span><br><span class="line"><span class="built_in">chmod</span> 400 &lt;path-to-keyfile&gt;</span><br></pre></td></tr></table></figure><p>在conf中加入如下配置</p><figure class="highlight yaml"><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="attr">security:</span></span><br><span class="line">  <span class="attr">authorization:</span> <span class="string">enabled</span>  <span class="comment"># 启用身份验证</span></span><br><span class="line">  <span class="attr">keyFile:</span> <span class="string">&lt;path-to-keyfile&gt;</span></span><br></pre></td></tr></table></figure><h3 id="踩坑二：不能获取密钥文件"><a href="#踩坑二：不能获取密钥文件" class="headerlink" title="踩坑二：不能获取密钥文件"></a>踩坑二：不能获取密钥文件</h3><p>报错如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="punctuation">&#123;</span><span class="attr">&quot;error&quot;</span><span class="punctuation">:</span><span class="string">&quot;Location5579201: Unable to acquire security key[s]&quot;</span><span class="punctuation">&#125;</span></span><br></pre></td></tr></table></figure><p>这是因为你的文件<strong>权限</strong>太低或者太高了（MongoDB会检查文件权限）</p><p>MongoDB官方要求<code>keyfile</code>的权限为400，并且<code>mongodb</code>为所有者</p><p>在Docker挂载卷设置这个权限还不行，要在Docker容器内部设置</p><p>所以在容器启动MongoDB服务之前，你必须要设置好容器内的<code>keyfile</code>文件权限</p><p>在你的启动文件前加上如下代码</p><figure class="highlight sh"><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="built_in">chown</span> mongodb:mongodb &lt;path-to-keyfile&gt;</span><br><span class="line"><span class="built_in">chmod</span> 400 &lt;path-to-keyfile&gt;</span><br></pre></td></tr></table></figure><p>这样就不会报错了</p><h3 id="踩坑三：环境变量配置鉴权不生效"><a href="#踩坑三：环境变量配置鉴权不生效" class="headerlink" title="踩坑三：环境变量配置鉴权不生效"></a>踩坑三：环境变量配置鉴权不生效</h3><p>使用环境变量初始化用户名和密码</p><figure class="highlight yaml"><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="attr">environment:</span></span><br><span class="line">  <span class="attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="string">&lt;username&gt;</span></span><br><span class="line">  <span class="attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="string">&lt;password&gt;</span></span><br></pre></td></tr></table></figure><p>罪魁祸首如下：</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">command:</span> [<span class="string">&quot;/bin/bash&quot;</span>, <span class="string">&quot;/entrypoint.sh&quot;</span>]</span><br></pre></td></tr></table></figure><p>使用<code>command</code>命令之后，Docker不会执行MongoDB官网提供的<code>docker-entrypoint.sh</code>脚本，而从环境变量初始化用户名和密码就是通过官方提供的启动脚本进行的，所以指定command之后，环境变量不会生效</p><p>采取措施：在自己的<code>sh</code>脚本下使用官方的<code>sh</code>脚本启动MongoDB</p><figure class="highlight sh"><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">MONGO_CONFIG=<span class="string">&quot;/etc/mongo/mongod.conf&quot;</span></span><br><span class="line"><span class="comment"># 启动官方脚本（附带自定义配置）</span></span><br><span class="line">/usr/local/bin/docker-entrypoint.sh --config <span class="variable">$MONGO_CONFIG</span></span><br></pre></td></tr></table></figure><h3 id="踩坑四：使用容器名初始化副本集"><a href="#踩坑四：使用容器名初始化副本集" class="headerlink" title="踩坑四：使用容器名初始化副本集"></a>踩坑四：使用容器名初始化副本集</h3><p>错误示例如下：</p><figure class="highlight js"><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">rs.<span class="title function_">initiate</span>(&#123;</span><br><span class="line">    <span class="attr">_id</span>: <span class="string">&quot;rs0&quot;</span>,</span><br><span class="line">    <span class="attr">members</span>: [</span><br><span class="line">        &#123; <span class="attr">_id</span>: <span class="number">0</span>, <span class="attr">host</span>: <span class="string">&quot;mongo1:27017&quot;</span> &#125;,</span><br><span class="line">        &#123; <span class="attr">_id</span>: <span class="number">1</span>, <span class="attr">host</span>: <span class="string">&quot;mongo2:27018&quot;</span> &#125;,</span><br><span class="line">        &#123; <span class="attr">_id</span>: <span class="number">2</span>, <span class="attr">host</span>: <span class="string">&quot;mongo3:27019&quot;</span> &#125;</span><br><span class="line">    ]</span><br><span class="line">&#125;);</span><br></pre></td></tr></table></figure><p>这样会导致连接不上MongoDB副本集，因为MongoDB真的会直接返回容器名，不会返回真实域名或者IP</p><p>（另外，MongoDB提倡使用固定域名初始化副本集，防止IP地址变更造成的麻烦）</p><p>官方正确示例如下，直接指定域名或者IP：</p><figure class="highlight js"><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">rs.<span class="title function_">initiate</span>( &#123;</span><br><span class="line">   _id : <span class="string">&quot;rs0&quot;</span>,</span><br><span class="line">   <span class="attr">members</span>: [</span><br><span class="line">      &#123; <span class="attr">_id</span>: <span class="number">0</span>, <span class="attr">host</span>: <span class="string">&quot;mongodb0.example.net:27017&quot;</span> &#125;,</span><br><span class="line">      &#123; <span class="attr">_id</span>: <span class="number">1</span>, <span class="attr">host</span>: <span class="string">&quot;mongodb1.example.net:27017&quot;</span> &#125;,</span><br><span class="line">      &#123; <span class="attr">_id</span>: <span class="number">2</span>, <span class="attr">host</span>: <span class="string">&quot;mongodb2.example.net:27017&quot;</span> &#125;</span><br><span class="line">   ]</span><br><span class="line">&#125;)</span><br></pre></td></tr></table></figure><h3 id="撒花完结"><a href="#撒花完结" class="headerlink" title="撒花完结"></a>撒花完结</h3><p>踩完以上坑后，就可以美美使用MongoDB<strong>副本集</strong>和<strong>事务</strong>啦🌸🌸🌸</p><p>另外，附上我的Docker Compose</p><figure class="highlight yaml"><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="attr">mongo1:</span></span><br><span class="line">  <span class="attr">image:</span> <span class="string">mongo:latest</span></span><br><span class="line">  <span class="attr">container_name:</span> <span class="string">mongo1</span></span><br><span class="line">  <span class="attr">hostname:</span> <span class="string">mongo1</span></span><br><span class="line">  <span class="attr">ports:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">&quot;27017:27017&quot;</span></span><br><span class="line">  <span class="attr">environment:</span></span><br><span class="line">    <span class="attr">MONGO_INITDB_ROOT_USERNAME:</span> <span class="string">&lt;username&gt;</span></span><br><span class="line">    <span class="attr">MONGO_INITDB_ROOT_PASSWORD:</span> <span class="string">&lt;password&gt;</span></span><br><span class="line">  <span class="attr">volumes:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">./cumt-forum/mongo/data/mongo1:/data/db</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">./cumt-forum/mongo/mongo-keyfile:/data/mongo-keyfile</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">./cumt-forum/mongo/conf/mongod.conf:/etc/mongo/mongod.conf</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">./cumt-forum/mongo/conf/entrypoint.sh:/entrypoint.sh</span></span><br><span class="line">  <span class="attr">command:</span> [<span class="string">&quot;/bin/bash&quot;</span>, <span class="string">&quot;/entrypoint.sh&quot;</span>]</span><br><span class="line">  <span class="attr">depends_on:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">mongo2</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">mongo3</span></span><br><span class="line">  <span class="attr">restart:</span> <span class="string">always</span></span><br><span class="line">  <span class="attr">networks:</span></span><br><span class="line">    <span class="bullet">-</span> <span class="string">cumt-forum</span></span><br></pre></td></tr></table></figure><p>还有启动脚本</p><figure class="highlight sh"><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="meta">#!/bin/bash</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置密钥文件的权限</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Setting permissions for keyfile...&quot;</span></span><br><span class="line"><span class="built_in">chown</span> mongodb:mongodb /data/mongo-keyfile</span><br><span class="line"><span class="built_in">chmod</span> 400 /data/mongo-keyfile</span><br><span class="line"></span><br><span class="line"><span class="comment"># 设置 MongoDB 配置文件</span></span><br><span class="line">MONGO_CONFIG=<span class="string">&quot;/etc/mongo/mongod.conf&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 启动 MongoDB 服务</span></span><br><span class="line"><span class="built_in">echo</span> <span class="string">&quot;Starting MongoDB with config file: <span class="variable">$MONGO_CONFIG</span>&quot;</span></span><br><span class="line">/usr/local/bin/docker-entrypoint.sh --config <span class="variable">$MONGO_CONFIG</span></span><br></pre></td></tr></table></figure><blockquote><p>如果解决了你的问题、或者还有其他问题，欢迎在评论区留言</p></blockquote>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;搞了老长时间，MongoDB真的我哭死🥹&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="MongoDB" scheme="https://www.wotemo.com/categories/MongoDB/"/>
    
    <category term="踩坑" scheme="https://www.wotemo.com/categories/MongoDB/%E8%B8%A9%E5%9D%91/"/>
    
    
    <category term="MongoDB" scheme="https://www.wotemo.com/tags/MongoDB/"/>
    
    <category term="踩坑" scheme="https://www.wotemo.com/tags/%E8%B8%A9%E5%9D%91/"/>
    
    <category term="副本集" scheme="https://www.wotemo.com/tags/%E5%89%AF%E6%9C%AC%E9%9B%86/"/>
    
  </entry>
  
  <entry>
    <title>音乐专赏 - Let Her Go</title>
    <link href="https://www.wotemo.com/posts/17449.html"/>
    <id>https://www.wotemo.com/posts/17449.html</id>
    <published>2024-06-11T14:15:55.000Z</published>
    <updated>2024-06-11T15:45:45.000Z</updated>
    
    <content type="html"><![CDATA[<center><span style="font-size: larger;"><strong><div class="note purple modern"><p>Let Her Go - Passenger</p></div></strong></span></center><h2 id="单曲「Let-Her-Go」"><a href="#单曲「Let-Her-Go」" class="headerlink" title="单曲「Let Her Go」"></a>单曲「Let Her Go」</h2>    <div id="aplayer-LPKTxzzk" class="aplayer aplayer-tag-marker meting-tag-marker"         data-id="28242472" data-server="netease" data-type="song" data-mode="circulation" data-autoplay="false" data-mutex="true" data-listmaxheight="340px" data-preload="none" data-theme="#993399"    ></div><h2 id="MV-Youtube"><a href="#MV-Youtube" class="headerlink" title="MV | Youtube"></a>MV | Youtube</h2><div id="article-container">  <center><div id="iframe-container"></div></center></div><script>  window.onload = function() {    var articleContainer = document.getElementById('article-container');    var iframeContainer = document.getElementById('iframe-container');    var containerWidth = articleContainer.offsetWidth;    var aspectRatio = 16 / 9;    var iframe = document.createElement('iframe');    iframe.src = "https://www.youtube.com/embed/HTcL9WkB_wg?si=NB0m78Q2D-VF1WwE";    iframe.setAttribute("frameborder", "0");    iframe.setAttribute("allowfullscreen", "");    iframe.style.width = "100%";    iframe.style.height = (containerWidth / aspectRatio) + "px";    iframeContainer.appendChild(iframe);  };</script><!-- <center><iframe width="560" height="315" src="https://www.youtube.com/embed/HTcL9WkB_wg?si=NB0m78Q2D-VF1WwE" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe></center> --><hr/><center>Well you only need the light when it's burning low<br/>只有在朦胧黯淡时才念及灯火光亮</center><center>Only miss the sun when it starts to snow<br/>只有在冰天雪地时才怀念阳光温暖</center><center>Only know you love her when you let her go<br/>只有在已然放手后才始知那是真爱</center><center>Only know you've been high when you're feeling low<br/>只有在身处低谷时才遥想过去峥嵘</center><center>Only hate the road when you're missing home<br/>只有在乡愁涌动时才痛恨旅途遥远</center><center>Only know you love her when you let her go<br/>只有在让她走之后才始知那是真爱</center><center>And you let her go<br/>你却让她走了</center><center>Staring at the bottom of your glass<br/>酒杯已空，醉然凝视。</center><center>Hoping one day you'll make a dream last<br/>希望有一天，你能让梦持续。</center><center>But dreams come slow and they go so fast<br/>但是梦想来的太慢，走得太快。</center><center>You see her when you close your eyes<br/>闭上双眼，整个脑海都是她。</center><center>Maybe one day you'll understand why<br/>也许有一天，你会明白，</center><center>Everything you touch surely dies<br/>自己是如何亲手毁掉了一段爱情</center><center>But you only need the light when it's burning low<br/>但是你只需要朦胧暗淡时的光亮。</center><center>Only miss the sun when it starts to snow<br/>只有在冰天雪地时才怀念阳光温暖</center><center>Only know you love her when you let her go<br/>只有在让她走之后才始知那是真爱</center><center>Only know you've been high when you're feeling low<br/>只有在身处低谷时才遥想过去峥嵘</center><center>Only hate the road when you're missing home<br/>只有在乡愁涌动时才痛恨旅途遥远</center><center>Only know you love her when you let her go<br/>只有在让她走之后才始知那是真爱</center><center>Staring at the ceiling in the dark<br/>黑夜中你痴痴地望着天花板</center><center>Same old empty feeling in your heart<br/>又是熟悉的空虚寂寞侵袭着你的心</center><center>Cause love comes slow and it goes so fast<br/>爱情来的太慢，走得太快</center><center>Well you see her when you fall asleep<br/>如今你只得在梦里与她相见</center><center>But never to touch and never to keep<br/>可这梦脆弱的经不起触碰 经不起长久'</center><center>Cause you loved her too much and you dive too deep<br/>你爱她越深 你亦而陷的越深</center><center>Well you only need the light when it's burning low<br/>只有在朦胧黯淡时才念及灯火光亮</center><center>Only miss the sun when it starts to snow<br/>只有在冰天雪地时才怀念阳光温暖</center><center>Only know you love her when you let her go<br/>只有在已然放手后才始知那是真爱</center><center>Only know you've been high when you're feeling low<br/>只有在身处低谷时才遥想过去峥嵘</center><center>Only hate the road when you're missing home<br/>只有在乡愁涌动时才痛恨旅途遥远</center><center>Only know you love her when you let her go<br/>只有在让她走之后才始知那是真爱</center><center>And you let her go<br/>你却让她走了</center><center>And you let her go<br/>你却让她走了</center><center>Well you let her go<br/>你却让她走了</center><center>Cause you only need the light when it's burning low<br/>因为你只需要在燃烧时的光</center><center>Only miss the sun when it starts to snow<br/>只有在冰天雪地时才怀念阳光温暖</center><center>Only know you love her when you let her go<br/>只有在让她走之后才始知那是真爱</center><center>Only know you've been high when you're feeling low<br/>只有在身处低谷时才遥想过去峥嵘</center><center>Only hate the road when you're missing home<br/>只有在乡愁涌动时才痛恨旅途遥远</center><center>Only know you love her when you let her go<br/>只有在让她走之后才始知那是真爱</center><center>And you let her go<br/>你却让她走了</center>]]></content>
    
    
    <summary type="html">Passenger - Let Her Go (Feat. Ed Sheeran - Anniversary Edition) [Official Video]</summary>
    
    
    
    
  </entry>
  
  <entry>
    <title>数据结构作业3</title>
    <link href="https://www.wotemo.com/posts/7809.html"/>
    <id>https://www.wotemo.com/posts/7809.html</id>
    <published>2024-06-09T06:16:16.000Z</published>
    <updated>2024-06-09T06:16:16.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>此作业为某大学数据结构作业3，点击下方按钮，输入密码后可查看答案</p></blockquote><h1 id="问题-A-无向图的深度优先搜索"><a href="#问题-A-无向图的深度优先搜索" class="headerlink" title="问题 A: 无向图的深度优先搜索"></a>问题 A: 无向图的深度优先搜索</h1><h3 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h3><p>已知一个无向图G的顶点和边，顶点从0依次编号，现在需要深度优先搜索，访问任一邻接顶点时编号小的顶点优先，请编程输出图G的深度优先搜索序列。</p><h3 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h3><p>第一行是整数m和n（1&lt;m,n&lt;100），分别代表顶点数和边数。后边n行，每行2个数，分别表示一个边的两个顶点。</p><h3 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h3><p>该图从0号顶点开始的深度优先搜索序列。</p><h3 id="样例输入"><a href="#样例输入" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">5 5</span><br><span class="line">0 1</span><br><span class="line">2 0</span><br><span class="line">1 3</span><br><span class="line">1 4</span><br><span class="line">4 2</span><br></pre></td></tr></table></figure><h3 id="样例输出"><a href="#样例输出" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">0 1 3 4 2</span><br></pre></td></tr></table></figure><h3 id="参考答案"><a href="#参考答案" class="headerlink" title="参考答案"></a>参考答案</h3><a class="btn-beautify pink" href="/others/7809-ans.html"   title="查看答案"><i class="far fa-hand-point-right"></i><span>查看答案</span></a><h1 id="问题-B-最小堆的形成"><a href="#问题-B-最小堆的形成" class="headerlink" title="问题 B: 最小堆的形成"></a>问题 B: 最小堆的形成</h1><h3 id="题目描述-1"><a href="#题目描述-1" class="headerlink" title="题目描述"></a>题目描述</h3><p>现在给你n个结点的完全二叉树数组存储序列，请编程调整为最小堆，并输出相应最小堆的存储序列。</p><h3 id="输入-1"><a href="#输入-1" class="headerlink" title="输入"></a>输入</h3><p>第一行是n，第二行是n个结点的完全二叉树数组存储序列。</p><h3 id="输出-1"><a href="#输出-1" class="headerlink" title="输出"></a>输出</h3><p>输出相应最小堆的存储序列。</p><h3 id="样例输入-1"><a href="#样例输入-1" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">8</span><br><span class="line">53 17 78 23 45 65 87 9</span><br></pre></td></tr></table></figure><h3 id="样例输出-1"><a href="#样例输出-1" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">9 17 65 23 45 78 87 53</span><br></pre></td></tr></table></figure><h3 id="参考答案-1"><a href="#参考答案-1" class="headerlink" title="参考答案"></a>参考答案</h3><a class="btn-beautify pink" href="/others/7809-ans.html"   title="查看答案"><i class="far fa-hand-point-right"></i><span>查看答案</span></a><h1 id="问题-C-折半查找的次数"><a href="#问题-C-折半查找的次数" class="headerlink" title="问题 C: 折半查找的次数"></a>问题 C: 折半查找的次数</h1><h3 id="题目描述-2"><a href="#题目描述-2" class="headerlink" title="题目描述"></a>题目描述</h3><p>给你一个无重复数的有序序列，如果采用折半查找的方式，对于给定的数，需要比较几次找到，请编程实现。</p><h3 id="输入-2"><a href="#输入-2" class="headerlink" title="输入"></a>输入</h3><p>第一行是N，表示序列中数的个数，序列最长1000，第二行是一个有序序列，第三行是要找的数x。</p><h3 id="输出-2"><a href="#输出-2" class="headerlink" title="输出"></a>输出</h3><p>如果找到x，输出折半比较的次数，否则输出NO。</p><h3 id="样例输入-2"><a href="#样例输入-2" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">11</span><br><span class="line">5 13 19 21 37 56 64 75 80 88 92</span><br><span class="line">19</span><br></pre></td></tr></table></figure><h3 id="样例输出-2"><a href="#样例输出-2" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2</span><br></pre></td></tr></table></figure><h3 id="参考答案-2"><a href="#参考答案-2" class="headerlink" title="参考答案"></a>参考答案</h3><a class="btn-beautify pink" href="/others/7809-ans.html"   title="查看答案"><i class="far fa-hand-point-right"></i><span>查看答案</span></a><h1 id="问题-D-N个数的排序"><a href="#问题-D-N个数的排序" class="headerlink" title="问题 D: N个数的排序"></a>问题 D: N个数的排序</h1><h3 id="题目描述-3"><a href="#题目描述-3" class="headerlink" title="题目描述"></a>题目描述</h3><p>给你N个自然数，编程输出排序后的这N个数。</p><h3 id="输入-3"><a href="#输入-3" class="headerlink" title="输入"></a>输入</h3><p>第一行是整数的个数N（N&lt;&#x3D;100）。第二行是用空格隔开的N个数。</p><h3 id="输出-3"><a href="#输出-3" class="headerlink" title="输出"></a>输出</h3><p>排序输出N个数，每个数间用一个空格间隔。</p><h3 id="样例输入-3"><a href="#样例输入-3" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">5</span><br><span class="line">9 6 8 7 5</span><br></pre></td></tr></table></figure><h3 id="样例输出-3"><a href="#样例输出-3" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">5 6 7 8 9</span><br></pre></td></tr></table></figure><h3 id="参考答案-3"><a href="#参考答案-3" class="headerlink" title="参考答案"></a>参考答案</h3><a class="btn-beautify pink" href="/others/7809-ans.html"   title="查看答案"><i class="far fa-hand-point-right"></i><span>查看答案</span></a><h1 id="问题-E-班级同学信息查询"><a href="#问题-E-班级同学信息查询" class="headerlink" title="问题 E: 班级同学信息查询"></a>问题 E: 班级同学信息查询</h1><h3 id="题目描述-4"><a href="#题目描述-4" class="headerlink" title="题目描述"></a>题目描述</h3><p>班级里有N个同学，老师希望你编个程序，把每个同学的学号、姓名、座位号保存下来，然后每次要查同学信息时，直接输入相应同学的学号，即可输出该同学的姓名和座位号。</p><h3 id="输入-4"><a href="#输入-4" class="headerlink" title="输入"></a>输入</h3><p>第一行为整数N（N&lt;100）,表示班里同学的人数。接下来N行，每行分别是每个同学的学号、姓名和座位号，最后一行是要查询的同学的学号。</p><h3 id="输出-4"><a href="#输出-4" class="headerlink" title="输出"></a>输出</h3><p>输出查询同学的姓名和座位号。</p><h3 id="样例输入-4"><a href="#样例输入-4" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">3</span><br><span class="line">1 zhang 11</span><br><span class="line">2 wang 22</span><br><span class="line">3 li 33</span><br><span class="line">2</span><br></pre></td></tr></table></figure><h3 id="样例输出-4"><a href="#样例输出-4" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">wang 22</span><br></pre></td></tr></table></figure><h3 id="参考答案-4"><a href="#参考答案-4" class="headerlink" title="参考答案"></a>参考答案</h3><a class="btn-beautify pink" href="/others/7809-ans.html"   title="查看答案"><i class="far fa-hand-point-right"></i><span>查看答案</span></a>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;此作业为某大学数据结构作业3，点击下方按钮，输入密码后可查看答案&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;问题-A-无向图的深度优先搜索&quot;&gt;&lt;a href=&quot;#问题-A-无向图的深度优先搜索&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="c++" scheme="https://www.wotemo.com/categories/c/"/>
    
    <category term="Data Structure" scheme="https://www.wotemo.com/categories/c/Data-Structure/"/>
    
    
    <category term="c++" scheme="https://www.wotemo.com/tags/c/"/>
    
    <category term="Data Structure" scheme="https://www.wotemo.com/tags/Data-Structure/"/>
    
  </entry>
  
  <entry>
    <title>数据结构作业2</title>
    <link href="https://www.wotemo.com/posts/56896.html"/>
    <id>https://www.wotemo.com/posts/56896.html</id>
    <published>2024-06-04T08:39:10.000Z</published>
    <updated>2024-06-04T08:39:10.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>此作业为某大学数据结构作业2，答案为我当时所写，仅供学习和参考</p></blockquote><h1 id="问题-A-统计回文子串"><a href="#问题-A-统计回文子串" class="headerlink" title="问题 A: 统计回文子串"></a>问题 A: 统计回文子串</h1><h3 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h3><p>现在给你一个字符串S，请你计算S中有多少连续子串是回文串。</p><h3 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h3><p>输入包含多组测试数据。每组输入是一个非空字符串，长度不超过5000。</p><h3 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h3><p>对于每组输入，输出回文子串的个数。</p><h3 id="样例输入"><a href="#样例输入" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">aba</span><br><span class="line">aa</span><br></pre></td></tr></table></figure><h3 id="样例输出"><a href="#样例输出" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><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">4</span><br><span class="line">3</span><br></pre></td></tr></table></figure><h3 id="参考答案"><a href="#参考答案" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    string s;</span><br><span class="line">    <span class="keyword">while</span> (<span class="built_in">getline</span>(cin, s)) &#123;</span><br><span class="line">        <span class="type">int</span> ans=<span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; s.<span class="built_in">length</span>(); ++i) &#123;</span><br><span class="line">            <span class="type">int</span> j=i,k=i;</span><br><span class="line">            <span class="keyword">while</span> (j&gt;=<span class="number">0</span> &amp;&amp; k&lt;s.<span class="built_in">length</span>()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (s[j]==s[k]) ++ans;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">break</span>;</span><br><span class="line">                --j;</span><br><span class="line">                ++k;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; s.<span class="built_in">length</span>()<span class="number">-1</span>; ++i) &#123;</span><br><span class="line">            <span class="type">int</span> j=i,k=i+<span class="number">1</span>;</span><br><span class="line">            <span class="keyword">while</span> (j&gt;=<span class="number">0</span> &amp;&amp; k&lt;s.<span class="built_in">length</span>()) &#123;</span><br><span class="line">                <span class="keyword">if</span> (s[j]==s[k]) ++ans;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">break</span>;</span><br><span class="line">                --j;</span><br><span class="line">                ++k;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        cout &lt;&lt; ans &lt;&lt; endl;</span><br><span class="line">        s.<span class="built_in">clear</span>();</span><br><span class="line">        <span class="keyword">if</span> (cin.<span class="built_in">peek</span>() == <span class="string">&#x27;\n&#x27;</span>) <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-B-构建矩阵"><a href="#问题-B-构建矩阵" class="headerlink" title="问题 B: 构建矩阵"></a>问题 B: 构建矩阵</h1><h3 id="题目描述-1"><a href="#题目描述-1" class="headerlink" title="题目描述"></a>题目描述</h3><p>现请你构建一个N*N的矩阵，第i行j列的元素为i与j的乘积。（i，j均从1开始）</p><h3 id="输入-1"><a href="#输入-1" class="headerlink" title="输入"></a>输入</h3><p>输入的第一行为一个正整数C，表示测试样例的个数。<br>然后是C行测试样例，每行为一个整数N（1&lt;&#x3D;N&lt;&#x3D;9），表示矩阵的行列数。</p><h3 id="输出-1"><a href="#输出-1" class="headerlink" title="输出"></a>输出</h3><p>对于每一组输入，输出构建的矩阵。</p><h3 id="样例输入-1"><a href="#样例输入-1" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">2</span><br><span class="line">1</span><br><span class="line">4</span><br></pre></td></tr></table></figure><h3 id="样例输出-1"><a href="#样例输出-1" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><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">1</span><br><span class="line">1 2 3 4</span><br><span class="line">2 4 6 8</span><br><span class="line">3 6 9 12</span><br><span class="line">4 8 12 16</span><br></pre></td></tr></table></figure><h3 id="参考答案-1"><a href="#参考答案-1" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n;</span><br><span class="line">    cin &gt;&gt; n;</span><br><span class="line">    <span class="keyword">while</span> (n--) &#123;</span><br><span class="line">        <span class="type">int</span> N;</span><br><span class="line">        cin &gt;&gt; N;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= N; ++i) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">1</span>; j &lt;= N; ++j) &#123;</span><br><span class="line">                cout &lt;&lt; i*j &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            cout &lt;&lt; endl;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-C-找规律填数字"><a href="#问题-C-找规律填数字" class="headerlink" title="问题 C: 找规律填数字"></a>问题 C: 找规律填数字</h1><h3 id="题目描述-2"><a href="#题目描述-2" class="headerlink" title="题目描述"></a>题目描述</h3><p>小宇正在读小学，今天老师布置了几道数学题目。小宇平时上课经常不专心，这些他可发愁了，怎么办呢？看看你能不能帮帮他。<br>题目是给你一组有规律序列的前面5个整数，请你给出它后面跟着的5个整数，如：1,2,3,4,5,<em><strong>,</strong></em>,_<strong>,</strong>,___。这是个等差数列，后面应该是6,7,8,9,10，就这么简单。而且现在小宇已经知道这串序列要么是等差数列，要么是等比数列或者是斐波那契数列。</p><h3 id="输入-2"><a href="#输入-2" class="headerlink" title="输入"></a>输入</h3><p>输入包含多组测试数据。每组输入5个整数，每个数字之间隔一个空格，当5个数字都为0时输入结束。</p><h3 id="输出-2"><a href="#输出-2" class="headerlink" title="输出"></a>输出</h3><p>对于每组输入，输出这串数列的后面5个数字，每个数字之间隔一个空格。</p><h3 id="样例输入-2"><a href="#样例输入-2" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">1 2 3 4 5</span><br><span class="line">1 2 4 8 16</span><br><span class="line">1 2 3 5 8</span><br><span class="line">0 0 0 0 0</span><br></pre></td></tr></table></figure><h3 id="样例输出-2"><a href="#样例输出-2" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><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">6 7 8 9 10</span><br><span class="line">32 64 128 256 512</span><br><span class="line">13 21 34 55 89</span><br></pre></td></tr></table></figure><h3 id="参考答案-2"><a href="#参考答案-2" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isDis</span><span class="params">(<span class="type">int</span>* arr)</span></span>&#123;</span><br><span class="line">    <span class="type">int</span> distant = arr[<span class="number">1</span>]-arr[<span class="number">0</span>];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; <span class="number">4</span>; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> ((arr[i+<span class="number">1</span>]-arr[i])!=distant) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">isFib</span><span class="params">(<span class="type">int</span>* arr)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">3</span>; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (arr[i] + arr[i+<span class="number">1</span>] != arr[i+<span class="number">2</span>]) <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">outDis</span><span class="params">(<span class="type">int</span> num, <span class="type">int</span> dis)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; ++i) &#123;</span><br><span class="line">        cout &lt;&lt; num+dis &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        num += dis;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">outGeo</span><span class="params">(<span class="type">int</span> num, <span class="type">int</span> pro)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; ++i) &#123;</span><br><span class="line">        cout &lt;&lt; num*pro &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        num *= pro;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">outFib</span><span class="params">(<span class="type">int</span> num1, <span class="type">int</span> num2)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; ++i) &#123;</span><br><span class="line">        cout &lt;&lt; num1 + num2 &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        <span class="type">int</span> tmp = num1;</span><br><span class="line">        num1 = num2;</span><br><span class="line">        num2 += tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; endl;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> arr[<span class="number">5</span>];</span><br><span class="line">    <span class="keyword">while</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">        cin &gt;&gt; arr[<span class="number">0</span>] &gt;&gt; arr[<span class="number">1</span>] &gt;&gt; arr[<span class="number">2</span>] &gt;&gt; arr[<span class="number">3</span>] &gt;&gt; arr[<span class="number">4</span>];</span><br><span class="line">        <span class="type">bool</span> flag = <span class="literal">true</span>;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">auto</span> it : arr) &#123;</span><br><span class="line">            <span class="keyword">if</span> (it!=<span class="number">0</span>) flag = <span class="literal">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (flag) <span class="keyword">break</span>;</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">isDis</span>(arr)) &#123;</span><br><span class="line">            <span class="built_in">outDis</span>(arr[<span class="number">4</span>], arr[<span class="number">1</span>]-arr[<span class="number">0</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">isFib</span>(arr)) &#123;</span><br><span class="line">            <span class="built_in">outFib</span>(arr[<span class="number">3</span>], arr[<span class="number">4</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="built_in">outGeo</span>(arr[<span class="number">4</span>], arr[<span class="number">1</span>]/arr[<span class="number">0</span>]);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-D-复原二叉树"><a href="#问题-D-复原二叉树" class="headerlink" title="问题 D: 复原二叉树"></a>问题 D: 复原二叉树</h1><h3 id="题目描述-3"><a href="#题目描述-3" class="headerlink" title="题目描述"></a>题目描述</h3><p>小明在做数据结构的作业，其中一题是给你一棵二叉树的前序遍历和中序遍历结果，要求你写出这棵二叉树的后序遍历结果。</p><h3 id="输入-3"><a href="#输入-3" class="headerlink" title="输入"></a>输入</h3><p>输入包含多组测试数据。每组输入包含两个字符串，分别表示二叉树的前序遍历和中序遍历结果。每个字符串由不重复的大写字母组成。</p><h3 id="输出-3"><a href="#输出-3" class="headerlink" title="输出"></a>输出</h3><p>对于每组输入，输出对应的二叉树的后续遍历结果。</p><h3 id="样例输入-3"><a href="#样例输入-3" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">DBACEGF ABCDEFG</span><br><span class="line">BCAD CBAD</span><br></pre></td></tr></table></figure><h3 id="样例输出-3"><a href="#样例输出-3" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><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">ACBFGED</span><br><span class="line">CDAB</span><br></pre></td></tr></table></figure><h3 id="参考答案-3"><a href="#参考答案-3" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span>&#123;</span><br><span class="line">    <span class="type">char</span> data;</span><br><span class="line">    node *lChild, *rChild;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">node* <span class="title">getNewNode</span><span class="params">(<span class="type">char</span>&amp; data)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">node</span>(&#123;data, <span class="literal">nullptr</span>, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">node* <span class="title">mergeTree</span><span class="params">(string preS, string inS)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (preS.<span class="built_in">empty</span>()) <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">    <span class="keyword">if</span> (preS.<span class="built_in">length</span>()==<span class="number">1</span>) <span class="keyword">return</span> <span class="built_in">getNewNode</span>(preS[<span class="number">0</span>]);</span><br><span class="line">    node* root = <span class="built_in">getNewNode</span>(preS[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (inS[<span class="number">0</span>]==preS[<span class="number">0</span>]) &#123;</span><br><span class="line">        root-&gt;rChild = <span class="built_in">mergeTree</span>(<span class="built_in">string</span>(preS.<span class="built_in">begin</span>()+<span class="number">1</span>, preS.<span class="built_in">end</span>()), <span class="built_in">string</span>(inS.<span class="built_in">begin</span>()+<span class="number">1</span>, inS.<span class="built_in">end</span>()));</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">int</span> k = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (inS[k]!=preS[<span class="number">0</span>]) &#123;</span><br><span class="line">            ++k;</span><br><span class="line">        &#125;</span><br><span class="line">        root-&gt;lChild = <span class="built_in">mergeTree</span>(<span class="built_in">string</span>(preS.<span class="built_in">begin</span>()+<span class="number">1</span>, preS.<span class="built_in">begin</span>()+<span class="number">1</span>+k), <span class="built_in">string</span>(inS.<span class="built_in">begin</span>(), inS.<span class="built_in">begin</span>()+k));</span><br><span class="line">        root-&gt;rChild = <span class="built_in">mergeTree</span>(<span class="built_in">string</span>(preS.<span class="built_in">begin</span>()+<span class="number">1</span>+k, preS.<span class="built_in">end</span>()), <span class="built_in">string</span>(inS.<span class="built_in">begin</span>()+<span class="number">1</span>+k, inS.<span class="built_in">end</span>()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> root;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">postOrder</span><span class="params">(node* root)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (root==<span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="built_in">postOrder</span>(root-&gt;lChild);</span><br><span class="line">    <span class="built_in">postOrder</span>(root-&gt;rChild);</span><br><span class="line">    cout &lt;&lt; root-&gt;data;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">clear</span><span class="params">(node* root)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (root==<span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="built_in">clear</span>(root-&gt;lChild);</span><br><span class="line">    <span class="built_in">clear</span>(root-&gt;rChild);</span><br><span class="line">    <span class="keyword">delete</span> root;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    string s, preS, inS;</span><br><span class="line">    <span class="keyword">while</span> (<span class="built_in">getline</span>(cin, s)) &#123;</span><br><span class="line">        preS.<span class="built_in">assign</span>(s, <span class="number">0</span>, s.<span class="built_in">length</span>()/<span class="number">2</span>);</span><br><span class="line">        inS.<span class="built_in">assign</span>(s, (s.<span class="built_in">length</span>()+<span class="number">2</span>)/<span class="number">2</span>, s.<span class="built_in">length</span>()/<span class="number">2</span>);</span><br><span class="line">        node* root = <span class="built_in">mergeTree</span>(preS, inS);</span><br><span class="line">        <span class="comment">//输出后序遍历</span></span><br><span class="line">        <span class="built_in">postOrder</span>(root);</span><br><span class="line">        cout &lt;&lt; endl;</span><br><span class="line">        s.<span class="built_in">clear</span>();</span><br><span class="line">        <span class="built_in">clear</span>(root);</span><br><span class="line">        <span class="keyword">if</span> (cin.<span class="built_in">peek</span>() == <span class="string">&#x27;\n&#x27;</span>) <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-E-子树的后序遍历"><a href="#问题-E-子树的后序遍历" class="headerlink" title="问题 E: 子树的后序遍历"></a>问题 E: 子树的后序遍历</h1><h3 id="题目描述-4"><a href="#题目描述-4" class="headerlink" title="题目描述"></a>题目描述</h3><p>给你一颗二叉树的中序和后序遍历序列，请编程输出该二叉树左子树或右子树的后序遍历序列。</p><h3 id="输入-4"><a href="#输入-4" class="headerlink" title="输入"></a>输入</h3><p>占三行，第一行表示二叉树的中序遍历序列，第二行表示后序遍历序列。用大写字母标识结点，二叉树的结点最多26个。<br>第三行是单个字母，L表示要求输出该二叉树的左子树的后序遍历序列，R表示要求输出该二叉树的右子树的后序遍历序列。</p><h3 id="输出-4"><a href="#输出-4" class="headerlink" title="输出"></a>输出</h3><p>按要求输出该二叉树左子树或右子树的后序遍历序列。</p><h3 id="样例输入-4"><a href="#样例输入-4" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">BDCEAFHG</span><br><span class="line">DECBHGFA</span><br><span class="line">R</span><br></pre></td></tr></table></figure><h3 id="样例输出-4"><a href="#样例输出-4" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">HGF</span><br></pre></td></tr></table></figure><h3 id="参考答案-4"><a href="#参考答案-4" class="headerlink" title="参考答案"></a>参考答案</h3><p><em><strong>这里我偷了个懒，直接把字符串倒过来，用上一问的代码做的</strong></em></p><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span>&#123;</span><br><span class="line">    <span class="type">char</span> data;</span><br><span class="line">    node *lChild, *rChild;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">node* <span class="title">getNewNode</span><span class="params">(<span class="type">char</span>&amp; data)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">node</span>(&#123;data, <span class="literal">nullptr</span>, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function">node* <span class="title">mergeTree</span><span class="params">(string preS, string inS)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (preS.<span class="built_in">empty</span>()) <span class="keyword">return</span> <span class="literal">nullptr</span>;</span><br><span class="line">    <span class="keyword">if</span> (preS.<span class="built_in">length</span>()==<span class="number">1</span>) <span class="keyword">return</span> <span class="built_in">getNewNode</span>(preS[<span class="number">0</span>]);</span><br><span class="line">    node* root = <span class="built_in">getNewNode</span>(preS[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (inS[<span class="number">0</span>]==preS[<span class="number">0</span>]) &#123;</span><br><span class="line">        root-&gt;lChild = <span class="built_in">mergeTree</span>(<span class="built_in">string</span>(preS.<span class="built_in">begin</span>()+<span class="number">1</span>, preS.<span class="built_in">end</span>()), <span class="built_in">string</span>(inS.<span class="built_in">begin</span>()+<span class="number">1</span>, inS.<span class="built_in">end</span>()));</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="type">int</span> k = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (inS[k]!=preS[<span class="number">0</span>]) &#123;</span><br><span class="line">            ++k;</span><br><span class="line">        &#125;</span><br><span class="line">        root-&gt;rChild = <span class="built_in">mergeTree</span>(<span class="built_in">string</span>(preS.<span class="built_in">begin</span>()+<span class="number">1</span>, preS.<span class="built_in">begin</span>()+<span class="number">1</span>+k), <span class="built_in">string</span>(inS.<span class="built_in">begin</span>(), inS.<span class="built_in">begin</span>()+k));</span><br><span class="line">        root-&gt;lChild = <span class="built_in">mergeTree</span>(<span class="built_in">string</span>(preS.<span class="built_in">begin</span>()+<span class="number">1</span>+k, preS.<span class="built_in">end</span>()), <span class="built_in">string</span>(inS.<span class="built_in">begin</span>()+<span class="number">1</span>+k, inS.<span class="built_in">end</span>()));</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> root;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">postOrder</span><span class="params">(node* root)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (root==<span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="built_in">postOrder</span>(root-&gt;lChild);</span><br><span class="line">    <span class="built_in">postOrder</span>(root-&gt;rChild);</span><br><span class="line">    cout &lt;&lt; root-&gt;data;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">clear</span><span class="params">(node* root)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (root==<span class="literal">nullptr</span>) <span class="keyword">return</span>;</span><br><span class="line">    <span class="built_in">clear</span>(root-&gt;lChild);</span><br><span class="line">    <span class="built_in">clear</span>(root-&gt;rChild);</span><br><span class="line">    <span class="keyword">delete</span> root;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    string s, preS, inS;</span><br><span class="line">    cin &gt;&gt; preS &gt;&gt; inS;</span><br><span class="line">    <span class="type">char</span> flag;</span><br><span class="line">    cin &gt;&gt; flag;</span><br><span class="line">    <span class="built_in">reverse</span>(preS.<span class="built_in">begin</span>(), preS.<span class="built_in">end</span>());</span><br><span class="line">    <span class="built_in">reverse</span>(inS.<span class="built_in">begin</span>(), inS.<span class="built_in">end</span>());</span><br><span class="line">    node* root = <span class="built_in">mergeTree</span>(inS, preS);</span><br><span class="line">    <span class="comment">//输出后序遍历</span></span><br><span class="line">    <span class="keyword">if</span> (flag == <span class="string">&#x27;L&#x27;</span>) <span class="built_in">postOrder</span>(root-&gt;lChild);</span><br><span class="line">    <span class="keyword">else</span> <span class="built_in">postOrder</span>(root-&gt;rChild);</span><br><span class="line"></span><br><span class="line">    s.<span class="built_in">clear</span>();</span><br><span class="line">    <span class="built_in">clear</span>(root);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-F-迷宫问题"><a href="#问题-F-迷宫问题" class="headerlink" title="问题 F: 迷宫问题"></a>问题 F: 迷宫问题</h1><h3 id="题目描述-5"><a href="#题目描述-5" class="headerlink" title="题目描述"></a>题目描述</h3><p>小明置身于一个迷宫，请你帮小明找出从起点到终点的最短路程。<br>小明只能向上下左右四个方向移动。</p><h3 id="输入-5"><a href="#输入-5" class="headerlink" title="输入"></a>输入</h3><p>输入包含多组测试数据。输入的第一行是一个整数T，表示有T组测试数据。<br>每组输入的第一行是两个整数N和M（1&lt;&#x3D;N,M&lt;&#x3D;100）。<br>接下来N行，每行输入M个字符，每个字符表示迷宫中的一个小方格。<br>字符的含义如下：<br>‘S’：起点<br>‘E’：终点<br>‘-’：空地，可以通过<br>‘#’：障碍，无法通过<br>输入数据保证有且仅有一个起点和终点。</p><h3 id="输出-5"><a href="#输出-5" class="headerlink" title="输出"></a>输出</h3><p>对于每组输入，输出从起点到终点的最短路程，如果不存在从起点到终点的路，则输出-1。</p><h3 id="样例输入-5"><a href="#样例输入-5" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">1</span><br><span class="line">5 5</span><br><span class="line">S-###</span><br><span class="line">-----</span><br><span class="line">##---</span><br><span class="line">E#---</span><br><span class="line">---##</span><br></pre></td></tr></table></figure><h3 id="样例输出-5"><a href="#样例输出-5" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">9</span><br></pre></td></tr></table></figure><h3 id="参考答案-5"><a href="#参考答案-5" class="headerlink" title="参考答案"></a>参考答案</h3><p><em><strong>使用<u>BFS</u>做，DFS会超时</strong></em></p><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;queue&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> N, M;</span><br><span class="line"></span><br><span class="line"><span class="type">bool</span> l[<span class="number">105</span>][<span class="number">105</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">point</span> &#123;</span><br><span class="line">    <span class="type">int</span> x;</span><br><span class="line">    <span class="type">int</span> y;</span><br><span class="line">    <span class="type">int</span> step;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line">vector&lt;pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt;&gt; v;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">bool</span> <span class="title">canMove</span><span class="params">(point&amp; p)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (p.x &gt;= <span class="number">0</span> &amp;&amp; p.y &gt;= <span class="number">0</span> &amp;&amp; p.x &lt; M &amp;&amp; p.y &lt; N &amp;&amp; l[p.y][p.x]) &#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">move_ming</span><span class="params">(pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt; S, pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt;&amp; E)</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (S==E) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    queue&lt;point&gt; q;</span><br><span class="line">    q.<span class="built_in">push</span>(<span class="built_in">point</span>(&#123;S.second, S.first, <span class="number">0</span>&#125;));</span><br><span class="line">    l[S.first][S.second] = <span class="literal">false</span>;</span><br><span class="line">    <span class="keyword">while</span> (!q.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">        point p = q.<span class="built_in">front</span>();</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> i = <span class="number">0</span>; i &lt; <span class="number">4</span>; ++i) &#123;</span><br><span class="line">            point next_p = <span class="built_in">point</span>(&#123;p.x+v[i].second, p.y+v[i].first, p.step+<span class="number">1</span>&#125;);</span><br><span class="line">            <span class="keyword">if</span> (<span class="built_in">canMove</span>(next_p)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (next_p.x == E.second &amp;&amp; next_p.y == E.first) &#123;</span><br><span class="line">                    <span class="keyword">return</span> next_p.step;</span><br><span class="line">                &#125;</span><br><span class="line">                q.<span class="built_in">push</span>(next_p);</span><br><span class="line">                l[next_p.y][next_p.x] = <span class="literal">false</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        q.<span class="built_in">pop</span>();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> <span class="number">-1</span>;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    v.<span class="built_in">push_back</span>(<span class="built_in">pair</span>&lt;<span class="type">int</span>, <span class="type">int</span>&gt;(<span class="number">-1</span>, <span class="number">0</span>));</span><br><span class="line">    v.<span class="built_in">push_back</span>(<span class="built_in">pair</span>&lt;<span class="type">int</span>, <span class="type">int</span>&gt;(<span class="number">1</span>, <span class="number">0</span>));</span><br><span class="line">    v.<span class="built_in">push_back</span>(<span class="built_in">pair</span>&lt;<span class="type">int</span>, <span class="type">int</span>&gt;(<span class="number">0</span>, <span class="number">1</span>));</span><br><span class="line">    v.<span class="built_in">push_back</span>(<span class="built_in">pair</span>&lt;<span class="type">int</span>, <span class="type">int</span>&gt;(<span class="number">0</span>, <span class="number">-1</span>));</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> T;</span><br><span class="line">    cin &gt;&gt; T;</span><br><span class="line">    <span class="keyword">while</span> (T--) &#123;</span><br><span class="line">        cin &gt;&gt; N &gt;&gt; M;</span><br><span class="line">        pair&lt;<span class="type">int</span>, <span class="type">int</span>&gt; S, E;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; N; ++i) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; M; ++j) &#123;</span><br><span class="line">                l[i][j] = <span class="literal">false</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="type">char</span> sign;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; N; ++i) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt; M; ++j) &#123;</span><br><span class="line">                cin &gt;&gt; sign;</span><br><span class="line">                <span class="keyword">if</span> (sign == <span class="string">&#x27;-&#x27;</span>) &#123;</span><br><span class="line">                    l[i][j] = <span class="literal">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (sign == <span class="string">&#x27;#&#x27;</span>) &#123;</span><br><span class="line">                    l[i][j] = <span class="literal">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (sign == <span class="string">&#x27;S&#x27;</span>) &#123;</span><br><span class="line">                    l[i][j] = <span class="literal">true</span>;</span><br><span class="line">                    S.first = i;</span><br><span class="line">                    S.second = j;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (sign == <span class="string">&#x27;E&#x27;</span>) &#123;</span><br><span class="line">                    l[i][j] = <span class="literal">true</span>;</span><br><span class="line">                    E.first = i;</span><br><span class="line">                    E.second = j;</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">        cout &lt;&lt; <span class="built_in">move_ming</span>(S, E) &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;此作业为某大学数据结构作业2，答案为我当时所写，仅供学习和参考&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;问题-A-统计回文子串&quot;&gt;&lt;a href=&quot;#问题-A-统计回文子串&quot; class=&quot;headerlink&quot; title=&quot;问题</summary>
        
      
    
    
    
    <category term="c++" scheme="https://www.wotemo.com/categories/c/"/>
    
    <category term="Data Structure" scheme="https://www.wotemo.com/categories/c/Data-Structure/"/>
    
    
    <category term="c++" scheme="https://www.wotemo.com/tags/c/"/>
    
    <category term="Data Structure" scheme="https://www.wotemo.com/tags/Data-Structure/"/>
    
  </entry>
  
  <entry>
    <title>数据结构作业1</title>
    <link href="https://www.wotemo.com/posts/57088.html"/>
    <id>https://www.wotemo.com/posts/57088.html</id>
    <published>2024-06-04T07:57:10.000Z</published>
    <updated>2024-06-04T07:57:10.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>此作业为某大学数据结构作业1，答案为我当时所写，仅供学习和参考</p></blockquote><h1 id="问题-A-查成绩"><a href="#问题-A-查成绩" class="headerlink" title="问题 A: 查成绩"></a>问题 A: 查成绩</h1><h3 id="题目描述"><a href="#题目描述" class="headerlink" title="题目描述"></a>题目描述</h3><p>期末考试结束后，数学老师给出了班里同学们的数学成绩，为了快速查成绩，请编程帮助查成绩。</p><h3 id="输入"><a href="#输入" class="headerlink" title="输入"></a>输入</h3><p>第一行为N（N&lt;1000）表示班级人数，第一行后N行，每行两个部分，一个是学号（符号最多8个），一个是成绩（整数）。<br>最后一行是要查找成绩同学的学号。</p><h3 id="输出"><a href="#输出" class="headerlink" title="输出"></a>输出</h3><p>输出要查找同学的学号。</p><h3 id="样例输入"><a href="#样例输入" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">2</span><br><span class="line">001 90</span><br><span class="line">002 95</span><br><span class="line">002</span><br></pre></td></tr></table></figure><h3 id="样例输出"><a href="#样例输出" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">95</span><br></pre></td></tr></table></figure><h3 id="参考答案"><a href="#参考答案" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n;</span><br><span class="line">    cin &gt;&gt; n;</span><br><span class="line">    <span class="type">int</span> num[n];</span><br><span class="line">    <span class="type">int</span> score[n];</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        cin &gt;&gt; num[i] &gt;&gt; score[i];</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">int</span> find_num;</span><br><span class="line">    cin &gt;&gt; find_num;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        <span class="keyword">if</span> (num[i] == find_num) &#123;</span><br><span class="line">            cout &lt;&lt; score[i];</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-B-插入数据"><a href="#问题-B-插入数据" class="headerlink" title="问题 B: 插入数据"></a>问题 B: 插入数据</h1><h3 id="题目描述-1"><a href="#题目描述-1" class="headerlink" title="题目描述"></a>题目描述</h3><p>已有一个整数序列，现在要在不同的位置插入一些整数，请输出插入数据后的序列。</p><h3 id="输入-1"><a href="#输入-1" class="headerlink" title="输入"></a>输入</h3><p>第一行是N（N&lt;1000）表示原序列中元素的个数，紧接着第二行是N个整数，第三行是要插入的元素个数M（M&lt;1000）,第四开始M行，每行是一对数据k（要插入到原序列的位置，从1开始计数）和x。</p><h3 id="输出-1"><a href="#输出-1" class="headerlink" title="输出"></a>输出</h3><p>输出插入元素后的整数序列。</p><h3 id="样例输入-1"><a href="#样例输入-1" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">5</span><br><span class="line">1 2 3 4 5</span><br><span class="line">2</span><br><span class="line">1 11</span><br><span class="line">3 33</span><br></pre></td></tr></table></figure><h3 id="样例输出-1"><a href="#样例输出-1" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">11 1 2 33 3 4 5</span><br></pre></td></tr></table></figure><h3 id="参考答案-1"><a href="#参考答案-1" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span>&#123;</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    node* next;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">node* <span class="title">getNewNode</span><span class="params">(<span class="type">int</span> data)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">node</span>(&#123;data, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">insert</span><span class="params">(node* head, <span class="type">int</span> index, <span class="type">int</span> data)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt; index; ++i) &#123;</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    node* tmp = <span class="built_in">getNewNode</span>(data);</span><br><span class="line">    tmp-&gt;next = head-&gt;next;</span><br><span class="line">    head-&gt;next = tmp;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">find_idx</span><span class="params">(vector&lt;node*&gt; &amp;v, node* head, <span class="type">int</span> index)</span></span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; index; ++i) &#123;</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    v.<span class="built_in">push_back</span>(head);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">v_insert</span><span class="params">(node* pos, node* head, <span class="type">int</span> data)</span></span>&#123;</span><br><span class="line">    node* p = head;</span><br><span class="line">    head = head-&gt;next;</span><br><span class="line">    <span class="keyword">while</span> (head!=pos) &#123;</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">        p = p-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    head = <span class="built_in">getNewNode</span>(data);</span><br><span class="line">    head-&gt;next = p-&gt;next;</span><br><span class="line">    p-&gt;next = head;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n;</span><br><span class="line">    cin &gt;&gt; n;</span><br><span class="line">    node* head = <span class="built_in">getNewNode</span>(<span class="number">0</span>), *tail = head;</span><br><span class="line">    <span class="type">int</span> num;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">1</span>; i &lt;= n; ++i) &#123;</span><br><span class="line">        cin &gt;&gt; num;</span><br><span class="line">        tail-&gt;next = <span class="built_in">getNewNode</span>(num);</span><br><span class="line">        tail = tail-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    cin &gt;&gt; n;</span><br><span class="line">    <span class="type">int</span> index;</span><br><span class="line">    vector&lt;node*&gt; v;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; nums;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        cin &gt;&gt; index &gt;&gt; num;</span><br><span class="line">        nums.<span class="built_in">push_back</span>(num);</span><br><span class="line">        <span class="built_in">find_idx</span>(v, head, index);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">0</span>; i &lt; n; ++i) &#123;</span><br><span class="line">        <span class="built_in">v_insert</span>(v[i], head, nums[i]);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (head-&gt;next!=<span class="literal">nullptr</span>) &#123;</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">        cout &lt;&lt; head-&gt;data &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-C-二路归并"><a href="#问题-C-二路归并" class="headerlink" title="问题 C: 二路归并"></a>问题 C: 二路归并</h1><h3 id="题目描述-2"><a href="#题目描述-2" class="headerlink" title="题目描述"></a>题目描述</h3><p>有两个按元素值递增有序的整数顺序表A和B，设计一个算法将顺序表A和B的全部元素合并到一个递增有序顺序表C中，并依次输出C中的元素。</p><h3 id="输入-2"><a href="#输入-2" class="headerlink" title="输入"></a>输入</h3><p>占两行，依次是A和B的序列（元素个数都小于100）。</p><h3 id="输出-2"><a href="#输出-2" class="headerlink" title="输出"></a>输出</h3><p>依次输出C中的元素。</p><h3 id="样例输入-2"><a href="#样例输入-2" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">1 2 3 4 5</span><br><span class="line">1 2 3 4 6</span><br></pre></td></tr></table></figure><h3 id="样例输出-2"><a href="#样例输出-2" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1 1 2 2 3 3 4 4 5 6</span><br></pre></td></tr></table></figure><h3 id="参考答案-2"><a href="#参考答案-2" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; v1, v2;</span><br><span class="line">    <span class="type">char</span> chs[<span class="number">10000</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    cin.<span class="built_in">getline</span>(chs, <span class="number">10000</span>);</span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (chs[i]!=<span class="number">0</span>) &#123;</span><br><span class="line">        <span class="type">int</span> j = i, num = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (chs[j] != <span class="string">&#x27; &#x27;</span> &amp;&amp; chs[j] != <span class="number">0</span>) &#123;</span><br><span class="line">            num = num*<span class="number">10</span>+chs[j++]-<span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        v1.<span class="built_in">push_back</span>(num);</span><br><span class="line">        i = j+<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="type">char</span> chs2[<span class="number">10000</span>];</span><br><span class="line">    cin.<span class="built_in">getline</span>(chs2, <span class="number">10000</span>);</span><br><span class="line">    i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (chs2[i]!=<span class="number">0</span>) &#123;</span><br><span class="line">        <span class="type">int</span> j = i, num = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (chs2[j] != <span class="string">&#x27; &#x27;</span> &amp;&amp; chs2[j] != <span class="number">0</span>) &#123;</span><br><span class="line">            num = num*<span class="number">10</span>+chs2[j++]-<span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        v2.<span class="built_in">push_back</span>(num);</span><br><span class="line">        i = j+<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> idx1=<span class="number">0</span>, idx2=<span class="number">0</span>;</span><br><span class="line">    vector&lt;<span class="type">int</span>&gt; ans;</span><br><span class="line">    <span class="keyword">while</span> (idx1 &lt; v1.<span class="built_in">size</span>() &amp;&amp; idx2 &lt; v2.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="keyword">if</span> (v1[idx1] &lt; v2[idx2]) &#123;</span><br><span class="line">            cout &lt;&lt; v1[idx1++] &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            cout &lt;&lt; v2[idx2++] &lt;&lt; <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="keyword">if</span> (idx1==v1.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> j = idx2; j &lt; v2.<span class="built_in">size</span>(); ++j) &#123;</span><br><span class="line">            <span class="keyword">if</span> (j == v2.<span class="built_in">size</span>()<span class="number">-1</span>) &#123; cout &lt;&lt; v2[j]; &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123; cout &lt;&lt; v2[j] &lt;&lt; <span class="string">&quot; &quot;</span>; &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> j = idx1; j &lt; v1.<span class="built_in">size</span>(); ++j) &#123;</span><br><span class="line">            <span class="keyword">if</span> (j == v1.<span class="built_in">size</span>()<span class="number">-1</span>) &#123; cout &lt;&lt; v1[j]; &#125;</span><br><span class="line">            <span class="keyword">else</span> &#123; cout &lt;&lt; v1[j] &lt;&lt; <span class="string">&quot; &quot;</span>; &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-D-最大值个数"><a href="#问题-D-最大值个数" class="headerlink" title="问题 D: 最大值个数"></a>问题 D: 最大值个数</h1><h3 id="题目描述-3"><a href="#题目描述-3" class="headerlink" title="题目描述"></a>题目描述</h3><p>有一个整数单链表L，其中可能存在多个值相同的结点，设计一个算法查找L中最大值结点的个数。</p><h3 id="输入-3"><a href="#输入-3" class="headerlink" title="输入"></a>输入</h3><p>单链表L中的元素，个数不定。</p><h3 id="输出-3"><a href="#输出-3" class="headerlink" title="输出"></a>输出</h3><p>查找L中最大值结点的个数。</p><h3 id="样例输入-3"><a href="#样例输入-3" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">1 2 3 4 5 5 20 20 1 2 3 4 5</span><br></pre></td></tr></table></figure><h3 id="样例输出-3"><a href="#样例输出-3" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">2</span><br></pre></td></tr></table></figure><h3 id="参考答案-3"><a href="#参考答案-3" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;string&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span>&#123;</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    node* next;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">node* <span class="title">getNewNode</span><span class="params">(<span class="type">int</span> data)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">node</span>(&#123;data, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    string str;</span><br><span class="line">    <span class="built_in">getline</span>(cin, str);</span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    node* head = <span class="built_in">getNewNode</span>(<span class="number">0</span>), *tail = head;</span><br><span class="line">    <span class="keyword">while</span> (i&lt;str.<span class="built_in">size</span>()) &#123;</span><br><span class="line">        <span class="type">int</span> j = i, num = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (str[j] != <span class="string">&#x27; &#x27;</span> &amp;&amp; str[j] != <span class="number">0</span>) &#123;</span><br><span class="line">            num = num*<span class="number">10</span>+str[j++]-<span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        node* p = <span class="built_in">getNewNode</span>(num);</span><br><span class="line">        tail-&gt;next = p;</span><br><span class="line">        tail = tail-&gt;next;</span><br><span class="line">        i = j+<span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="type">int</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="type">int</span> max_num = tail-&gt;data;</span><br><span class="line">    <span class="keyword">while</span> (head-&gt;next != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        head = head-&gt;next;</span><br><span class="line">        <span class="keyword">if</span> (head-&gt;data &gt; max_num) &#123;</span><br><span class="line">            max_num = head-&gt;data;</span><br><span class="line">            count = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (head-&gt;data == max_num) &#123;</span><br><span class="line">            ++count;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; count;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-E-约瑟夫（Joseph）问题"><a href="#问题-E-约瑟夫（Joseph）问题" class="headerlink" title="问题 E: 约瑟夫（Joseph）问题"></a>问题 E: 约瑟夫（Joseph）问题</h1><h3 id="题目描述-4"><a href="#题目描述-4" class="headerlink" title="题目描述"></a>题目描述</h3><p>编写一个程序求解约瑟夫（Joseph）问题。有n个小孩围成一圈，给他们从1开始依次编号，从编号为1的小孩开始报数，数到第m（0&lt;m&lt;n）个小孩出列，然后从出列的下一个小孩重新开始报数，数到第m个小孩又出列，…，如此反复直到所有的小孩全部出列为止，求整个出列序列。</p><h3 id="输入-4"><a href="#输入-4" class="headerlink" title="输入"></a>输入</h3><p>占一行为n和m（n&lt;100）。</p><h3 id="输出-4"><a href="#输出-4" class="headerlink" title="输出"></a>输出</h3><p>整个出列序列。</p><h3 id="样例输入-4"><a href="#样例输入-4" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">6 5</span><br></pre></td></tr></table></figure><h3 id="样例输出-4"><a href="#样例输出-4" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">5 4 6 2 3 1</span><br></pre></td></tr></table></figure><h3 id="参考答案-4"><a href="#参考答案-4" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span>&#123;</span><br><span class="line">    <span class="type">int</span> data;</span><br><span class="line">    node* next;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="function">node* <span class="title">getNewNode</span><span class="params">(<span class="type">int</span> data)</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> <span class="built_in">node</span>(&#123;data, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n, m;</span><br><span class="line">    cin &gt;&gt; n &gt;&gt; m;</span><br><span class="line">    node* p = <span class="built_in">getNewNode</span>(<span class="number">1</span>), *q = p;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= n; ++i) &#123;</span><br><span class="line">        q-&gt;next = <span class="built_in">getNewNode</span>(i);</span><br><span class="line">        q = q-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    q-&gt;next = p;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (p-&gt;next!=p) &#123;</span><br><span class="line">        m %= n;</span><br><span class="line">        <span class="keyword">for</span> (<span class="type">int</span> i = <span class="number">2</span>; i &lt;= m; ++i) &#123;</span><br><span class="line">            p = p-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">        cout &lt;&lt; p-&gt;data &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        p-&gt;data = p-&gt;next-&gt;data;</span><br><span class="line">        p-&gt;next = p-&gt;next-&gt;next;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">    cout &lt;&lt; p-&gt;data;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-F-括号配对"><a href="#问题-F-括号配对" class="headerlink" title="问题 F: 括号配对"></a>问题 F: 括号配对</h1><h3 id="题目描述-5"><a href="#题目描述-5" class="headerlink" title="题目描述"></a>题目描述</h3><p>设计一个算法利用顺序栈检查用户输入的表达式中括号是否配对（假设表达式中可能含有圆括号()、中括号[]和大括号{}）。</p><h3 id="输入-5"><a href="#输入-5" class="headerlink" title="输入"></a>输入</h3><p>占一行为含有三种括号的表达式（最长100个符号）。</p><h3 id="输出-5"><a href="#输出-5" class="headerlink" title="输出"></a>输出</h3><p>匹配时输出YES，小括号不匹配输出NO1，中括号不匹配时输出NO2，大括号不匹配时输出NO3。</p><h3 id="样例输入-5"><a href="#样例输入-5" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">&#123;([a])&#125;</span><br></pre></td></tr></table></figure><h3 id="样例输出-5"><a href="#样例输出-5" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">YES</span><br></pre></td></tr></table></figure><h3 id="参考答案-5"><a href="#参考答案-5" class="headerlink" title="参考答案"></a>参考答案</h3><p><em><strong>我认为此题目描述不是很清楚，并且某些样例也有问题，这个答案参考了别人所写</strong></em></p><p><em><strong>只需注意，遇到<u>中括号</u>以右边为准，其他括号以左边为准</strong></em></p><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;stack&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">char</span> chs[<span class="number">1000</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    cin.<span class="built_in">getline</span>(chs, <span class="number">1000</span>);</span><br><span class="line">    stack&lt;<span class="type">char</span>&gt; stk;</span><br><span class="line">    <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span> (chs[i]!=<span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;(&#x27;</span> || chs[i] == <span class="string">&#x27;[&#x27;</span> || chs[i] == <span class="string">&#x27;&#123;&#x27;</span>) stk.<span class="built_in">push</span>(chs[i]);</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;)&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (stk.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">                cout &lt;&lt; <span class="string">&quot;NO1&quot;</span>;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;(&#x27;</span>) &#123;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;[&#x27;</span>) &#123;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;NO2&quot;</span>;</span><br><span class="line">                    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;&#123;&#x27;</span>) &#123;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;NO3&quot;</span>;</span><br><span class="line">                    <span class="keyword">return</span> <span class="number">0</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 class="keyword">if</span> (chs[i] == <span class="string">&#x27;]&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (stk.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">                cout &lt;&lt; <span class="string">&quot;NO2&quot;</span>;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;[&#x27;</span>) &#123;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125; <span class="keyword">else</span> <span class="keyword">if</span> (stk.<span class="built_in">top</span>() != <span class="string">&#x27;[&#x27;</span>) &#123;</span><br><span class="line">                cout &lt;&lt; <span class="string">&quot;NO2&quot;</span>;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;&#125;&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span> (stk.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">                cout &lt;&lt; <span class="string">&quot;NO3&quot;</span>;</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;&#123;&#x27;</span>) &#123;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;(&#x27;</span>) &#123;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;NO1&quot;</span>;</span><br><span class="line">                    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;[&#x27;</span>) &#123;</span><br><span class="line">                    cout &lt;&lt; <span class="string">&quot;NO2&quot;</span>;</span><br><span class="line">                    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        ++i;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (stk.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;YES&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;(&#x27;</span>) cout &lt;&lt; <span class="string">&quot;NO1&quot;</span>;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;[&#x27;</span>) cout &lt;&lt; <span class="string">&quot;NO2&quot;</span>;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (stk.<span class="built_in">top</span>() == <span class="string">&#x27;&#123;&#x27;</span>) cout &lt;&lt; <span class="string">&quot;NO3&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-G-后缀表达式"><a href="#问题-G-后缀表达式" class="headerlink" title="问题 G: 后缀表达式"></a>问题 G: 后缀表达式</h1><h3 id="题目描述-6"><a href="#题目描述-6" class="headerlink" title="题目描述"></a>题目描述</h3><p>给出一个中缀表达式，输出该表达式的后缀表达式。</p><h3 id="输入-6"><a href="#输入-6" class="headerlink" title="输入"></a>输入</h3><p>占一行，一个中缀表达式（运算符只有+-*&#x2F;，最多1000个字符），输出后缀表达式。</p><h3 id="输出-6"><a href="#输出-6" class="headerlink" title="输出"></a>输出</h3><p>输出后缀表达式。</p><h3 id="样例输入-6"><a href="#样例输入-6" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">(56-20)/(4+2)</span><br></pre></td></tr></table></figure><h3 id="样例输出-6"><a href="#样例输出-6" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">56 20 - 4 2 + /</span><br></pre></td></tr></table></figure><h3 id="参考答案-6"><a href="#参考答案-6" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;bits/stdc++.h&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">char</span> chs[<span class="number">1005</span>];</span><br><span class="line">    cin.<span class="built_in">getline</span>(chs, <span class="number">1005</span>);</span><br><span class="line">    <span class="type">int</span> i=<span class="number">0</span>;</span><br><span class="line">    stack&lt;<span class="type">char</span>&gt; stk;</span><br><span class="line">    <span class="keyword">while</span> (chs[i]!=<span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (chs[i] &gt;= <span class="string">&#x27;0&#x27;</span> &amp;&amp; chs[i] &lt;=<span class="string">&#x27;9&#x27;</span>) &#123;</span><br><span class="line">            <span class="type">int</span> j = i;</span><br><span class="line">            <span class="keyword">while</span> (chs[j] &gt;= <span class="string">&#x27;0&#x27;</span> &amp;&amp; chs[j] &lt;=<span class="string">&#x27;9&#x27;</span>) &#123;</span><br><span class="line">                cout &lt;&lt; chs[j++];</span><br><span class="line">            &#125;</span><br><span class="line">            cout &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">            i = j;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;(&#x27;</span>) &#123;</span><br><span class="line">            stk.<span class="built_in">push</span>(<span class="string">&#x27;(&#x27;</span>);</span><br><span class="line">            ++i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;+&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">while</span> (!stk.<span class="built_in">empty</span>() &amp;&amp; (stk.<span class="built_in">top</span>() == <span class="string">&#x27;*&#x27;</span> || stk.<span class="built_in">top</span>() == <span class="string">&#x27;/&#x27;</span> || stk.<span class="built_in">top</span>() == <span class="string">&#x27;-&#x27;</span>)) &#123;</span><br><span class="line">                cout  &lt;&lt; stk.<span class="built_in">top</span>() &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125;</span><br><span class="line">            stk.<span class="built_in">push</span>(<span class="string">&#x27;+&#x27;</span>);</span><br><span class="line">            ++i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;-&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">while</span> (!stk.<span class="built_in">empty</span>() &amp;&amp; (stk.<span class="built_in">top</span>() == <span class="string">&#x27;*&#x27;</span> || stk.<span class="built_in">top</span>() == <span class="string">&#x27;/&#x27;</span> || stk.<span class="built_in">top</span>() == <span class="string">&#x27;+&#x27;</span>)) &#123;</span><br><span class="line">                cout  &lt;&lt; stk.<span class="built_in">top</span>() &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125;</span><br><span class="line">            stk.<span class="built_in">push</span>(<span class="string">&#x27;-&#x27;</span>);</span><br><span class="line">            ++i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;*&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">while</span> (!stk.<span class="built_in">empty</span>() &amp;&amp; (stk.<span class="built_in">top</span>() == <span class="string">&#x27;/&#x27;</span>)) &#123;</span><br><span class="line">                cout  &lt;&lt; stk.<span class="built_in">top</span>() &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125;</span><br><span class="line">            stk.<span class="built_in">push</span>(<span class="string">&#x27;*&#x27;</span>);</span><br><span class="line">            ++i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;/&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">while</span> (!stk.<span class="built_in">empty</span>() &amp;&amp; (stk.<span class="built_in">top</span>() == <span class="string">&#x27;*&#x27;</span>)) &#123;</span><br><span class="line">                cout  &lt;&lt; stk.<span class="built_in">top</span>() &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125;</span><br><span class="line">            stk.<span class="built_in">push</span>(<span class="string">&#x27;/&#x27;</span>);</span><br><span class="line">            ++i;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (chs[i] == <span class="string">&#x27;)&#x27;</span>) &#123;</span><br><span class="line">            <span class="keyword">while</span> (!stk.<span class="built_in">empty</span>() &amp;&amp; stk.<span class="built_in">top</span>() != <span class="string">&#x27;(&#x27;</span>) &#123;</span><br><span class="line">                cout &lt;&lt; stk.<span class="built_in">top</span>() &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">                stk.<span class="built_in">pop</span>();</span><br><span class="line">            &#125;</span><br><span class="line">            stk.<span class="built_in">pop</span>();</span><br><span class="line">            ++i;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (!stk.<span class="built_in">empty</span>()) &#123;</span><br><span class="line">        cout &lt;&lt; stk.<span class="built_in">top</span>() &lt;&lt; <span class="string">&quot; &quot;</span>;</span><br><span class="line">        stk.<span class="built_in">pop</span>();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div><h1 id="问题-H-字符串反转"><a href="#问题-H-字符串反转" class="headerlink" title="问题 H: 字符串反转"></a>问题 H: 字符串反转</h1><h3 id="题目描述-7"><a href="#题目描述-7" class="headerlink" title="题目描述"></a>题目描述</h3><p>小C很喜欢倒着写单词，现在给你一行小C写的文本，你能把每个单词都反转并输出它们吗？</p><h3 id="输入-7"><a href="#输入-7" class="headerlink" title="输入"></a>输入</h3><p>输入包含多组测试样例。第一行为一个整数T，代表测试样例的数量，后面跟着T个测试样例。<br>每个测试样例占一行，包含多个单词。一行最多有1000个字符。</p><h3 id="输出-7"><a href="#输出-7" class="headerlink" title="输出"></a>输出</h3><p>对于每一个测试样例，你应该输出转换后的文本。</p><h3 id="样例输入-7"><a href="#样例输入-7" class="headerlink" title="样例输入"></a>样例输入</h3><figure class="highlight plaintext"><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">3</span><br><span class="line">olleh !dlrow</span><br><span class="line">I ekil .bulcmca</span><br><span class="line">I evol .mca</span><br></pre></td></tr></table></figure><h3 id="样例输出-7"><a href="#样例输出-7" class="headerlink" title="样例输出"></a>样例输出</h3><figure class="highlight plaintext"><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">hello world!</span><br><span class="line">I like acmclub.</span><br><span class="line">I love acm.</span><br></pre></td></tr></table></figure><h3 id="参考答案-7"><a href="#参考答案-7" class="headerlink" title="参考答案"></a>参考答案</h3><div class="hide-block"><button type="button" class="hide-button" style="">查看答案    </button><div class="hide-content"><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;vector&gt;</span></span></span><br><span class="line"></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="type">int</span> n = <span class="number">0</span>;</span><br><span class="line">    <span class="type">char</span> tmp[<span class="number">1005</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">    cin.<span class="built_in">getline</span>(tmp, <span class="number">1005</span>);</span><br><span class="line">    <span class="type">int</span> a = <span class="number">0</span>;</span><br><span class="line">    vector&lt;string&gt; ans;</span><br><span class="line">    <span class="keyword">while</span> (tmp[a] != <span class="string">&#x27; &#x27;</span> &amp;&amp; tmp[a] != <span class="number">0</span>) &#123;</span><br><span class="line">        n = n*<span class="number">10</span> + tmp[a++]-<span class="string">&#x27;0&#x27;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> k = <span class="number">0</span>; k &lt; n; ++k) &#123;</span><br><span class="line">        ans.<span class="built_in">emplace_back</span>(<span class="string">&quot;&quot;</span>);</span><br><span class="line">        <span class="type">char</span> chs[<span class="number">1005</span>] = &#123;<span class="number">0</span>&#125;;</span><br><span class="line">        cin.<span class="built_in">getline</span>(chs, <span class="number">1005</span>);</span><br><span class="line">        <span class="type">int</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (chs[i]!=<span class="number">0</span>) &#123;</span><br><span class="line">            string str;</span><br><span class="line">            <span class="type">int</span> j = i;</span><br><span class="line">            <span class="keyword">while</span> (chs[j] != <span class="string">&#x27; &#x27;</span> &amp;&amp; chs[j] != <span class="number">0</span>) &#123;</span><br><span class="line">                str.<span class="built_in">push_back</span>(chs[j++]);</span><br><span class="line">            &#125;</span><br><span class="line">            i = j+<span class="number">1</span>;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">auto</span> it = str.<span class="built_in">rbegin</span>();it != str.<span class="built_in">rend</span>(); ++it) &#123;</span><br><span class="line">                ans[k].<span class="built_in">push_back</span>(*it);</span><br><span class="line">            &#125;</span><br><span class="line">            ans[k].<span class="built_in">push_back</span>(<span class="string">&#x27; &#x27;</span>);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> p = <span class="number">0</span>; p &lt; n; ++p) &#123;</span><br><span class="line">        cout &lt;&lt; ans[p] &lt;&lt; endl;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div></div>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;此作业为某大学数据结构作业1，答案为我当时所写，仅供学习和参考&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;问题-A-查成绩&quot;&gt;&lt;a href=&quot;#问题-A-查成绩&quot; class=&quot;headerlink&quot; title=&quot;问题 A:</summary>
        
      
    
    
    
    <category term="c++" scheme="https://www.wotemo.com/categories/c/"/>
    
    <category term="Data Structure" scheme="https://www.wotemo.com/categories/c/Data-Structure/"/>
    
    
    <category term="c++" scheme="https://www.wotemo.com/tags/c/"/>
    
    <category term="Data Structure" scheme="https://www.wotemo.com/tags/Data-Structure/"/>
    
  </entry>
  
  <entry>
    <title>一款完备的SpringBoot博客平台</title>
    <link href="https://www.wotemo.com/posts/6404.html"/>
    <id>https://www.wotemo.com/posts/6404.html</id>
    <published>2024-02-07T13:30:33.000Z</published>
    <updated>2024-02-07T13:30:33.000Z</updated>
    
    <content type="html"><![CDATA[<h3 id="一-功能"><a href="#一-功能" class="headerlink" title="一. 功能"></a>一. 功能</h3><ol><li>用户注册与登录（JWT令牌单点登录）、注销</li><li>修改用户信息（管理员有更多权限）</li><li>发表文章（有点踩）</li><li>发表评论（可回复）</li><li>上传文件</li></ol><h3 id="二-使用工具"><a href="#二-使用工具" class="headerlink" title="二. 使用工具"></a>二. 使用工具</h3><ol><li>Springboot</li><li>Mybatis</li><li>Redis</li><li>Mysql</li><li>Leancloud</li><li>Postman（其他亦可）</li></ol><h3 id="三-Mysql数据库"><a href="#三-Mysql数据库" class="headerlink" title="三. Mysql数据库"></a>三. Mysql数据库</h3><p>表结构如下：</p><figure class="highlight plaintext"><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></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">create table user</span><br><span class="line">(</span><br><span class="line">    id                varchar(255)                         comment &#x27;用户的唯一标识ID&#x27;</span><br><span class="line">        primary key,</span><br><span class="line">    username          varchar(20) unique                   not null comment &#x27;唯一用户名&#x27;,</span><br><span class="line">    email             varchar(255)                         not null comment &#x27;用户邮箱&#x27;,</span><br><span class="line">    is_administrator  tinyint(1) default 0                 not null comment &#x27;用户是否为管理员&#x27;,</span><br><span class="line">    registration_time datetime   default CURRENT_TIMESTAMP not null comment &#x27;用户注册时间&#x27;,</span><br><span class="line">    update_time       datetime   default CURRENT_TIMESTAMP not null comment &#x27;用户修改数据时间&#x27;</span><br><span class="line">)</span><br><span class="line">    comment &#x27;用户信息&#x27;;</span><br><span class="line"></span><br><span class="line">create table user_info</span><br><span class="line">(</span><br><span class="line">    user_id          varchar(255)                                                                   comment &#x27;用户个人信息唯一标识ID&#x27;</span><br><span class="line">        primary key,</span><br><span class="line">    nickname    varchar(20)                                                                    null comment &#x27;用户昵称&#x27;,</span><br><span class="line">    avatar      varchar(510) default &#x27;https://img.wotemo.com/blog-platform/default_avatar.png&#x27; null comment &#x27;用户头像&#x27;,</span><br><span class="line">    birthday    date                                                                           not null comment &#x27;用户生日&#x27;,</span><br><span class="line">    address     varchar(255)                                                                   null comment &#x27;用户居住地(外键)&#x27;,</span><br><span class="line">    profile     varchar(200) default &#x27;该用户没有填写个人简介&#x27;                                       null comment &#x27;用户个人简介&#x27;,</span><br><span class="line">    create_time datetime     default CURRENT_TIMESTAMP                                         not null comment &#x27;用户个人信息创建时间&#x27;,</span><br><span class="line">    update_time datetime     default CURRENT_TIMESTAMP                                         not null comment &#x27;用户个人信息更新时间&#x27;</span><br><span class="line">)</span><br><span class="line">    comment &#x27;用户个人信息&#x27;;</span><br><span class="line"></span><br><span class="line">create table user_address</span><br><span class="line">(</span><br><span class="line">    id           varchar(255) comment &#x27;用户地址唯一标识&#x27;</span><br><span class="line">        primary key,</span><br><span class="line">    province     varchar(20)  not null comment &#x27;用户地址-省份&#x27;,</span><br><span class="line">    city         varchar(20)  not null comment &#x27;用户地址-城市&#x27;,</span><br><span class="line">    full_address varchar(255) null comment &#x27;用户详细地址&#x27;</span><br><span class="line">)</span><br><span class="line">    comment &#x27;用户地址&#x27;;</span><br><span class="line"></span><br><span class="line">create table user_article</span><br><span class="line">(</span><br><span class="line">    id          varchar(255)                       comment &#x27;文章唯一标识ID&#x27;</span><br><span class="line">        primary key,</span><br><span class="line">    author      varchar(255)                       not null comment &#x27;发表文章的用户ID&#x27;,</span><br><span class="line">    title       text                               not null comment &#x27;文章标题&#x27;,</span><br><span class="line">    content     mediumtext                         null comment &#x27;文章内容&#x27;,</span><br><span class="line">    dislike     int      default 0                 null comment &#x27;文章点踩(仅表示数量)&#x27;,</span><br><span class="line">    create_time datetime default CURRENT_TIMESTAMP not null comment &#x27;文章创建时间&#x27;,</span><br><span class="line">    update_time datetime default CURRENT_TIMESTAMP not null comment &#x27;文章更新时间&#x27;</span><br><span class="line">)</span><br><span class="line">    comment &#x27;用户发表的文章&#x27;;</span><br><span class="line"></span><br><span class="line">create table user_comment</span><br><span class="line">(</span><br><span class="line">    id             varchar(255)  comment &#x27;评论唯一标识ID&#x27;</span><br><span class="line">        primary key,</span><br><span class="line">    article_id     varchar(255)  not null comment &#x27;评论所属文章ID(外键)&#x27;,</span><br><span class="line">    user_id        varchar(255)  not null comment &#x27;发表用户ID(外键)&#x27;,</span><br><span class="line">    content        text          not null comment &#x27;评论内容&#x27;,</span><br><span class="line">    parent_id      varchar(255)  null comment &#x27;回复评论ID(外键)&#x27;,</span><br><span class="line">    create_time    datetime default CURRENT_TIMESTAMP not null comment &#x27;评论发表时间&#x27;</span><br><span class="line">)</span><br><span class="line">    comment &#x27;用户发表的评论&#x27;;</span><br><span class="line"></span><br><span class="line">create table user_like</span><br><span class="line">(</span><br><span class="line">    user_id     varchar(255)                       not null comment &#x27;点赞用户ID(外键)&#x27;,</span><br><span class="line">    article_id  varchar(255)                       not null comment &#x27;点赞文章ID(外键)&#x27;,</span><br><span class="line">    create_time datetime default CURRENT_TIMESTAMP not null comment &#x27;点赞时间&#x27;,</span><br><span class="line">    primary key (user_id, article_id)</span><br><span class="line">)</span><br><span class="line">    comment &#x27;用户文章点赞&#x27;;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">create table user_password</span><br><span class="line">(</span><br><span class="line">    user_id     varchar(255)                       comment &#x27;用户唯一ID&#x27;</span><br><span class="line">        primary key,</span><br><span class="line">    username    varchar(20)                        not null comment &#x27;用户名&#x27;,</span><br><span class="line">    email       varchar(255)                       not null comment &#x27;用户邮箱&#x27;,</span><br><span class="line">    password    varchar(16)                        not null comment &#x27;用户密码&#x27;,</span><br><span class="line">    create_time datetime default CURRENT_TIMESTAMP not null comment &#x27;创建时间&#x27;,</span><br><span class="line">    update_time datetime default CURRENT_TIMESTAMP not null comment &#x27;更新时间&#x27;</span><br><span class="line">)</span><br><span class="line">    comment &#x27;用户密码&#x27;;</span><br></pre></td></tr></table></figure><h3 id="四-修改必要数据"><a href="#四-修改必要数据" class="headerlink" title="四. 修改必要数据"></a>四. 修改必要数据</h3><p><strong>application.yaml</strong></p><ul><li>修改spring.datasource下的username和password等为你自己的mysql数据库账号密码等</li><li>修改spring.data.redis下的port和password等为你自己的redis端口密码等</li><li>修改spring.mail下的username、password、from等信息以发送邮箱</li><li>修改leancloud.initialization下的app-id、app-key、server-url为你自己的（Leancloud仅用于文件存储）</li><li>（可选）jwt下的secret-key（密钥，用于生成jwt）和ttl（失效时间）</li></ul><h3 id="五-Postman-API"><a href="#五-Postman-API" class="headerlink" title="五. Postman API"></a>五. Postman API</h3><p>项目的全部API在根目录下的postman文件夹中，可以在postman中导入</p><h3 id="六-代码获取"><a href="#六-代码获取" class="headerlink" title="六. 代码获取"></a>六. 代码获取</h3><ul><li><a href="https://github.com/wtmxxx/BlogPlatform">Github</a>：<a href="https://github.com/wtmxxx/BlogPlatform">https://github.com/wtmxxx/BlogPlatform</a></li></ul>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h3 id=&quot;一-功能&quot;&gt;&lt;a href=&quot;#一-功能&quot; class=&quot;headerlink&quot; title=&quot;一. 功能&quot;&gt;&lt;/a&gt;一.</summary>
        
      
    
    
    
    <category term="Java" scheme="https://www.wotemo.com/categories/Java/"/>
    
    <category term="SpringBoot" scheme="https://www.wotemo.com/categories/Java/SpringBoot/"/>
    
    <category term="BlogPlatform" scheme="https://www.wotemo.com/categories/Java/SpringBoot/BlogPlatform/"/>
    
    
    <category term="Java" scheme="https://www.wotemo.com/tags/Java/"/>
    
    <category term="SpringBoot" scheme="https://www.wotemo.com/tags/SpringBoot/"/>
    
    <category term="BlogPlatform" scheme="https://www.wotemo.com/tags/BlogPlatform/"/>
    
  </entry>
  
  <entry>
    <title>Java多线程补充（转）</title>
    <link href="https://www.wotemo.com/posts/7950.html"/>
    <id>https://www.wotemo.com/posts/7950.html</id>
    <published>2024-02-07T13:30:33.000Z</published>
    <updated>2024-02-07T13:30:33.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="1-线程状态"><a href="#1-线程状态" class="headerlink" title="1 线程状态"></a>1 线程状态</h1><h2 id="1-1-状态介绍"><a href="#1-1-状态介绍" class="headerlink" title="1.1 状态介绍"></a>1.1 状态介绍</h2><p>当线程被创建并启动以后，它既不是一启动就进入了执行状态，也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢？Java中的线程</p><p>状态被定义在了java.lang.Thread.State枚举类中，State枚举类的源码如下：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">enum</span> <span class="title class_">State</span> &#123;</span><br><span class="line">    </span><br><span class="line">        <span class="comment">/* 新建 */</span></span><br><span class="line">        NEW , </span><br><span class="line"></span><br><span class="line">        <span class="comment">/* 可运行状态 */</span></span><br><span class="line">        RUNNABLE , </span><br><span class="line"></span><br><span class="line">        <span class="comment">/* 阻塞状态 */</span></span><br><span class="line">        BLOCKED , </span><br><span class="line"></span><br><span class="line">        <span class="comment">/* 无限等待状态 */</span></span><br><span class="line">        WAITING , </span><br><span class="line"></span><br><span class="line">        <span class="comment">/* 计时等待 */</span></span><br><span class="line">        TIMED_WAITING , </span><br><span class="line"></span><br><span class="line">        <span class="comment">/* 终止 */</span></span><br><span class="line">        TERMINATED;</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="keyword">public</span> State <span class="title function_">getState</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> jdk.internal.misc.VM.toThreadState(threadStatus);</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>通过源码我们可以看到Java中的线程存在6种状态，每种线程状态的含义如下</p><table><thead><tr><th>线程状态</th><th>具体含义</th></tr></thead><tbody><tr><td>NEW</td><td>一个尚未启动的线程的状态。也称之为初始状态、开始状态。线程刚被创建，但是并未启动。还没调用start方法。MyThread t &#x3D; new MyThread()只有线程象，没有线程特征。</td></tr><tr><td>RUNNABLE</td><td>当我们调用线程对象的start方法，那么此时线程对象进入了RUNNABLE状态。那么此时才是真正的在JVM进程中创建了一个线程，线程一经启动并不是立即得到执行，线程的运行与否要听令与CPU的调度，那么我们把这个中间状态称之为可执行状态(RUNNABLE)也就是说它具备执行的资格，但是并没有真正的执行起来而是在等待CPU的度。</td></tr><tr><td>BLOCKED</td><td>当一个线程试图获取一个对象锁，而该对象锁被其他的线程持有，则该线程进入Blocked状态；当该线程持有锁时，该线程将变成Runnable状态。</td></tr><tr><td>WAITING</td><td>一个正在等待的线程的状态。也称之为等待状态。造成线程等待的原因有两种，分别是调用Object.wait()、join()方法。处于等待状态的线程，正在等待其他线程去执行一个特定的操作。例如：因为wait()而等待的线程正在等待另一个线程去调用notify()或notifyAll()；一个因为join()而等待的线程正在等待另一个线程结束。</td></tr><tr><td>TIMED_WAITING</td><td>一个在限定时间内等待的线程的状态。也称之为限时等待状态。造成线程限时等待状态的原因有三种，分别是：Thread.sleep(long)，Object.wait(long)、join(long)。</td></tr><tr><td>TERMINATED</td><td>一个完全运行完成的线程的状态。也称之为终止状态、结束状态</td></tr></tbody></table><p>各个状态的转换，如下图所示：</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571652681276.png" alt="1571652681276"> </p><h2 id="1-2-案例演示"><a href="#1-2-案例演示" class="headerlink" title="1.2 案例演示"></a>1.2 案例演示</h2><p>为了验证上面论述的状态即状态转换的正确性，也为了加深对线程状态转换的理解，下面通过三个案例演示线程间中的状态转换。</p><h3 id="1-2-1-案例一"><a href="#1-2-1-案例一" class="headerlink" title="1.2.1 案例一"></a>1.2.1 案例一</h3><p>本案例主要演示TIME_WAITING的状态转换。</p><p>需求：编写一段代码，依次显示一个线程的这些状态：NEW -&gt; RUNNABLE -&gt; TIME_WAITING -&gt; RUNNABLE -&gt;  TERMINATED</p><p>为了简化我们的开发，本次我们使用匿名内部类结合lambda表达式的方式使用多线程。</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadStateDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//定义一个内部线程</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;2.执行thread.start()之后，线程的状态：&quot;</span> + Thread.currentThread().getState());</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//休眠100毫秒</span></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) &#123;</span><br><span class="line">                e.printStackTrace();</span><br><span class="line">            &#125;</span><br><span class="line">            System.out.println(<span class="string">&quot;4.执行Thread.sleep(long)完成之后，线程的状态：&quot;</span> + Thread.currentThread().getState());</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取start()之前的状态</span></span><br><span class="line">        System.out.println(<span class="string">&quot;1.通过new初始化一个线程，但是还没有start()之前，线程的状态：&quot;</span> + thread.getState());</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">//休眠50毫秒</span></span><br><span class="line">        Thread.sleep(<span class="number">50</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//因为thread1需要休眠100毫秒，所以在第50毫秒，thread处于sleep状态</span></span><br><span class="line">        <span class="comment">//用main线程来获取thread1线程的状态，因为thread1线程睡眠时间较长</span></span><br><span class="line">        <span class="comment">//所以当main线程执行的时候，thread1线程还没有睡醒，还处于计时等待状态</span></span><br><span class="line">        System.out.println(<span class="string">&quot;3.执行Thread.sleep(long)时，线程的状态：&quot;</span> + thread.getState());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//thread1和main线程主动休眠150毫秒，所以在第150毫秒,thread早已执行完毕</span></span><br><span class="line">        Thread.sleep(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;5.线程执行完毕之后，线程的状态：&quot;</span> + thread.getState() + <span class="string">&quot;\n&quot;</span>);</span><br><span class="line"></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><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"><span class="number">1.</span>通过<span class="keyword">new</span>初始化一个线程，但是还没有start()之前，线程的状态：NEW</span><br><span class="line"><span class="number">2.</span>执行thread.start()之后，线程的状态：RUNNABLE</span><br><span class="line"><span class="number">3.</span>执行Thread.sleep(<span class="type">long</span>)时，线程的状态：TIMED_WAITING</span><br><span class="line"><span class="number">4.</span>执行Thread.sleep(<span class="type">long</span>)完成之后，线程的状态：RUNNABLE</span><br><span class="line"><span class="number">5.</span>线程执行完毕之后，线程的状态：TERMINATED</span><br></pre></td></tr></table></figure><h3 id="1-2-2-案例二"><a href="#1-2-2-案例二" class="headerlink" title="1.2.2 案例二"></a>1.2.2 案例二</h3><p>本案例主要演示WAITING的状态转换。</p><p>需求：编写一段代码，依次显示一个线程的这些状态：NEW -&gt; RUNNABLE -&gt; WAITING -&gt; RUNNABLE -&gt; TERMINATED</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadStateDemo02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//定义一个对象，用来加锁和解锁</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//定义一个内部线程</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;2.执行thread.start()之后，线程的状态：&quot;</span> + Thread.currentThread().getState());</span><br><span class="line">            <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//thread1需要休眠100毫秒</span></span><br><span class="line">                    Thread.sleep(<span class="number">100</span>);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">//thread1100毫秒之后，通过wait()方法释放obj对象是锁</span></span><br><span class="line">                    obj.wait();</span><br><span class="line">                    </span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#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">            System.out.println(<span class="string">&quot;4.被object.notify()方法唤醒之后，线程的状态：&quot;</span> + Thread.currentThread().getState());</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取start()之前的状态</span></span><br><span class="line">        System.out.println(<span class="string">&quot;1.通过new初始化一个线程，但是还没有start()之前，线程的状态：&quot;</span> + thread1.getState());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//启动线程</span></span><br><span class="line">        thread1.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//main线程休眠150毫秒</span></span><br><span class="line">        Thread.sleep(<span class="number">150</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//因为thread1在第100毫秒进入wait等待状态，所以第150秒肯定可以获取其状态</span></span><br><span class="line">        System.out.println(<span class="string">&quot;3.执行object.wait()时，线程的状态：&quot;</span> + thread1.getState());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//声明另一个线程进行解锁</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">                <span class="comment">//唤醒等待的线程</span></span><br><span class="line">                obj.notify();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//main线程休眠10毫秒等待thread1线程能够苏醒</span></span><br><span class="line">        Thread.sleep(<span class="number">10</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取thread1运行结束之后的状态</span></span><br><span class="line">        System.out.println(<span class="string">&quot;5.线程执行完毕之后，线程的状态：&quot;</span> + thread1.getState() + <span class="string">&quot;\n&quot;</span>);</span><br><span class="line"></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><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"><span class="number">1.</span>通过<span class="keyword">new</span>初始化一个线程，但是还没有start()之前，线程的状态：NEW</span><br><span class="line"><span class="number">2.</span>执行thread.start()之后，线程的状态：RUNNABLE</span><br><span class="line"><span class="number">3.</span>执行object.wait()时，线程的状态：WAITING</span><br><span class="line"><span class="number">4.</span>被object.notify()方法唤醒之后，线程的状态：RUNNABLE</span><br><span class="line"><span class="number">5.</span>线程执行完毕之后，线程的状态：TERMINATED</span><br></pre></td></tr></table></figure><h3 id="1-2-3-案例三"><a href="#1-2-3-案例三" class="headerlink" title="1.2.3 案例三"></a>1.2.3 案例三</h3><p>本案例主要演示BLOCKED的状态转换。</p><p>需求：编写一段代码，依次显示一个线程的这些状态：NEW -&gt; RUNNABLE -&gt; BLOCKED -&gt; RUNNABLE -&gt; TERMINATED</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadStateDemo03</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//定义一个对象，用来加锁和解锁</span></span><br><span class="line">        <span class="type">Object</span> <span class="variable">obj2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//定义一个线程，先抢占了obj2对象的锁</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (obj2) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(<span class="number">100</span>);              <span class="comment">//第一个线程要持有锁100毫秒</span></span><br><span class="line">                    obj2.wait();                          <span class="comment">//然后通过wait()方法进行等待状态，并释放obj2的对象锁</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#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;).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//定义目标线程，获取等待获取obj2的锁</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(() -&gt; &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;2.执行thread.start()之后，线程的状态：&quot;</span> + Thread.currentThread().getState());</span><br><span class="line">            <span class="keyword">synchronized</span> (obj2) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    Thread.sleep(<span class="number">100</span>);              <span class="comment">//thread3要持有对象锁100毫秒</span></span><br><span class="line">                    obj2.notify();                        <span class="comment">//然后通过notify()方法唤醒所有在ojb2上等待的线程继续执行后续操作</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#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">            System.out.println(<span class="string">&quot;4.阻塞结束后，线程的状态：&quot;</span> + Thread.currentThread().getState());</span><br><span class="line">        &#125;);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//获取start()之前的状态</span></span><br><span class="line">        System.out.println(<span class="string">&quot;1.通过new初始化一个线程，但是还没有thread.start()之前，线程的状态：&quot;</span> + thread.getState());</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">//先等100毫秒</span></span><br><span class="line">        Thread.sleep(<span class="number">50</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//第一个线程释放锁至少需要100毫秒，所以在第50毫秒时，thread正在因等待obj的对象锁而阻塞</span></span><br><span class="line">        System.out.println(<span class="string">&quot;3.因为等待锁而阻塞时，线程的状态：&quot;</span> + thread.getState());</span><br><span class="line"></span><br><span class="line">        <span class="comment">//再等300毫秒</span></span><br><span class="line">        Thread.sleep(<span class="number">300</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">//两个线程的执行时间加上之前等待的50毫秒总共是250毫秒，所以第300毫秒，所有的线程都已经执行完毕</span></span><br><span class="line">        System.out.println(<span class="string">&quot;5.线程执行完毕之后，线程的状态：&quot;</span> + thread.getState());</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><br><span class="line"><span class="comment">//---------------------代码简化--------------------------------------------</span></span><br><span class="line"><span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (obj)&#123;</span><br><span class="line">                <span class="keyword">try</span> &#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) &#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">        t1.start();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(()-&gt;&#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;线程开启之后的状态&quot;</span> + Thread.currentThread().getState());</span><br><span class="line">            <span class="keyword">synchronized</span> (obj)&#123;</span><br><span class="line">                System.out.println(<span class="string">&quot;进入之后的状态&quot;</span> + Thread.currentThread().getState());</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">        System.out.println(<span class="string">&quot;创建线程对象后,但是不调用start方法的状态&quot;</span> + t2.getState());</span><br><span class="line">        t2.start();</span><br><span class="line">        Thread.sleep(<span class="number">100</span>);</span><br><span class="line">        System.out.println(t2.getState());</span><br><span class="line">        Thread.sleep(<span class="number">2000</span>);</span><br><span class="line">        System.out.println(t2.getState());</span><br></pre></td></tr></table></figure><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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span>通过<span class="keyword">new</span>初始化一个线程，但是还没有thread.start()之前，线程的状态：NEW</span><br><span class="line"><span class="number">2.</span>执行thread.start()之后，线程的状态：RUNNABLE</span><br><span class="line"><span class="number">3.</span>因为等待锁而阻塞时，线程的状态：BLOCKED</span><br><span class="line"><span class="number">4.</span>阻塞结束后，线程的状态：RUNNABLE</span><br><span class="line"><span class="number">5.</span>线程执行完毕之后，线程的状态：TERMINATED</span><br></pre></td></tr></table></figure><p>通过上面3个案例的代码演示，我们可以证明开始章节说所述的线程状态以及线程状态转换都是正确的。</p><h1 id="2-线程池"><a href="#2-线程池" class="headerlink" title="2 线程池"></a>2 线程池</h1><h2 id="2-1-概述"><a href="#2-1-概述" class="headerlink" title="2.1 概述"></a>2.1 概述</h2><p>提到池，大家应该能想到的就是水池。水池就是一个容器，在该容器中存储了很多的水。那么什么是线程池呢？线程池也是可以看做成一个池子，在该池子中存储很多个线程。</p><p>线程池存在的意义：</p><p>系统创建一个线程的成本是比较高的，因为它涉及到与操作系统交互，当程序中需要创建大量生存期很短暂的线程时，频繁的创建和销毁线程对系统的资源消耗有可能大于业务处理是对系</p><p>统资源的消耗，这样就有点”舍本逐末”了。针对这一种情况，为了提高性能，我们就可以采用线程池。线程池在启动的时，会创建大量空闲线程，当我们向线程池提交任务的时，线程池就</p><p>会启动一个线程来执行该任务。等待任务执行完毕以后，线程并不会死亡，而是再次返回到线程池中称为空闲状态。等待下一次任务的执行。</p><h2 id="2-2-自定义线程池"><a href="#2-2-自定义线程池" class="headerlink" title="2.2 自定义线程池"></a>2.2 自定义线程池</h2><h3 id="2-2-1-线程池的设计思路"><a href="#2-2-1-线程池的设计思路" class="headerlink" title="2.2.1 线程池的设计思路"></a>2.2.1 线程池的设计思路</h3><p>线程池的思路和生产者消费者模型是很接近的</p><ol><li>准备一个任务容器</li><li>一次性启动多个(2个)消费者线程</li><li>刚开始任务容器是空的，所以线程都在wait</li><li>直到一个外部线程向这个任务容器中扔了一个”任务”，就会有一个消费者线程被唤醒</li><li>这个消费者线程取出”任务”，并且执行这个任务，执行完毕后，继续等待下一次任务的到来</li></ol><p>在整个过程中，都不需要创建新的线程，而是循环使用这些已经存在的线程。</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571655104091.png" alt="1571655104091"> </p><h3 id="2-2-2-代码实现"><a href="#2-2-2-代码实现" class="headerlink" title="2.2.2 代码实现"></a>2.2.2 代码实现</h3><p>实现思路：</p><ul><li>创建一个线程池类(ThreadPool)</li><li>在该类中定义两个成员变量poolSize(线程池初始化线程的个数) , BlockingQueue<Runnable>(任务容器)</li><li>通过构造方法来创建两个线程对象(消费者线程)，并且启动</li><li>使用内部类的方式去定义一个线程类(TaskThread),可以提供一个构造方法用来初始化线程名称</li><li>两个消费者线程需要不断的从任务容器中获取任务，如果没有任务，则线程处于阻塞状态。</li><li>提供一个方法(submit)向任务容器中添加任务</li><li>定义测试类进行测试</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPool</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化线程个数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">int</span> <span class="variable">DEFAULT_POOL_SIZE</span> <span class="operator">=</span> <span class="number">2</span> ;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 在该类中定义两个成员变量poolSize(线程池初始化线程的个数) , BlockingQueue&lt;Runnable&gt;(任务容器)</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">poolSize</span> <span class="operator">=</span> DEFAULT_POOL_SIZE ;</span><br><span class="line">    <span class="keyword">private</span> BlockingQueue&lt;Runnable&gt; blockingQueue = <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;Runnable&gt;() ;</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="title function_">ThreadPool</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="built_in">this</span>.initThread();</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">public</span> <span class="title function_">ThreadPool</span><span class="params">(<span class="type">int</span> poolSize)</span> &#123;</span><br><span class="line">        <span class="keyword">if</span>(poolSize &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="built_in">this</span>.poolSize = poolSize ;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="built_in">this</span>.initThread();</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">public</span> <span class="keyword">void</span> <span class="title function_">initThread</span><span class="params">()</span>&#123;</span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; poolSize ; x++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">TaskThread</span>(<span class="string">&quot;线程---&gt;&quot;</span> + x).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">// 提供一个方法(submit)向任务容器中添加任务</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">submit</span><span class="params">(Runnable runnable)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            blockingQueue.put(runnable);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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">    <span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TaskThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</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="title function_">TaskThread</span><span class="params">(String name)</span> &#123;</span><br><span class="line">            <span class="built_in">super</span>(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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 两个消费者线程需要不断的从任务容器中获取任务，如果没有任务，则线程处于阻塞状态。</span></span><br><span class="line">                    <span class="type">Runnable</span> <span class="variable">task</span> <span class="operator">=</span> blockingQueue.take();</span><br><span class="line">                    task.run();</span><br><span class="line"></span><br><span class="line">                &#125; <span class="keyword">catch</span> (InterruptedException e) &#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">        &#125;</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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建线程池对象,无参构造方法创建</span></span><br><span class="line">        <span class="comment">// ThreadPool threadPool = new ThreadPool();</span></span><br><span class="line">        <span class="type">ThreadPool</span> <span class="variable">threadPool</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPool</span>(<span class="number">5</span>);</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="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">10</span> ; x++) &#123;</span><br><span class="line">            threadPool.submit( () -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt;&gt;处理了任务&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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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><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">线程---&gt;<span class="number">0</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">1</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">0</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">1</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">0</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">1</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">0</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">1</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">0</span>----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;<span class="number">1</span>----&gt;&gt;&gt;处理了任务</span><br></pre></td></tr></table></figure><p>通过控制台的输出，我们可以看到在线程池中存在两个线程，通过这2个线程处理了10个任务。</p><p>使用有参构造方法创建线程池对象，传递的参数是5，控制台输出结果</p><figure class="highlight plaintext"><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">线程---&gt;3----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;4----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;2----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;0----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;2----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;4----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;3----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;1----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;2----&gt;&gt;&gt;处理了任务</span><br><span class="line">线程---&gt;0----&gt;&gt;&gt;处理了任务</span><br></pre></td></tr></table></figure><p>通过控制台的输出，我们可以看到在线程池中存在两个线程，通过这5个线程处理了10个任务。</p><h2 id="2-3-JDK中线程池"><a href="#2-3-JDK中线程池" class="headerlink" title="2.3 JDK中线程池"></a>2.3 JDK中线程池</h2><h3 id="2-3-1-Executors"><a href="#2-3-1-Executors" class="headerlink" title="2.3.1 Executors"></a>2.3.1 Executors</h3><p>JDK对线程池也进行了相关的实现，在真实企业开发中我们也很少去自定义线程池，而是使用JDK中自带的线程池。</p><p>我们可以使用Executors中所提供的<strong>静态</strong>方法来创建线程池。</p><p><font color="blue" size="3"><strong>获取线程池的方法</strong></font>：</p><p>&#x2F;&#x2F;通过不同的方法创建出来的线程池具有不同的特点。</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></pre></td><td class="code"><pre><span class="line">ExecutorService <span class="title function_">newCachedThreadPool</span><span class="params">()</span>: 创建一个可缓存线程池，可灵活的去创建线程，并且灵活的回收线程，若无可回收，则新建线程。</span><br><span class="line">ExecutorService <span class="title function_">newFixedThreadPool</span><span class="params">(<span class="type">int</span> nThreads)</span>: 初始化一个具有固定数量线程的线程池</span><br><span class="line">ExecutorService <span class="title function_">newSingleThreadExecutor</span><span class="params">()</span>: 初始化一个具有一个线程的线程池</span><br><span class="line"><span class="comment">//做完一个，再做一个，不停歇，直到做完，老黄牛性格</span></span><br><span class="line">ScheduledExecutorService <span class="title function_">newSingleThreadScheduledExecutor</span><span class="params">()</span>: 初始化一个具有一个线程的线程池，支持定时及周期性任务执行</span><br><span class="line"><span class="comment">//按照固定的计划去执行线程，一个做完之后按照计划再做另一个</span></span><br></pre></td></tr></table></figure><p>这个方法返回的都是ExecutorService类型的对象(ScheduledExecutorService继承ExecutorService),而ExecutorService可以看做就是一个线程池，那么ExecutorService</p><p>给我们提供了哪些方法供我们使用呢？</p><p><font color="blue" size="3"><strong>ExecutorService中的常见方法</strong></font>：</p><figure class="highlight java"><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">Future&lt;?&gt; submit(Runnable task)：提交任务方法</span><br><span class="line"><span class="keyword">void</span> <span class="title function_">shutdown</span><span class="params">()</span>：关闭线程池的方法</span><br></pre></td></tr></table></figure><p><font color="blue" size="3"><strong>案例1</strong></font>：演示newCachedThreadPool方法所获取到的线程池的特点</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 演示Executors中的newCachedThreadPool返回的线程池的特点</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取线程池对象</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newCachedThreadPool();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交任务</span></span><br><span class="line">        threadPool.submit(() -&gt; &#123;</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"></span><br><span class="line">        <span class="comment">// 提交任务</span></span><br><span class="line">        threadPool.submit(() -&gt; &#123;</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"></span><br><span class="line">        <span class="comment">// 不使用线程池了，还可以将线程池关闭</span></span><br><span class="line">        threadPool.shutdown();</span><br><span class="line"></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><figure class="highlight java"><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">pool-<span class="number">1</span>-thread-<span class="number">2</span>---执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>---执行了任务</span><br></pre></td></tr></table></figure><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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 演示Executors中的newCachedThreadPool返回的线程池的特点</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取线程池对象</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newCachedThreadPool();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交任务</span></span><br><span class="line">        threadPool.submit(() -&gt; &#123;</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"></span><br><span class="line">        <span class="comment">// 线程休眠2秒，主线程休眠2秒，此时之前提交的任务应该已经执行完毕</span></span><br><span class="line">        TimeUnit.SECONDS.sleep(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交任务</span></span><br><span class="line">        threadPool.submit(() -&gt; &#123;</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"></span><br><span class="line">        <span class="comment">// 不使用线程池了，还可以将线程池关闭</span></span><br><span class="line">        threadPool.shutdown();</span><br><span class="line"></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><figure class="highlight java"><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">pool-<span class="number">1</span>-thread-<span class="number">1</span>---执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>---执行了任务</span><br></pre></td></tr></table></figure><p>我们发现是通过一个线程执行了两个任务。此时就说明线程池中的线程”pool-1-thread-1”被线程池回收了，成为了空闲线程，当我们再次提交任务的时候，该线程就去执行新的任务。</p><p><font color="blue" size="3"><strong>案例2</strong></font>：演示newFixedThreadPool方法所获取到的线程池的特点</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo03</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 演示newFixedThreadPool方法所获取到的线程池的特点</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取线程池对象,初始化一个具有固定数量线程的线程池</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newFixedThreadPool(<span class="number">3</span>);  <span class="comment">// 在该线程池中存在3个线程</span></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="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line">            threadPool.submit( () -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-----&gt;&gt;&gt;执行了任务&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">        threadPool.shutdown();</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><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">pool-<span class="number">1</span>-thread-<span class="number">1</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span>-----&gt;&gt;&gt;执行了任务</span><br></pre></td></tr></table></figure><p>通过控制台的输出结果，我们可以看到5个任务是通过3个线程进行执行的，说明此线程池中存在三个线程对象</p><p><font color="blue" size="3"><strong>案例3</strong></font>：演示newSingleThreadExecutor方法所获取到的线程池的特点</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo04</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 演示newSingleThreadExecutor方法所获取到的线程池的特点</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取线程池对象,初始化一个具有一个线程的线程池</span></span><br><span class="line">        <span class="type">ExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newSingleThreadExecutor();</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="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line">            threadPool.submit(() -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-----&gt;&gt;&gt;执行了任务&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">        threadPool.shutdown();</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><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">pool-<span class="number">1</span>-thread-<span class="number">1</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>-----&gt;&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>-----&gt;&gt;&gt;执行了任务</span><br></pre></td></tr></table></figure><p>通过控制台的输出结果，我们可以看到5个任务是通过1个线程进行执行的,说明此线程池中只存在一个线程对象。</p><p><font color="blue" size="3"><strong>案例4</strong></font>： 演示newSingleThreadScheduledExecutor方法所获取到的线程池的特点(初始化一个具有一个线程的线程池)</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo05</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 演示：newSingleThreadScheduledExecutor方法所获取到的线程池的第一个特点(初始化一个具有一个线程的线程池)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取线程池对象</span></span><br><span class="line">        <span class="type">ScheduledExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newSingleThreadScheduledExecutor();</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="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line">            threadPool.submit(() -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt;执行了任务&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">        threadPool.shutdown();</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><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">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt;执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt;执行了任务</span><br></pre></td></tr></table></figure><p>通过控制台的输出结果，我们可以看到5个任务是通过1个线程进行执行的,说明此线程池中只存在一个线程对象。</p><p><font color="blue" size="3"><strong>案例5</strong></font>： 演示newSingleThreadScheduledExecutor方法所获取到的线程池的特点(支持定时及周期性任务执行)</p><p>ScheduledExecutorService中和定时以及周期性执行相关的方法</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></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">command: 任务类对象</span></span><br><span class="line"><span class="comment">delay  : 延迟多长时间开始执行任务, 任务提交到线程池以后我们需要等待多长时间开始执行这个任务</span></span><br><span class="line"><span class="comment">unit   : 指定时间操作单元</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> ScheduledFuture&lt;?&gt; schedule(Runnable command,<span class="type">long</span> delay, TimeUnit unit);</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">command: 任务类对象</span></span><br><span class="line"><span class="comment">initialDelay: 延迟多长时间开始第一次该执行任务, 任务提交到线程池以后我们需要等待多长时间开始第一次执行这个任务</span></span><br><span class="line"><span class="comment">period:        下一次执行该任务所对应的时间间隔</span></span><br><span class="line"><span class="comment">unit: 指定时间操作单元</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">public</span> ScheduledFuture&lt;?&gt; scheduleAtFixedRate(Runnable command, <span class="type">long</span> initialDelay, <span class="type">long</span> period, TimeUnit unit);</span><br></pre></td></tr></table></figure><p>测试类1(演示定时执行)</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo06</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 演示newSingleThreadScheduledExecutor方法所获取到的线程池的特点(支持定时及周期性任务执行)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取线程池对象</span></span><br><span class="line">        <span class="type">ScheduledExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newSingleThreadScheduledExecutor();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交任务,10s以后开始执行该任务</span></span><br><span class="line">        threadPool.schedule( () -&gt; &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt;&gt;执行了该任务&quot;</span>);</span><br><span class="line">        &#125; , <span class="number">10</span> , TimeUnit.SECONDS) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 关闭线程池</span></span><br><span class="line">        threadPool.shutdown();</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>测试类2(演示周期性执行)</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExecutorsDemo07</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 演示newSingleThreadScheduledExecutor方法所获取到的线程池的特点(支持定时及周期性任务执行)</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取线程池对象</span></span><br><span class="line">        <span class="type">ScheduledExecutorService</span> <span class="variable">threadPool</span> <span class="operator">=</span> Executors.newSingleThreadScheduledExecutor();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交任务,10s以后开始第一次执行该任务，然后每隔1秒执行一次</span></span><br><span class="line">        threadPool.scheduleAtFixedRate( () -&gt; &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt;&gt;执行了该任务&quot;</span>);</span><br><span class="line">        &#125; , <span class="number">10</span> ,<span class="number">1</span>, TimeUnit.SECONDS) ;</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="2-3-2-ThreadPoolExecutor"><a href="#2-3-2-ThreadPoolExecutor" class="headerlink" title="2.3.2 ThreadPoolExecutor"></a>2.3.2 ThreadPoolExecutor</h3><h4 id="1-基本使用"><a href="#1-基本使用" class="headerlink" title="1) 基本使用"></a>1) 基本使用</h4><p>刚才我们是通过Executors中的静态方法去创建线程池的，通过查看源代码我们发现，其底层都是通过ThreadPoolExecutor构建的。比如：newFixedThreadPool方法的源码</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> ExecutorService <span class="title function_">newFixedThreadPool</span><span class="params">(<span class="type">int</span> nThreads)</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 创建了ThreadPoolExecutor对象，然后直接返回</span></span><br><span class="line"><span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(nThreads, nThreads,<span class="number">0L</span>, TimeUnit.MILLISECONDS, <span class="keyword">new</span> <span class="title class_">LinkedBlockingQueue</span>&lt;Runnable&gt;());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>那么也可以使用ThreadPoolExecutor去创建线程池。</p><p>ThreadPoolExecutor最完整的构造方法：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">ThreadPoolExecutor</span><span class="params">(<span class="type">int</span> corePoolSize,</span></span><br><span class="line"><span class="params">                              <span class="type">int</span> maximumPoolSize,</span></span><br><span class="line"><span class="params">                              <span class="type">long</span> keepAliveTime,</span></span><br><span class="line"><span class="params">                              TimeUnit unit,</span></span><br><span class="line"><span class="params">                              BlockingQueue&lt;Runnable&gt; workQueue,</span></span><br><span class="line"><span class="params">                              ThreadFactory threadFactory,</span></span><br><span class="line"><span class="params">                              RejectedExecutionHandler handler)</span></span><br></pre></td></tr></table></figure><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><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">corePoolSize：   核心线程的最大值，不能小于<span class="number">0</span></span><br><span class="line">maximumPoolSize：最大线程数，不能小于等于<span class="number">0</span>，maximumPoolSize &gt;= corePoolSize</span><br><span class="line">keepAliveTime：  空闲线程最大存活时间,不能小于<span class="number">0</span></span><br><span class="line">unit：           时间单位</span><br><span class="line">workQueue：      任务队列，不能为<span class="literal">null</span></span><br><span class="line">threadFactory：  创建线程工厂,不能为<span class="literal">null</span>      </span><br><span class="line">handler：        任务的拒绝策略,不能为<span class="literal">null</span>    </span><br></pre></td></tr></table></figure><p>案例演示通过ThreadPoolExecutor创建线程池</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolExecutorDemo01</span> &#123;</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="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 通过ThreadPoolExecutor创建一个线程池对象</span></span><br><span class="line">        <span class="type">ThreadPoolExecutor</span> <span class="variable">threadPoolExecutor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span> , <span class="number">3</span> , <span class="number">60</span> , TimeUnit.SECONDS ,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ArrayBlockingQueue</span>&lt;Runnable&gt;(<span class="number">3</span>) , Executors.defaultThreadFactory() , <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>.AbortPolicy()) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 以上代码表示的意思是：核心线程池中的线程数量最大为1,整个线程池中最多存在3个线程,空闲线程最大的存活时间为60,时间单位为秒,阻塞队列使用的是有界阻塞队列</span></span><br><span class="line"><span class="comment">         * 容量为3,使用默认的线程工厂;以及默认的任务处理策略</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">        threadPoolExecutor.submit( () -&gt; &#123;</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;------&gt;&gt;&gt;执行了任务&quot;</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">        threadPoolExecutor.shutdown();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-工作原理"><a href="#2-工作原理" class="headerlink" title="2) 工作原理"></a>2) 工作原理</h4><p>接下来我们就来研究一下线程池的工作原理，如下图所示</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571732060388.png" alt="1571732060388"> </p><p>当我们通过submit方法向线程池中提交任务的时候，具体的工作流程如下：</p><ol><li>客户端每次提交一个任务，线程池就会在核心线程池中创建一个工作线程来执行这个任务。当核心线程池中的线程已满时，则进入下一步操作。</li><li>把任务试图存储到工作队列中。如果工作队列没有满，则将新提交的任务存储在这个工作队列里，等待核心线程池中的空闲线程执行。如果工作队列满了，则进入下个流程。</li><li>线程池会再次在非核心线程池区域去创建新工作线程来执行任务，直到当前线程池总线程数量超过最大线程数时，就是按照指定的任务处理策略处理多余的任务。</li></ol><p>举例说明：</p><p>假如有一个工厂，工厂里面有10个工人(正式员工)，每个工人同时只能做一件任务。因此只要当10个工人中有工人是空闲的，来了任务就分配给空闲的工人做；当10个工人都有任务在做时，</p><p>如果还来了任务，就把任务进行排队等待；如果说新任务数目增长的速度远远大于工人做任务的速度，那么此时工厂主管可能会想补救措施，比如重新招4个临时工人进来；然后就将任务也分配</p><p>给这4个临时工人做；如果说着14个工人做任务的速度还是不够，此时工厂主管可能就要考虑不再接收新的任务或者抛弃前面的一些任务了。当这14个工人当中有人空闲时，而新任务增长的速度</p><p>又比较缓慢，工厂主管可能就考虑辞掉4个临时工了，只保持原来的10个工人，毕竟请额外的工人是要花钱的。</p><p>这里的工厂可以看做成是一个线程池，每一个工人可以看做成是一个线程。其中10个正式员工，可以看做成是核心线程池中的线程，临时工就是非核心线程池中的线程。当临时工处于空闲状态</p><p>的时候，那么如果空闲的时间超过keepAliveTime所指定的时间，那么就会被销毁。</p><h4 id="3-案例演示"><a href="#3-案例演示" class="headerlink" title="3) 案例演示"></a>3) 案例演示</h4><p>接下来我们就通过一段代码的断点测试，来演示一下线程池的工作原理。</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolExecutorDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 核心线程数量为1 ， 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="type">ThreadPoolExecutor</span> <span class="variable">threadPoolExecutor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span> , <span class="number">3</span> , <span class="number">20</span> , TimeUnit.SECONDS ,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ArrayBlockingQueue</span>&lt;&gt;(<span class="number">1</span>) , Executors.defaultThreadFactory() , <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>.AbortPolicy()) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交3个任务，此时会产生一个核心线程,一个临时工线程，队列中会存在一个任务，20s后临时工线程被回收，核心线程不会被回收</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">3</span> ; x++) &#123;</span><br><span class="line">            threadPoolExecutor.submit(() -&gt; &#123;<span class="comment">// 断点位置</span></span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt; 执行了任务&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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>初次debug方式启动线程，查看变量值</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571735388181.png" alt="1571735388181"> </p><p>由于此时还没有提交任务，因此线程池中的线程数量为0，工作队列的任务数量也为0；提交一个任务</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571735465100.png" alt="1571735465100"> </p><p>再次查看各个值的变化</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571735607347.png" alt="1571735607347"> </p><p>再次提交一个任务</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571735465100.png" alt="1571735465100"> </p><p>再次查看各个值的变化</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571735715977.png" alt="1571735715977"> </p><p>此时会把第二个任务存储到工作队列中，因此工作队列的值为1了。再次提交一个任务</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571735465100.png" alt="1571735465100"> </p><p>再次查看各个值的变化</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571735904991.png" alt="1571735904991"> </p><p>此时3个任务都以及提交完毕，断点跳过。经过20s以后，再次查看该进程中的线程。</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571736824748.png" alt="1571736824748"> </p><p>我们发现非核心线程已经被线程池回收了。</p><h4 id="4-任务拒绝策略"><a href="#4-任务拒绝策略" class="headerlink" title="4) 任务拒绝策略"></a>4) 任务拒绝策略</h4><p>RejectedExecutionHandler是jdk提供的一个任务拒绝策略接口，它下面存在4个子类。</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">ThreadPoolExecutor.AbortPolicy:     丢弃任务并抛出RejectedExecutionException异常。是默认的策略。</span><br><span class="line">ThreadPoolExecutor.DiscardPolicy：    丢弃任务，但是不抛出异常 这是不推荐的做法。</span><br><span class="line">ThreadPoolExecutor.DiscardOldestPolicy：    抛弃队列中等待最久的任务 然后把当前任务加入队列中。</span><br><span class="line">ThreadPoolExecutor.CallerRunsPolicy:        调用任务的run()方法绕过线程池直接执行。</span><br></pre></td></tr></table></figure><p>注：明确线程池对多可执行的任务数 &#x3D; 队列容量 + 最大线程数</p><p><font color="blue" size="3"><strong>案例演示1</strong></font>：演示ThreadPoolExecutor.AbortPolicy任务处理策略</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolExecutorDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 核心线程数量为1 ， 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="type">ThreadPoolExecutor</span> <span class="variable">threadPoolExecutor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span> , <span class="number">3</span> , <span class="number">20</span> , TimeUnit.SECONDS ,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ArrayBlockingQueue</span>&lt;&gt;(<span class="number">1</span>) , Executors.defaultThreadFactory() , <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>.AbortPolicy()) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交5个任务，而该线程池最多可以处理4个任务，当我们使用AbortPolicy这个任务处理策略的时候，就会抛出异常</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line">            threadPoolExecutor.submit(() -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt; 执行了任务&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"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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><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">Exception in thread <span class="string">&quot;main&quot;</span> java.util.concurrent.RejectedExecutionException: Task java.util.concurrent.FutureTask@566776ad[Not completed, task = java.util.concurrent.Executors$RunnableAdapter<span class="meta">@edf4efb</span>[<span class="type">Wrapped</span> <span class="variable">task</span> <span class="operator">=</span> com.itheima.javase.thread.pool.demo04.ThreadPoolExecutorDemo01$$Lambda$<span class="number">14</span>/<span class="number">0x0000000100066840</span>@2f7a2457]] rejected from java.util.concurrent.ThreadPoolExecutor@6108b2d7[Running, <span class="type">pool</span> <span class="variable">size</span> <span class="operator">=</span> <span class="number">3</span>, <span class="type">active</span> <span class="variable">threads</span> <span class="operator">=</span> <span class="number">3</span>, <span class="type">queued</span> <span class="variable">tasks</span> <span class="operator">=</span> <span class="number">1</span>, <span class="type">completed</span> <span class="variable">tasks</span> <span class="operator">=</span> <span class="number">0</span>]</span><br><span class="line">at java.base/java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:<span class="number">2055</span>)</span><br><span class="line">at java.base/java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:<span class="number">825</span>)</span><br><span class="line">at java.base/java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:<span class="number">1355</span>)</span><br><span class="line">at java.base/java.util.concurrent.AbstractExecutorService.submit(AbstractExecutorService.java:<span class="number">118</span>)</span><br><span class="line">at com.itheima.javase.thread.pool.demo04.ThreadPoolExecutorDemo01.main(ThreadPoolExecutorDemo01.java:<span class="number">20</span>)</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span>----&gt;&gt; 执行了任务</span><br></pre></td></tr></table></figure><p>控制台报错，仅仅执行了4个任务，有一个任务被丢弃了</p><p><font color="blue" size="3"><strong>案例演示2</strong></font>：演示ThreadPoolExecutor.DiscardPolicy任务处理策略</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">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolExecutorDemo02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 核心线程数量为1 ， 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="type">ThreadPoolExecutor</span> <span class="variable">threadPoolExecutor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span> , <span class="number">3</span> , <span class="number">20</span> , TimeUnit.SECONDS ,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ArrayBlockingQueue</span>&lt;&gt;(<span class="number">1</span>) , Executors.defaultThreadFactory() , <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>.DiscardPolicy()) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交5个任务，而该线程池最多可以处理4个任务，当我们使用DiscardPolicy这个任务处理策略的时候，控制台不会报错</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line">            threadPoolExecutor.submit(() -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt; 执行了任务&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>控制台输出结果</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">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span>----&gt;&gt; 执行了任务</span><br></pre></td></tr></table></figure><p>控制台没有报错，仅仅执行了4个任务，有一个任务被丢弃了</p><p><font color="blue" size="3"><strong>案例演示3</strong></font>：演示ThreadPoolExecutor.DiscardOldestPolicy任务处理策略</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolExecutorDemo02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 核心线程数量为1 ， 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        ThreadPoolExecutor threadPoolExecutor;</span><br><span class="line">        threadPoolExecutor = <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span> , <span class="number">3</span> , <span class="number">20</span> , TimeUnit.SECONDS ,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ArrayBlockingQueue</span>&lt;&gt;(<span class="number">1</span>) , Executors.defaultThreadFactory() , <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>.DiscardOldestPolicy());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交5个任务</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 定义一个变量，来指定指定当前执行的任务;这个变量需要被final修饰</span></span><br><span class="line">            <span class="keyword">final</span> <span class="type">int</span> <span class="variable">y</span> <span class="operator">=</span> x ;</span><br><span class="line">            threadPoolExecutor.submit(() -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt; 执行了任务&quot;</span> + y);</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">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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">pool-<span class="number">1</span>-thread-<span class="number">2</span>----&gt;&gt; 执行了任务<span class="number">2</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt; 执行了任务<span class="number">0</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span>----&gt;&gt; 执行了任务<span class="number">3</span></span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt; 执行了任务<span class="number">4</span></span><br></pre></td></tr></table></figure><p>由于任务1在线程池中等待时间最长，因此任务1被丢弃。</p><p><font color="blue" size="3"><strong>案例演示4</strong></font>：演示ThreadPoolExecutor.CallerRunsPolicy任务处理策略</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ThreadPoolExecutorDemo04</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 核心线程数量为1 ， 最大线程池数量为3, 任务容器的容量为1 ,空闲线程的最大存在时间为20s</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        ThreadPoolExecutor threadPoolExecutor;</span><br><span class="line">        threadPoolExecutor = <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>(<span class="number">1</span> , <span class="number">3</span> , <span class="number">20</span> , TimeUnit.SECONDS ,</span><br><span class="line">                <span class="keyword">new</span> <span class="title class_">ArrayBlockingQueue</span>&lt;&gt;(<span class="number">1</span>) , Executors.defaultThreadFactory() , <span class="keyword">new</span> <span class="title class_">ThreadPoolExecutor</span>.CallerRunsPolicy());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 提交5个任务</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line">            threadPoolExecutor.submit(() -&gt; &#123;</span><br><span class="line">                System.out.println(Thread.currentThread().getName() + <span class="string">&quot;----&gt;&gt; 执行了任务&quot;</span>);</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">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">3</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">2</span>----&gt;&gt; 执行了任务</span><br><span class="line">pool-<span class="number">1</span>-thread-<span class="number">1</span>----&gt;&gt; 执行了任务</span><br><span class="line">main----&gt;&gt; 执行了任务</span><br></pre></td></tr></table></figure><p>通过控制台的输出，我们可以看到次策略没有通过线程池中的线程执行任务，而是直接调用任务的run()方法绕过线程池直接执行。</p><h1 id="3-volatile关键字"><a href="#3-volatile关键字" class="headerlink" title="3 volatile关键字"></a>3 volatile关键字</h1><h2 id="3-1-看程序说结果"><a href="#3-1-看程序说结果" class="headerlink" title="3.1 看程序说结果"></a>3.1 看程序说结果</h2><p>分析如下程序，说出在控制台的输出结果。</p><p>Thread的子类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义成员变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> <span class="literal">false</span> ;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFlag</span><span class="params">()</span> &#123; <span class="keyword">return</span> flag;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 线程休眠1秒</span></span><br><span class="line">        <span class="keyword">try</span> &#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) &#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="comment">// 将flag的值更改为true</span></span><br><span class="line">        <span class="built_in">this</span>.flag = <span class="literal">true</span> ;</span><br><span class="line">        System.out.println(<span class="string">&quot;flag=&quot;</span> + flag);</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>测试类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileThreadDemo01</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建VolatileThread线程对象</span></span><br><span class="line">        <span class="type">VolatileThread</span> <span class="variable">volatileThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VolatileThread</span>() ;</span><br><span class="line">        volatileThread.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 在main线程中获取开启的线程中flag的值</span></span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;main线程中获取开启的线程中flag的值为&quot;</span> + volatileThread.isFlag());</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>控制台输出结果</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="literal">false</span>，过了一段时间之后就变成了<span class="literal">true</span></span><br></pre></td></tr></table></figure><p>按照我们的分析，当我们把volatileThread线程启动起来以后，那么volatileThread线程开始执行。在volatileThread线程的run方法中，线程休眠1s，休眠一秒以后那么flag的值应该为</p><p>true，此时我们在主线程中不停的获取flag的值。发现前面释放false，后面是true</p><p>信息，那么这是为什么呢？要想知道原因，那么我们就需要学习一下JMM。</p><h2 id="3-2-JMM"><a href="#3-2-JMM" class="headerlink" title="3.2 JMM"></a>3.2 JMM</h2><p>概述：JMM(Java Memory Model)Java内存模型,是java虚拟机规范中所定义的一种内存模型。</p><p>Java内存模型(Java Memory Model)描述了Java程序中各种变量(线程共享变量)的访问规则，以及在JVM中将变量存储到内存和从内存中读取变量这样的底层细节。</p><p>特点：</p><ol><li><p>所有的共享变量都存储于主内存(计算机的RAM)这里所说的变量指的是实例变量和类变量。不包含局部变量，因为局部变量是线程私有的，因此不存在竞争问题。</p></li><li><p>每一个线程还存在自己的工作内存，线程的工作内存，保留了被线程使用的变量的工作副本。</p></li><li><p>线程对变量的所有的操作(读，写)都必须在工作内存中完成，而不能直接读写主内存中的变量，不同线程之间也不能直接访问对方工作内存中的变量，线程间变量的值的传递需要通过主</p><p>内存完成。</p></li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571743818653.png" alt="1571743818653"> </p><h2 id="3-3-问题分析"><a href="#3-3-问题分析" class="headerlink" title="3.3 问题分析"></a>3.3 问题分析</h2><p>了解了一下JMM,那么接下来我们就来分析一下上述程序产生问题的原因。</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571744627663.png" alt="1571744627663">  </p><p>产生问题的流程分析：</p><ol><li><p>VolatileThread线程从主内存读取到数据放入其对应的工作内存</p></li><li><p>将flag的值更改为true，但是这个时候flag的值还没有回写主内存</p></li><li><p>此时main线程读取到了flag的值并将其放入到自己的工作内存中，此时flag的值为false</p></li><li><p>VolatileThread线程将flag的值写回到主内存，但是main函数里面的while(true)调用的是系统比较底层的代码，速度快，快到没有时间再去读取主内存中的值，所以while(true)</p><p>读取到的值一直是false。(如果有一个时刻main线程从主内存中读取到了flag的最新值，那么if语句就可以执行，main线程何时从主内存中读取最新的值，我们无法控制)</p></li></ol><p>我们可以让主线程执行慢一点，执行慢一点以后，在某一个时刻，可能就会读取到主内存中最新的flag的值，那么if语句就可以进行执行。</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileThreadDemo02</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建VolatileThread线程对象</span></span><br><span class="line">        <span class="type">VolatileThread</span> <span class="variable">volatileThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VolatileThread</span>() ;</span><br><span class="line">        volatileThread.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// main方法</span></span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line">            <span class="keyword">if</span>(volatileThread.isFlag()) &#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="comment">// 让线程休眠100毫秒</span></span><br><span class="line">            TimeUnit.MILLISECONDS.sleep(<span class="number">100</span>);</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>控制台输出结果</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></pre></td><td class="code"><pre><span class="line">flag=<span class="literal">true</span></span><br><span class="line">执行了======</span><br><span class="line">执行了======</span><br><span class="line">执行了======</span><br><span class="line">....</span><br></pre></td></tr></table></figure><p>此时我们可以看到if语句已经执行了。当然我们在真实开发中可能不能使用这种方式来处理这个问题，那么这个问题应该怎么处理呢？我们就需要学习下一小节的内容。</p><h2 id="3-4-问题处理"><a href="#3-4-问题处理" class="headerlink" title="3.4 问题处理"></a>3.4 问题处理</h2><h3 id="3-4-1-加锁"><a href="#3-4-1-加锁" class="headerlink" title="3.4.1 加锁"></a>3.4.1 加锁</h3><p>第一种处理方案，我们可以通过加锁的方式进行处理。</p><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><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">public</span> <span class="keyword">class</span> <span class="title class_">VolatileThreadDemo03</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建VolatileThread线程对象</span></span><br><span class="line">        <span class="type">VolatileThread</span> <span class="variable">volatileThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VolatileThread</span>() ;</span><br><span class="line">        volatileThread.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// main方法</span></span><br><span class="line">        <span class="keyword">while</span>(<span class="literal">true</span>) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 加锁进行问题处理</span></span><br><span class="line">            <span class="keyword">synchronized</span> (volatileThread) &#123;</span><br><span class="line">                <span class="keyword">if</span>(volatileThread.isFlag()) &#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">        &#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>控制台输出结果</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></pre></td><td class="code"><pre><span class="line">flag=<span class="literal">true</span></span><br><span class="line">执行了======</span><br><span class="line">执行了======</span><br><span class="line">执行了======</span><br><span class="line">....</span><br></pre></td></tr></table></figure><p>工作原理说明</p><p>对上述代码加锁完毕以后，某一个线程支持该程序的过程如下：</p><p>a.线程获得锁</p><p>b.清空工作内存</p><p>c.从主内存拷贝共享变量最新的值到工作内存成为副本</p><p>d.执行代码</p><p>e.将修改后的副本的值刷新回主内存中</p><p>f.线程释放锁</p><h3 id="3-4-2-volatile关键字"><a href="#3-4-2-volatile关键字" class="headerlink" title="3.4.2 volatile关键字"></a>3.4.2 volatile关键字</h3><p>第二种处理方案，我们可以通过volatile关键字来修饰flag变量。</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义成员变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> <span class="literal">false</span> ;</span><br><span class="line">    <span class="keyword">public</span> <span class="type">boolean</span> <span class="title function_">isFlag</span><span class="params">()</span> &#123; <span class="keyword">return</span> flag;&#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 线程休眠1秒</span></span><br><span class="line">        <span class="keyword">try</span> &#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) &#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="comment">// 将flag的值更改为true</span></span><br><span class="line">        <span class="built_in">this</span>.flag = <span class="literal">true</span> ;</span><br><span class="line">        System.out.println(<span class="string">&quot;flag=&quot;</span> + flag);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//--------------------------------更新之后的案例-------------------------------------------</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileTest</span> <span class="keyword">extends</span> <span class="title class_">Thread</span>&#123;</span><br><span class="line">    <span class="type">boolean</span> <span class="variable">flag</span> <span class="operator">=</span> <span class="literal">false</span>;</span><br><span class="line">    <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (!flag) &#123;</span><br><span class="line">            i++;</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">&quot;stope&quot;</span> + i);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> Exception &#123;</span><br><span class="line">        <span class="type">VolatileTest</span> <span class="variable">vt</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VolatileTest</span>();</span><br><span class="line">        vt.start();</span><br><span class="line"></span><br><span class="line">        Thread.sleep(<span class="number">10</span>);</span><br><span class="line">        vt.flag = <span class="literal">true</span>;</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>控制台输出结果</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></pre></td><td class="code"><pre><span class="line">flag=<span class="literal">true</span></span><br><span class="line">执行了======</span><br><span class="line">执行了======</span><br><span class="line">执行了======</span><br><span class="line">....</span><br></pre></td></tr></table></figure><p>工作原理说明</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571746088704.png" alt="1571746088704"> </p><p>执行流程分析</p><ol><li>VolatileThread线程从主内存读取到数据放入其对应的工作内存</li><li>将flag的值更改为true，但是这个时候flag的值还没有回写主内存</li><li>此时main线程读取到了flag的值并将其放入到自己的工作内存中，此时flag的值为false</li><li>VolatileThread线程将flag的值写到主内存</li><li>main线程工作内存中的flag变量副本失效</li><li>main线程再次使用flag时，main线程会从主内存读取最新的值，放入到工作内存中，然后在进行使用</li></ol><p>总结： volatile保证不同线程对共享变量操作的可见性，也就是说一个线程修改了volatile修饰的变量，当修改写回主内存时，另外一个线程立即看到最新的值。</p><p>​      但是volatile不保证原子性(关于原子性问题，我们在下面的小节中会介绍)。</p><p>volatile与synchronized的区别：</p><ol><li><p>volatile只能修饰实例变量和类变量，而synchronized可以修饰方法，以及代码块。</p></li><li><p>volatile保证数据的可见性，但是不保证原子性(多线程进行写操作，不保证线程安全);而synchronized是一种排他（互斥）的机制(因此有时我们也将synchronized这种锁称</p><p>之为排他（互斥）锁)，synchronized修饰的代码块，被修饰的代码块称之为同步代码块，无法被中断可以保证原子性，也可以间接的保证可见性。</p></li></ol><h1 id="4-原子性"><a href="#4-原子性" class="headerlink" title="4 原子性"></a>4 原子性</h1><p>概述：所谓的原子性是指在一次操作或者多次操作中，要么所有的操作全部都得到了执行并且不会受到任何因素的干扰而中断，要么所有的操作都不执行，多个操作是一个不可以分割的整体。</p><p>&#x2F;&#x2F;比如说：你喂你女朋友吃冰淇淋，如果没有女朋友，你就假想一下，实在不行，你就喂你旁边的哥们吃一口冰淇淋。这就是一个不可分割的整体，一个是你喂，一个是她吃。这就是一个整体，如果没有她吃，那么你喂就没有意义，如果没有你喂，她吃就没有意义。</p><p>&#x2F;&#x2F;比如：从张三的账户给李四的账户转1000元，这个动作将包含两个基本的操作：从张三的账户扣除1000元，给李四的账户增加1000元。这两个操作必须符合原子性的要求，要么都成功要么</p><p>都失败。</p><h2 id="4-1-看程序说结果"><a href="#4-1-看程序说结果" class="headerlink" title="4.1 看程序说结果"></a>4.1 看程序说结果</h2><p>分析如下程序的执行结果</p><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><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="keyword">class</span> <span class="title class_">VolatileAtomicThread</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义一个int类型的变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span> ;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 对该变量进行++操作，100次</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">100</span> ; x++) &#123;</span><br><span class="line">            count++ ;</span><br><span class="line">            System.out.println(<span class="string">&quot;冰淇淋的个数 =========&gt;&gt;&gt;&gt; &quot;</span> + count);</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">&#125;</span><br></pre></td></tr></table></figure><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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileAtomicThreadDemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建VolatileAtomicThread对象</span></span><br><span class="line">        <span class="type">VolatileAtomicThread</span> <span class="variable">volatileAtomicThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VolatileAtomicThread</span>() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 开启100个线程对count进行++操作</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">100</span> ; x++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(volatileAtomicThread).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">&#125;</span><br></pre></td></tr></table></figure><p>程序分析：我们在主线程中通过for循环启动了100个线程，每一个线程都会对VolatileAtomicThread类中的count加100次。那么直接结果应该是10000。但是真正的执行结果和我们分析</p><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">....</span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9997</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9998</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9999</span></span><br></pre></td></tr></table></figure><p>通过控制台的输出，我们可以看到最终count的结果可能并不是10000。接下来我们就来分析一下问题产生的原因。</p><h2 id="4-2-问题分析说明"><a href="#4-2-问题分析说明" class="headerlink" title="4.2 问题分析说明"></a>4.2 问题分析说明</h2><p>以上问题主要是发生在count++操作上：</p><p>count++操作包含3个步骤：</p><ul><li>从主内存中读取数据到工作内存</li><li>对工作内存中的数据进行++操作</li><li>将工作内存中的数据写回到主内存</li></ul><p>count++操作不是一个原子性操作，也就是说在某一个时刻对某一个操作的执行，有可能被其他的线程打断。</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571794778139.png" alt="1571794778139"> </p><p>产生问题的执行流程分析：</p><ol><li><p>假设此时count的值是100，线程A需要对改变量进行自增1的操作，首先它需要从主内存中读取变量count的值。由于CPU的切换关系，此时CPU的执行权被切换到了B线程。A线程就处</p><p>于就绪状态，B线程处于运行状态。</p></li><li><p>线程B也需要从主内存中读取count变量的值,由于线程A没有对count值做任何修改因此此时B读取到的数据还是100</p></li><li><p>线程B工作内存中对count执行了+1操作，但是未刷新之主内存中</p></li><li><p>此时CPU的执行权切换到了A线程上，由于此时线程B没有将工作内存中的数据刷新到主内存，因此A线程工作内存中的变量值还是100，没有失效。A线程对工作内存中的数据进行了+1操作。</p></li><li><p>线程B将101写入到主内存</p></li><li><p>线程A将101写入到主内存</p></li></ol><p>虽然计算了2次，但是只对A进行了1次修改。</p><h2 id="4-3-volatile原子性测试"><a href="#4-3-volatile原子性测试" class="headerlink" title="4.3 volatile原子性测试"></a>4.3 volatile原子性测试</h2><p>我们刚才说到了volatile在多线程环境下只保证了共享变量在多个线程间的可见性，但是不保证原子性。那么接下来我们就来做一个测试。测试的思想，就是使用volatile修饰count。</p><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><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="keyword">class</span> <span class="title class_">VolatileAtomicThread</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义一个int类型的变量,并且使用volatile修饰</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span> ;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 对该变量进行++操作，100次</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">100</span> ; x++) &#123;</span><br><span class="line">            count++ ;</span><br><span class="line">            System.out.println(<span class="string">&quot;count =========&gt;&gt;&gt;&gt; &quot;</span> + count);</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">&#125;</span><br></pre></td></tr></table></figure><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">...</span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9997</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9998</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9999</span></span><br></pre></td></tr></table></figure><p>通过控制台结果的输出，我们可以看到程序还是会出现问题。因此也就证明volatile关键字是不保证原子性的。</p><h2 id="4-4-volatile使用场景"><a href="#4-4-volatile使用场景" class="headerlink" title="4.4 volatile使用场景"></a>4.4 volatile使用场景</h2><p>volatile关键字不保证原子性操作，那么同学们可能会存在一些疑问，volatile关键字在什么情况下进行使用呢？这里我们举两个基本的使用场景。</p><h3 id="4-4-1-状态标志"><a href="#4-4-1-状态标志" class="headerlink" title="4.4.1 状态标志"></a>4.4.1 状态标志</h3><p>比如现在存在一个线程不断向控制台输出一段话”传智播客中国IT教育的标杆….”,当这个线程执行5秒以后，将该线程结束。</p><p>实现思路：定义一个boolean类型的变量，这个变量就相当于一个标志。当这个变量的值为true的时候，线程一直执行，10秒以后我们把这个变量的值更改为false，此时结束该线程的执行。</p><p>为了保证一个线程对这个变量的修改，另外一个线程立马可以看到，这个变量就需要通过volatile关键字进行修饰。</p><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileUseThread</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义标志变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">boolean</span> <span class="variable">shutdown</span> <span class="operator">=</span> <span class="literal">false</span> ;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">while</span>(!shutdown) &#123;</span><br><span class="line">            System.out.println(<span class="string">&quot;传智播客中国IT教育的标杆....&quot;</span>);</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">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">shutdown</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.shutdown = <span class="literal">true</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>测试类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileUseThreadDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建线程任务类对象</span></span><br><span class="line">        <span class="type">VolatileUseThread</span> <span class="variable">volatileUseThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">VolatileUseThread</span>() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建线程对象</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">thread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(volatileUseThread);</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">        TimeUnit.SECONDS.sleep(<span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 关闭线程</span></span><br><span class="line">        volatileUseThread.shutdown();</span><br><span class="line"></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>观察控制台输出，volatileUseThread线程执行5秒以后程序结束。</p><h3 id="4-4-2-独立观察"><a href="#4-4-2-独立观察" class="headerlink" title="4.4.2 独立观察"></a>4.4.2 独立观察</h3><p>&#x2F;&#x2F;AI养猪。。。。</p><p>&#x2F;&#x2F;设备区测量温度</p><p>&#x2F;&#x2F;当温度高了。。。需要给猪开空调。。。加冰棍。。。加喝的水。。。</p><p>volatile的另一种简单使用场景是：定期”发布”观察结果供程序内部使用。例如，假设有一种环境传感器能够感觉环境温度。一个后台线程可能会每隔几秒读取一次该传感器数据，并更新包</p><p>含这个volatile变量的值。然后，其他线程可以读取这个变量，从而随时能够看到最新的温度值。这种使用就是多个线程操作共享变量，但是是有一个线程对其进行写操作，其他的线程都是读。</p><p>我们可以设计一个程序，模拟上面的温度传感器案例。</p><p>实现步说明</p><ol><li><p>定义一个温度传感器(TemperatureSensor)的类,在该类中定义两个成员变量(temperature(温度值)，type(传感器的类型))，temperature变量需要被volatile修饰</p></li><li><p>定义一个读取温度传感器的线程的任务类(ReadTemperatureRunnable)，该类需要定义一个TemperatureSensor类型的成员变量(该线程需要读取温度传感器的数据)</p></li><li><p>定义一个定时采集温度的线程任务类(GatherTemperatureRunnable)，该类需要定义一个TemperatureSensor类型的成员变量(该线程需要将读到的温度设置给传感器)</p></li><li><p>创建测试类(TemperatureSensorDemo)</p><ol><li>创建TemperatureSensor对象</li><li>创建ReadTemperatureRunnable类对象，把TemperatureSensor作为构造方法的参数传递过来</li><li>创建GatherTemperatureRunnable类对象，把TemperatureSensor作为构造方法的参数传递过来</li><li>创建2个Thread对象，并启动，把第二步所创建的对象作为构造方法参数传递过来，这两个线程负责读取TemperatureSensor中的温度数据</li><li>创建1个Thread对象，并启动，把第三步所创建的对象作为构造方法参数传递过来，这个线程负责读取定时采集数据中的温度数据</li></ol></li></ol><p>TemperatureSensor类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TemperatureSensor</span> &#123;        <span class="comment">// 温度传感器类</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">int</span> temperature ;  <span class="comment">// 温度值</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> String type ;               <span class="comment">// 传感器的类型</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="type">int</span> <span class="title function_">getTemperature</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> temperature;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setTemperature</span><span class="params">(<span class="type">int</span> temperature)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.temperature = temperature;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> String <span class="title function_">getType</span><span class="params">()</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> type;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">setType</span><span class="params">(String type)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.type = type;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>ReadTemperatureRunnable类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ReadTemperatureRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 温度传感器</span></span><br><span class="line">    <span class="keyword">private</span> TemperatureSensor temperatureSensor ;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ReadTemperatureRunnable</span><span class="params">(TemperatureSensor temperatureSensor)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.temperatureSensor = temperatureSensor ;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</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="literal">true</span>) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 读取数据</span></span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;---读取到的温度数据为------&gt;&gt;&gt; &quot;</span> + temperatureSensor.getTemperature());</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 让线程休眠100毫秒，便于观察</span></span><br><span class="line">                TimeUnit.MILLISECONDS.sleep(<span class="number">100</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#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">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>GatherTemperatureRunnable类</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 class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">GatherTemperatureRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 温度传感器</span></span><br><span class="line">    <span class="keyword">private</span> TemperatureSensor temperatureSensor ;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">GatherTemperatureRunnable</span><span class="params">(TemperatureSensor temperatureSensor)</span> &#123;</span><br><span class="line">        <span class="built_in">this</span>.temperatureSensor = temperatureSensor ;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义一个变量，表示环境初始温度</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">temperature</span> <span class="operator">=</span> <span class="number">23</span> ;</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="literal">true</span>) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 将采集到的数据设置给温度传感器</span></span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-----采集到的数据为-----&gt;&gt;&gt; &quot;</span> + temperature);</span><br><span class="line">            temperatureSensor.setTemperature(temperature);</span><br><span class="line"></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 线程休眠2秒,模拟每隔两秒采集一次数据</span></span><br><span class="line">                TimeUnit.SECONDS.sleep(<span class="number">2</span>);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException e) &#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="comment">// 环境温度改变</span></span><br><span class="line">            temperature += <span class="number">2</span> ;</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">&#125;</span><br></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">TemperatureSensorDemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建TemperatureSensor对象</span></span><br><span class="line">        <span class="type">TemperatureSensor</span> <span class="variable">temperatureSensor</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">TemperatureSensor</span>();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建ReadTemperatureRunnable类对象</span></span><br><span class="line">        <span class="type">ReadTemperatureRunnable</span> <span class="variable">readTemperatureRunnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ReadTemperatureRunnable</span>(temperatureSensor) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建GatherTemperatureRunnable类对象</span></span><br><span class="line">        <span class="type">GatherTemperatureRunnable</span> <span class="variable">gatherTemperatureRunnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">GatherTemperatureRunnable</span>(temperatureSensor) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建2个Thread对象，并启动; 这两个线程负责读取TemperatureSensor中的温度数据</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">2</span> ; x++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(readTemperatureRunnable).start();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建1个Thread对象，并启动，这个线程负责读取定时采集数据中的温度数据</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">gatherThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(gatherTemperatureRunnable);</span><br><span class="line">        gatherThread.setName(<span class="string">&quot;温度采集线程&quot;</span>);</span><br><span class="line">        gatherThread.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></pre></td></tr></table></figure><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><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><br><span class="line">温度采集线程-----采集到的数据为-----&gt;&gt;&gt; <span class="number">23</span></span><br><span class="line">Thread-<span class="number">0</span>---读取到的温度数据为------&gt;&gt;&gt; <span class="number">23</span></span><br><span class="line">...</span><br><span class="line">温度采集线程-----采集到的数据为-----&gt;&gt;&gt; <span class="number">25</span></span><br><span class="line">Thread-<span class="number">1</span>---读取到的温度数据为------&gt;&gt;&gt; <span class="number">25</span></span><br><span class="line">...</span><br></pre></td></tr></table></figure><p>通过控制台的输出，我们可以看到当温度采集线程刚采集到环境温度以后，那么此时两个温度读取线程就可以立即感知到环境温度的变化。</p><h2 id="4-5-问题处理"><a href="#4-5-问题处理" class="headerlink" title="4.5 问题处理"></a>4.5 问题处理</h2><p>接下来我们就来讲解一下我们上述案例(引入原子性问题的案例)的解决方案。</p><h3 id="4-5-1-锁机制"><a href="#4-5-1-锁机制" class="headerlink" title="4.5.1 锁机制"></a>4.5.1 锁机制</h3><p>我们可以给count++操作添加锁，那么count++操作就是临界区中的代码，临界区中的代码一次只能被一个线程去执行，所以count++就变成了原子操作。</p><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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileAtomicThread</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义一个int类型的变量,</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">int</span> <span class="variable">count</span> <span class="operator">=</span> <span class="number">0</span> ;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义一个Object类型的变量，该变量将作为同步代码块的锁</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Object</span> <span class="variable">obj</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Object</span>();</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 对该变量进行++操作，100次</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">100</span> ; x++) &#123;</span><br><span class="line">            <span class="keyword">synchronized</span> (obj)&#123;</span><br><span class="line">                count++ ;</span><br><span class="line">                System.out.println(<span class="string">&quot;count =========&gt;&gt;&gt;&gt; &quot;</span> + count);</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">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9998</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9999</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">10000</span></span><br></pre></td></tr></table></figure><h3 id="4-5-2-原子类"><a href="#4-5-2-原子类" class="headerlink" title="4.5.2 原子类"></a>4.5.2 原子类</h3><h4 id="1-AtomicInteger"><a href="#1-AtomicInteger" class="headerlink" title="1) AtomicInteger"></a>1) AtomicInteger</h4><p>概述：java从JDK1.5开始提供了java.util.concurrent.atomic包(简称Atomic包)，这个包中的原子操作类提供了一种用法简单，性能高效，线程安全地更新一个变量的方式。因为变</p><p>量的类型有很多种，所以在Atomic包里一共提供了13个类，属于4种类型的原子更新方式，分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。本次我们只讲解</p><p>使用原子的方式更新基本类型，使用原子的方式更新基本类型Atomic包提供了以下3个类：</p><p>AtomicBoolean： 原子更新布尔类型</p><p>AtomicInteger： 原子更新整型</p><p>AtomicLong：原子更新长整型</p><p>以上3个类提供的方法几乎一模一样，所以本节仅以AtomicInteger为例进行讲解，AtomicInteger的常用方法如下：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">AtomicInteger</span><span class="params">()</span>：   初始化一个默认值为<span class="number">0</span>的原子型Integer</span><br><span class="line"><span class="keyword">public</span> <span class="title function_">AtomicInteger</span><span class="params">(<span class="type">int</span> initialValue)</span>： 初始化一个指定值的原子型Integer</span><br><span class="line"></span><br><span class="line"><span class="type">int</span> <span class="title function_">get</span><span class="params">()</span>:     获取值</span><br><span class="line"><span class="type">int</span> <span class="title function_">getAndIncrement</span><span class="params">()</span>:       以原子方式将当前值加<span class="number">1</span>，注意，这里返回的是自增前的值。</span><br><span class="line"><span class="type">int</span> <span class="title function_">incrementAndGet</span><span class="params">()</span>:      以原子方式将当前值加<span class="number">1</span>，注意，这里返回的是自增后的值。</span><br><span class="line"><span class="type">int</span> <span class="title function_">addAndGet</span><span class="params">(<span class="type">int</span> data)</span>: 以原子方式将输入的数值与实例中的值（AtomicInteger里的value）相加，并返回结果。</span><br><span class="line"><span class="type">int</span> <span class="title function_">getAndSet</span><span class="params">(<span class="type">int</span> value)</span>:    以原子方式设置为newValue的值，并返回旧值。</span><br></pre></td></tr></table></figure><p>案例演示AtomicInteger的基本使用：</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AtomicIntegerDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 原子型Integer</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 构造方法</span></span><br><span class="line">        <span class="comment">// public AtomicInteger()：初始化一个默认值为0的原子型Integer</span></span><br><span class="line">        <span class="comment">// AtomicInteger atomicInteger = new AtomicInteger() ;</span></span><br><span class="line">        <span class="comment">// System.out.println(atomicInteger);</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">// public AtomicInteger(int initialValue)： 初始化一个指定值的原子型Integer</span></span><br><span class="line">        <span class="type">AtomicInteger</span> <span class="variable">atomicInteger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>(<span class="number">5</span>) ;</span><br><span class="line">        System.out.println(atomicInteger);</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取值</span></span><br><span class="line">        System.out.println(atomicInteger.get());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 以原子方式将当前值加1，这里返回的是自增前的值</span></span><br><span class="line">        System.out.println(atomicInteger.getAndIncrement());</span><br><span class="line">        System.out.println(atomicInteger.get());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 以原子方式将当前值加1，这里返回的是自增后的值</span></span><br><span class="line">        System.out.println(atomicInteger.incrementAndGet());</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 以原子方式将输入的数值与实例中的值（AtomicInteger里的value）相加，并返回结果</span></span><br><span class="line">        System.out.println(atomicInteger.addAndGet(<span class="number">8</span>));</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 以原子方式设置为newValue的值，并返回旧值</span></span><br><span class="line">        System.out.println(atomicInteger.getAndSet(<span class="number">20</span>));</span><br><span class="line">        System.out.println(atomicInteger.get());</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="2-案例改造"><a href="#2-案例改造" class="headerlink" title="2) 案例改造"></a>2) 案例改造</h4><p>使用AtomicInteger对案例进行改造。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">VolatileAtomicThread</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义一个int类型的变量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">AtomicInteger</span> <span class="variable">atomicInteger</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">AtomicInteger</span>() ;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 对该变量进行++操作，100次</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">100</span> ; x++) &#123;</span><br><span class="line">            <span class="type">int</span> <span class="variable">i</span> <span class="operator">=</span> atomicInteger.incrementAndGet();</span><br><span class="line">            System.out.println(<span class="string">&quot;count =========&gt;&gt;&gt;&gt; &quot;</span> + i);</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">&#125;</span><br></pre></td></tr></table></figure><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">...</span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9998</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">9999</span></span><br><span class="line">count =========&gt;&gt;&gt;&gt; <span class="number">10000</span></span><br></pre></td></tr></table></figure><p>通过控制台的执行结果，我们可以看到最终得到的结果就是10000，因此也就证明AtomicInteger所提供的方法是原子性操作方法。</p><h2 id="4-6-AtomicInteger原理"><a href="#4-6-AtomicInteger原理" class="headerlink" title="4.6 AtomicInteger原理"></a>4.6 AtomicInteger原理</h2><h3 id="4-6-1-原理介绍"><a href="#4-6-1-原理介绍" class="headerlink" title="4.6.1 原理介绍"></a>4.6.1 原理介绍</h3><p>AtomicInteger的本质：自旋锁 + CAS算法</p><p>CAS的全成是： Compare And Swap(比较再交换); 是现代CPU广泛支持的一种对内存中的共享数据进行操作的一种特殊指令。CAS可以将read-modify-write转换为原子操作，这个原子操作</p><p>直接由处理器保证。CAS有3个操作数，内存值V，旧的预期值A，要修改的新值B。当且仅当旧预期值A和内存值V相同时，将内存值V修改为B并返回true，否则什么都不做，并返回false。</p><p>举例说明：</p><ol><li>在内存值V当中，存储着值为10的变量。</li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571817059527.png" alt="1571817059527">  </p><ol start="2"><li>此时线程1想要把变量的值增加1。对线程1来说，旧的预期值 A &#x3D; 10 ，要修改的新值 B &#x3D; 11。</li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571817085047.png" alt="1571817085047"> </p><ol start="3"><li>在线程1要提交更新之前，另一个线程2抢先一步，把内存值V中的变量值率先更新成了11。</li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571817628904.png" alt="1571817628904"> </p><ol start="4"><li>线程1开始提交更新，首先进行A和内存值V的实际值比较（Compare），发现A不等于V的值，提交失败。</li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571818176635.png" alt="1571818176635"> </p><ol start="5"><li>线程1重新获取内存值V作为当前A的值，并重新计算想要修改的新值。此时对线程1来说，A &#x3D; 11，B &#x3D; 12。这个重新尝试的过程被称为<font color="red" size="4"><strong>自旋</strong></font>。</li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571818465276.png" alt="1571818465276"> </p><ol start="6"><li>这一次比较幸运，没有其他线程改变V的值。线程1进行Compare，发现A和V的值是相等的。</li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571818597998.png" alt="1571818597998"> </p><ol start="7"><li>线程1进行SWAP，把内存V的值替换为B，也就是12。</li></ol><p><img src="https://img.wotemo.com/img/multithreading-assets/1571818747880.png" alt="1571818747880"> </p><p>举例说明：这好比春节的时候抢火车票，下手快的会抢先买到票，而下手慢的可以再次尝试，直到买到票。</p><h3 id="4-6-2-源码分析"><a href="#4-6-2-源码分析" class="headerlink" title="4.6.2 源码分析"></a>4.6.2 源码分析</h3><p>那么接下来我们就来查看一下AtomicInteger类中incrementAndGet方法的源码。</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">AtomicInteger</span> <span class="keyword">extends</span> <span class="title class_">Number</span> <span class="keyword">implements</span> <span class="title class_">java</span>.io.Serializable &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// cas算法的实现类</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> jdk.internal.misc.<span class="type">Unsafe</span> <span class="variable">U</span> <span class="operator">=</span> jdk.internal.misc.Unsafe.getUnsafe();</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 表示变量值在内存中的偏移量地址，unsafe类就是根据内存偏移量地址获取数据值。</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="type">long</span> <span class="variable">VALUE</span> <span class="operator">=</span> U.objectFieldOffset(AtomicInteger.class, <span class="string">&quot;value&quot;</span>);</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="type">int</span> value;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 以原子方式将当前值加1，这里返回的是自增后的值</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">incrementAndGet</span><span class="params">()</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">/* this表示当前AtomicInteger对象 ，1表示要增加的值 */</span></span><br><span class="line">        <span class="keyword">return</span> U.getAndAddInt(<span class="built_in">this</span>, VALUE, <span class="number">1</span>) + <span class="number">1</span>;<span class="comment">// 调用Unsafe类中的getAndAddInt方法</span></span><br><span class="line">        </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>UnSafe类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Unsafe</span> &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Unsafe类中的getAndAddInt方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">int</span> <span class="title function_">getAndAddInt</span><span class="params">(Object o, <span class="type">long</span> offset, <span class="type">int</span> delta)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="type">int</span> v;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// do...while就是自旋操作,当CAS成功以后，循环结束</span></span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            <span class="comment">// 获取AtomicInteger类中所封装的int类型的值，就相当于旧的预期值A</span></span><br><span class="line">            v = getIntVolatile(o, offset); </span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 调用本类的weakCompareAndSetInt方法实现比较在交换； o: AtomicInteger对象, v: 相当于旧的预期值A, v + delta：新值B</span></span><br><span class="line">        &#125; <span class="keyword">while</span> (!weakCompareAndSetInt(o, offset, v, v + delta));</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">return</span> v;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Unsafe类中的weakCompareAndSetInt方法</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="type">boolean</span> <span class="title function_">weakCompareAndSetInt</span><span class="params">(Object o, <span class="type">long</span> offset, <span class="type">int</span> expected, <span class="type">int</span> x)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> compareAndSetInt(o, offset, expected, x);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 本地方法，调用CPU指令实现CAS</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">native</span> <span class="type">boolean</span> <span class="title function_">compareAndSetInt</span><span class="params">(Object o, <span class="type">long</span> offset, <span class="type">int</span> expected, <span class="type">int</span> x)</span>;</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="4-7-CAS与Synchronized"><a href="#4-7-CAS与Synchronized" class="headerlink" title="4.7 CAS与Synchronized"></a>4.7 CAS与Synchronized</h2><p>CAS和Synchronized都可以保证多线程环境下共享数据的安全性。那么他们两者有什么区别？</p><p>Synchronized是从悲观的角度出发：</p><p>总是假设最坏的情况，每次去拿数据的时候都认为别人会修改，所以每次在拿数据的时候都会上锁，这样别人想拿这个数据就会阻塞直到它拿到锁（<strong>共享资源每次只给一个线程使用，其它线</strong></p><p><strong>程阻塞，用完后再把资源转让给其它线程</strong>）。因此Synchronized我们也将其称之为悲观锁。jdk中的ReentrantLock也是一种悲观锁。</p><p>CAS是从乐观的角度出发:</p><p>总是假设最好的情况，每次去拿数据的时候都认为别人不会修改，所以不会上锁，但是在更新的时候会判断一下在此期间别人有没有去更新这个数据。CAS这种机制我们也可以将其称之为乐观锁。</p><h1 id="5-并发工具类"><a href="#5-并发工具类" class="headerlink" title="5 并发工具类"></a>5 并发工具类</h1><p>在JDK的并发包里提供了几个非常有用的并发容器和并发工具类。供我们在多线程开发中进行使用。</p><h2 id="5-1-ConcurrentHashMap"><a href="#5-1-ConcurrentHashMap" class="headerlink" title="5.1 ConcurrentHashMap"></a>5.1 ConcurrentHashMap</h2><h3 id="5-1-1-概述以及基本使用"><a href="#5-1-1-概述以及基本使用" class="headerlink" title="5.1.1 概述以及基本使用"></a>5.1.1 概述以及基本使用</h3><p>在集合类中HashMap是比较常用的集合对象，但是HashMap是线程不安全的(多线程环境下可能会存在问题)。为了保证数据的安全性我们可以使用Hashtable，但是Hashtable的效率低下。</p><p>基于以上两个原因我们可以使用JDK1.5以后所提供的ConcurrentHashMap。</p><p><font color="blue" size="3"><strong>案例1</strong></font>：演示HashMap线程不安全</p><p>实现步骤</p><ol><li>创建一个HashMap集合对象</li><li>创建两个线程对象，第一个线程对象向集合中添加元素(1-24),第二个线程对象向集合中添加元素(25-50);</li><li>主线程休眠1秒，以便让其他两个线程将数据填装完毕</li><li>从集合中找出键和值不相同的数据</li></ol><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HashMapDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建一个HashMap集合对象</span></span><br><span class="line">        HashMap&lt;String , String&gt; hashMap = <span class="keyword">new</span> <span class="title class_">HashMap</span>&lt;String , String&gt;() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建两个线程对象,我们本次使用匿名内部类的方式去常见线程对象</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 第一个线程对象向集合中添加元素(1-24)</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">1</span> ; x &lt; <span class="number">25</span> ; x++) &#123;</span><br><span class="line">                    hashMap.put(String.valueOf(x) , String.valueOf(x)) ;</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">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 线程t2</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 第二个线程对象向集合中添加元素(25-50)</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">25</span> ; x &lt; <span class="number">51</span> ; x++) &#123;</span><br><span class="line">                    hashMap.put(String.valueOf(x) , String.valueOf(x)) ;</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">        &#125;;</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.start();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;----------------------------------------------------------&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 主线程休眠2s，以便让其他两个线程将数据填装完毕</span></span><br><span class="line">            TimeUnit.SECONDS.sleep(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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="comment">// 从集合中找出键和值不相同的数据</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">1</span> ; x &lt; <span class="number">51</span> ; x++) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// HashMap中的键就是当前循环变量的x这个数据的字符串表现形式 ， 根据键找到值，然后在进行判断</span></span><br><span class="line">            <span class="keyword">if</span>( !String.valueOf(x).equals( hashMap.get(String.valueOf(x)) ) ) &#123;</span><br><span class="line">                System.out.println(String.valueOf(x) + <span class="string">&quot;:&quot;</span> + hashMap.get(String.valueOf(x)));</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><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><figure class="highlight java"><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><br><span class="line"><span class="number">5</span>:<span class="literal">null</span></span><br></pre></td></tr></table></figure><p>通过控制台的输出结果，我们可以看到在多线程操作HashMap的时候，可能会出现线程安全问题。</p><blockquote><p>注1：需要多次运行才可以看到具体的效果; 可以使用循环将代码进行改造，以便让问题方便的暴露出来！</p></blockquote><p><font color="blue" size="3"><strong>案例2</strong></font>：演示Hashtable线程安全</p><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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">HashtableDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建一个Hashtable集合对象</span></span><br><span class="line">        Hashtable&lt;String , String&gt; hashtable = <span class="keyword">new</span> <span class="title class_">Hashtable</span>&lt;String , String&gt;() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建两个线程对象,我们本次使用匿名内部类的方式去常见线程对象</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 第一个线程对象向集合中添加元素(1-24)</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">1</span> ; x &lt; <span class="number">25</span> ; x++) &#123;</span><br><span class="line">                    hashtable.put(String.valueOf(x) , String.valueOf(x)) ;</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">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 线程t2</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 第二个线程对象向集合中添加元素(25-50)</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">25</span> ; x &lt; <span class="number">51</span> ; x++) &#123;</span><br><span class="line">                    hashtable.put(String.valueOf(x) , String.valueOf(x)) ;</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">        &#125;;</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.start();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;----------------------------------------------------------&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 主线程休眠2s，以便让其他两个线程将数据填装完毕</span></span><br><span class="line">            TimeUnit.SECONDS.sleep(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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="comment">// 从集合中找出键和值不相同的数据</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">1</span> ; x &lt; <span class="number">51</span> ; x++) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Hashtable中的键就是当前循环变量的x这个数据的字符串表现形式 ， 根据键找到值，然后在进行判断</span></span><br><span class="line">            <span class="keyword">if</span>( !String.valueOf(x).equals( hashtable.get(String.valueOf(x)) ) ) &#123;</span><br><span class="line">                System.out.println(String.valueOf(x) + <span class="string">&quot;:&quot;</span> + hashtable.get(String.valueOf(x)));</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">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不论该程序运行多少次，都不会产生数据问题。因此也就证明Hashtable是线程安全的。</p><p><font color="blue" size="3"><strong>Hashtable保证线程安全的原理</strong></font>：</p><p>查看Hashtable的源码</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">Hashtable</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">Dictionary</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">Map</span>&lt;K,V&gt;, Cloneable, java.io.Serializable &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Entry数组，一个Entry就相当于一个元素</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">transient</span> Entry&lt;?,?&gt;[] table;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Entry类的定义</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Entry</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">Map</span>.Entry&lt;K,V&gt; &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> hash;<span class="comment">// 当前key的hash码值</span></span><br><span class="line">        <span class="keyword">final</span> K key;<span class="comment">// 键</span></span><br><span class="line">        V value;<span class="comment">// 值</span></span><br><span class="line">        Entry&lt;K,V&gt; next;<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="keyword">public</span> <span class="keyword">synchronized</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span>&#123;...&#125;</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="keyword">synchronized</span> V <span class="title function_">get</span><span class="params">(Object key)</span>&#123;...&#125;</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="keyword">synchronized</span> <span class="type">int</span> <span class="title function_">size</span><span class="params">()</span>&#123;...&#125;</span><br><span class="line">    </span><br><span class="line">    ...</span><br><span class="line">    </span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>对应的结构如下图所示</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571905221097.png" alt="1571905221097"> </p><p>Hashtable保证线程安全性的是使用方法全局锁进行实现的。在线程竞争激烈的情况下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法，其他线程也访问HashTable</p><p>的同步方法时，会进入阻塞状态。如线程1使用put进行元素添加，线程2不但不能使用put方法添加元素，也不能使用get方法来获取元素，所以竞争越激烈效率越低。</p><p><font color="blue" size="3"><strong>案例3</strong></font>：演示ConcurrentHashMap线程安全</p><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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentHashMapDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建一个ConcurrentHashMap集合对象</span></span><br><span class="line">        ConcurrentHashMap&lt;String , String&gt; concurrentHashMap = <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;String , String&gt;() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建两个线程对象,我们本次使用匿名内部类的方式去常见线程对象</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 第一个线程对象向集合中添加元素(1-24)</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">1</span> ; x &lt; <span class="number">25</span> ; x++) &#123;</span><br><span class="line">                    concurrentHashMap.put(String.valueOf(x) , String.valueOf(x)) ;</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">        &#125;;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 线程t2</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 第二个线程对象向集合中添加元素(25-50)</span></span><br><span class="line">                <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">25</span> ; x &lt; <span class="number">51</span> ; x++) &#123;</span><br><span class="line">                    concurrentHashMap.put(String.valueOf(x) , String.valueOf(x)) ;</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">        &#125;;</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.start();</span><br><span class="line"></span><br><span class="line">        System.out.println(<span class="string">&quot;----------------------------------------------------------&quot;</span>);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 主线程休眠2s，以便让其他两个线程将数据填装完毕</span></span><br><span class="line">            TimeUnit.SECONDS.sleep(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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="comment">// 从集合中找出键和值不相同的数据</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">1</span> ; x &lt; <span class="number">51</span> ; x++) &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// concurrentHashMap中的键就是当前循环变量的x这个数据的字符串表现形式 ， 根据键找到值，然后在进行判断</span></span><br><span class="line">            <span class="keyword">if</span>( !String.valueOf(x).equals( concurrentHashMap.get(String.valueOf(x)) ) ) &#123;</span><br><span class="line">                System.out.println(String.valueOf(x) + <span class="string">&quot;:&quot;</span> + concurrentHashMap.get(String.valueOf(x)));</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">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>不论该程序运行多少次，都不会产生数据问题。因此也就证明ConcurrentHashMap是线程安全的。</p><h3 id="5-1-2-源码分析"><a href="#5-1-2-源码分析" class="headerlink" title="5.1.2 源码分析"></a>5.1.2 源码分析</h3><p>由于ConcurrentHashMap在jdk1.7和jdk1.8的时候实现原理不太相同，因此需要分别来讲解一下两个不同版本的实现原理。</p><h4 id="1-jdk1-7版本"><a href="#1-jdk1-7版本" class="headerlink" title="1) jdk1.7版本"></a>1) jdk1.7版本</h4><p><font color="blue" size="3"><strong>ConcurrentHashMap中的重要成员变量</strong></font></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentHashMap</span>&lt;K, V&gt; <span class="keyword">extends</span> <span class="title class_">AbstractMap</span>&lt;K, V&gt; <span class="keyword">implements</span> <span class="title class_">ConcurrentMap</span>&lt;K, V&gt;, Serializable &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Segment翻译中文为&quot;段&quot; , 段数组对象</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="keyword">final</span> Segment&lt;K,V&gt;[] segments;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Segment是一种可重入锁（ReentrantLock），在ConcurrentHashMap里扮演锁的角色，将一个大的table分割成多个小的table进行加锁。</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Segment</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">ReentrantLock</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="keyword">transient</span> <span class="keyword">volatile</span> <span class="type">int</span> count;    <span class="comment">// Segment中元素的数量，由volatile修饰，支持内存可见性；</span></span><br><span class="line">        <span class="keyword">transient</span> <span class="type">int</span> modCount; <span class="comment">// 对table的大小造成影响的操作的数量（比如put或者remove操作）;</span></span><br><span class="line">        <span class="keyword">transient</span> <span class="type">int</span> threshold; <span class="comment">// 扩容阈值;</span></span><br><span class="line">        <span class="keyword">transient</span> <span class="keyword">volatile</span> HashEntry&lt;K,V&gt;[] table;  <span class="comment">// 链表数组，数组中的每一个元素代表了一个链表的头部;</span></span><br><span class="line">        <span class="keyword">final</span> <span class="type">float</span> loadFactor; <span class="comment">// 负载因子 </span></span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Segment中的元素是以HashEntry的形式存放在数组中的，其结构与普通HashMap的HashEntry基本一致，不同的是Segment的HashEntry，其value由     // volatile修饰，以支持内存可见性，即写操作对其他读线程即时可见。</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">HashEntry</span>&lt;K,V&gt; &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> hash;<span class="comment">// 当前节点key对应的哈希码值</span></span><br><span class="line">        <span class="keyword">final</span> K key;<span class="comment">// 存储键</span></span><br><span class="line">        <span class="keyword">volatile</span> V value;<span class="comment">// 存储值</span></span><br><span class="line">        <span class="keyword">volatile</span> HashEntry&lt;K,V&gt; next;<span class="comment">// 下一个节点</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>对应的结构如下图所示</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571880094854.png" alt="1571880094854">  </p><p>简单来讲，就是ConcurrentHashMap比HashMap多了一次hash过程，第1次hash定位到Segment，第2次hash定位到HashEntry，然后链表搜索找到指定节点。在进行写操作时，只需锁住写</p><p>元素所在的Segment即可(这种锁被称为<font size="3" color="red"><strong>分段锁</strong></font>)，其他Segment无需加锁，从而产生锁竞争的概率大大减小，提高了并发读写的效率。该种实现方式的缺点是hash过程比普通的HashMap要长</p><p>(因为需要进行两次hash操作)。</p><p><font color="blue" size="3"><strong>ConcurrentHashMap的put方法源码分析</strong></font></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentHashMap</span>&lt;K, V&gt; <span class="keyword">extends</span> <span class="title class_">AbstractMap</span>&lt;K, V&gt; <span class="keyword">implements</span> <span class="title class_">ConcurrentMap</span>&lt;K, V&gt;, Serializable &#123; </span><br><span class="line">    </span><br><span class="line">    <span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 定义一个Segment对象</span></span><br><span class="line">        Segment&lt;K,V&gt; s;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 如果value的值为空，那么抛出异常</span></span><br><span class="line">        <span class="keyword">if</span> (value == <span class="literal">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// hash函数获取key的hashCode，然后做了一些处理</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> hash(key);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 通过key的hashCode定位segment</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">j</span> <span class="operator">=</span> (hash &gt;&gt;&gt; segmentShift) &amp; segmentMask;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 对定位的Segment进行判断，如果Segment为空，调用ensureSegment进行初始化操作(第一次hash定位)</span></span><br><span class="line">        <span class="keyword">if</span> ((s = (Segment&lt;K,V&gt;)UNSAFE.getObject(segments, (j &lt;&lt; SSHIFT) + SBASE)) == <span class="literal">null</span>) </span><br><span class="line">            s = ensureSegment(j);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 调用Segment对象的put方法添加元素</span></span><br><span class="line">        <span class="keyword">return</span> s.put(key, hash, value, <span class="literal">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Segment是一种可ReentrantLock，在ConcurrentHashMap里扮演锁的角色，将一个大的table分割成多个小的table进行加锁。</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">Segment</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">ReentrantLock</span> <span class="keyword">implements</span> <span class="title class_">Serializable</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 添加元素</span></span><br><span class="line">        <span class="keyword">final</span> V <span class="title function_">put</span><span class="params">(K key, <span class="type">int</span> hash, V value, <span class="type">boolean</span> onlyIfAbsent)</span> &#123;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 尝试对该段进行加锁,如果加锁失败，则调用scanAndLockForPut方法;在该方法中就要进行再次尝试或者进行自旋等待</span></span><br><span class="line">            HashEntry&lt;K,V&gt; node = tryLock() ? <span class="literal">null</span> : scanAndLockForPut(key, hash, value);</span><br><span class="line">            V oldValue;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 获取HashEntry数组对象</span></span><br><span class="line">                HashEntry&lt;K,V&gt;[] tab = table;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 根据key的hashCode值计算索引(第二次hash定位)</span></span><br><span class="line">                <span class="type">int</span> <span class="variable">index</span> <span class="operator">=</span> (tab.length - <span class="number">1</span>) &amp; hash;</span><br><span class="line">                HashEntry&lt;K,V&gt; first = entryAt(tab, index);</span><br><span class="line">                <span class="keyword">for</span> (HashEntry&lt;K,V&gt; e = first;;) </span><br><span class="line">                    </span><br><span class="line">                    <span class="comment">// 若不为null</span></span><br><span class="line">                    <span class="keyword">if</span> (e != <span class="literal">null</span>) &#123;</span><br><span class="line">                        K k;</span><br><span class="line">                        </span><br><span class="line">                        <span class="comment">// 判读当前节点的key是否和链表头节点的key相同(依赖于hashCode方法和equals方法) </span></span><br><span class="line">                        <span class="comment">// 如果相同，值进行更新</span></span><br><span class="line">                        <span class="keyword">if</span> ((k = e.key) == key || (e.hash == hash &amp;&amp; key.equals(k))) &#123;</span><br><span class="line">                            oldValue = e.value;</span><br><span class="line">                            <span class="keyword">if</span> (!onlyIfAbsent) &#123;</span><br><span class="line">                                e.value = value;</span><br><span class="line">                                ++modCount;</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">break</span>;</span><br><span class="line">                        &#125;</span><br><span class="line">                        </span><br><span class="line">                        e = e.next;</span><br><span class="line">                    &#125; <span class="keyword">else</span> &#123;  <span class="comment">// 若头结点为null</span></span><br><span class="line">                        </span><br><span class="line">                        <span class="comment">// 将新节点添加到链表中</span></span><br><span class="line">                        <span class="keyword">if</span> (node != <span class="literal">null</span>) </span><br><span class="line">                            node.setNext(first);</span><br><span class="line">                        <span class="keyword">else</span></span><br><span class="line">                            node = <span class="keyword">new</span> <span class="title class_">HashEntry</span>&lt;K,V&gt;(hash, key, value, first);</span><br><span class="line">                        <span class="type">int</span> <span class="variable">c</span> <span class="operator">=</span> count + <span class="number">1</span>;</span><br><span class="line">                        </span><br><span class="line">                        <span class="comment">// 如果超过阈值，则进行rehash操作</span></span><br><span class="line">                        <span class="keyword">if</span> (c &gt; threshold &amp;&amp; tab.length &lt; MAXIMUM_CAPACITY)</span><br><span class="line">                            rehash(node);</span><br><span class="line">                        <span class="keyword">else</span></span><br><span class="line">                            setEntryAt(tab, index, node);</span><br><span class="line">                        ++modCount;</span><br><span class="line">                        count = c;</span><br><span class="line">                        oldValue = <span class="literal">null</span>;</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 class="keyword">finally</span> &#123;</span><br><span class="line">                unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        </span><br><span class="line">            <span class="keyword">return</span> oldValue;</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">&#125;</span><br><span class="line"></span><br></pre></td></tr></table></figure><blockquote><p>注：源代码进行简单讲解即可(核心：进行了两次哈希定位以及加锁过程)</p></blockquote><h4 id="2-jdk1-8版本"><a href="#2-jdk1-8版本" class="headerlink" title="2) jdk1.8版本"></a>2) jdk1.8版本</h4><p>在JDK1.8中为了进一步优化ConcurrentHashMap的性能，去掉了Segment分段锁的设计。在数据结构方面，则是跟HashMap一样，使用一个哈希表table数组。(数组 + 链表 + 红黑树) </p><p>而线程安全方面是结合CAS机制 + 局部锁实现的，减低锁的粒度，提高性能。同时在HashMap的基础上，对哈希表table数组和链表节点的value，next指针等使用volatile来修饰，从而</p><p>实现线程可见性。</p><p><font color="blue" size="3"><strong>ConcurrentHashMap中的重要成员变量</strong></font></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentHashMap</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">AbstractMap</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">ConcurrentMap</span>&lt;K,V&gt;, Serializable &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Node数组</span></span><br><span class="line">    <span class="keyword">transient</span> <span class="keyword">volatile</span> Node&lt;K,V&gt;[] table;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// Node类的定义</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">class</span> <span class="title class_">Node</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">Map</span>.Entry&lt;K,V&gt; &#123; </span><br><span class="line">        </span><br><span class="line">        <span class="keyword">final</span> <span class="type">int</span> hash;<span class="comment">// 当前key的hashCode值</span></span><br><span class="line">        <span class="keyword">final</span> K key;<span class="comment">// 键</span></span><br><span class="line">        <span class="keyword">volatile</span> V val;<span class="comment">// 值</span></span><br><span class="line">        <span class="keyword">volatile</span> Node&lt;K,V&gt; next;<span class="comment">// 下一个节点</span></span><br><span class="line">        </span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// TreeNode类的定义</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">class</span> <span class="title class_">TreeNode</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">Node</span>&lt;K,V&gt; &#123;</span><br><span class="line">        TreeNode&lt;K,V&gt; parent;  <span class="comment">// 父节点</span></span><br><span class="line">        TreeNode&lt;K,V&gt; left;   <span class="comment">// 左子节点</span></span><br><span class="line">        TreeNode&lt;K,V&gt; right;   <span class="comment">// 右子节点</span></span><br><span class="line">        TreeNode&lt;K,V&gt; prev;    <span class="comment">// needed to unlink next upon deletion</span></span><br><span class="line">        <span class="type">boolean</span> red;   <span class="comment">// 节点的颜色状态</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>对应的结构如下图</p><p><img src="https://img.wotemo.com/img/multithreading-assets/1571901607504.png" alt="1571901607504"> </p><p><font color="blue" size="3"><strong>ConcurrentHashMap的put方法源码分析</strong></font></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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ConcurrentHashMap</span>&lt;K,V&gt; <span class="keyword">extends</span> <span class="title class_">AbstractMap</span>&lt;K,V&gt; <span class="keyword">implements</span> <span class="title class_">ConcurrentMap</span>&lt;K,V&gt;, Serializable &#123;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// 添加元素</span></span><br><span class="line">    <span class="keyword">public</span> V <span class="title function_">put</span><span class="params">(K key, V value)</span> &#123;</span><br><span class="line">    <span class="keyword">return</span> putVal(key, value, <span class="literal">false</span>);</span><br><span class="line">&#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// putVal方法定义</span></span><br><span class="line">    <span class="keyword">final</span> V <span class="title function_">putVal</span><span class="params">(K key, V value, <span class="type">boolean</span> onlyIfAbsent)</span> &#123;</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// key为null直接抛出异常</span></span><br><span class="line">        <span class="keyword">if</span> (key == <span class="literal">null</span> || value == <span class="literal">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">NullPointerException</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 计算key所对应的hashCode值</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">hash</span> <span class="operator">=</span> spread(key.hashCode());</span><br><span class="line">        <span class="type">int</span> <span class="variable">binCount</span> <span class="operator">=</span> <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (Node&lt;K,V&gt;[] tab = table;;) &#123;</span><br><span class="line">            Node&lt;K,V&gt; f; <span class="type">int</span> n, i, fh;</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 哈希表如果不存在，那么此时初始化哈希表</span></span><br><span class="line">            <span class="keyword">if</span> (tab == <span class="literal">null</span> || (n = tab.length) == <span class="number">0</span>)</span><br><span class="line">                tab = initTable();</span><br><span class="line">            </span><br><span class="line">            <span class="comment">// 通过hash值计算key在table表中的索引，将其值赋值给变量i,然后根据索引找到对应的Node，如果Node为null,做出处理</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((f = tabAt(tab, i = (n - <span class="number">1</span>) &amp; hash)) == <span class="literal">null</span>) &#123;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// 新增链表头结点，cas方式添加到哈希表table</span></span><br><span class="line">                <span class="keyword">if</span> (casTabAt(tab, i, <span class="literal">null</span>, <span class="keyword">new</span> <span class="title class_">Node</span>&lt;K,V&gt;(hash, key, value, <span class="literal">null</span>))) <span class="keyword">break</span>;                   </span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((fh = f.hash) == MOVED)</span><br><span class="line">                tab = helpTransfer(tab, f);</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="type">V</span> <span class="variable">oldVal</span> <span class="operator">=</span> <span class="literal">null</span>;</span><br><span class="line">                </span><br><span class="line">                <span class="comment">// f为链表头结点，使用synchronized加锁</span></span><br><span class="line">                <span class="keyword">synchronized</span> (f) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (tabAt(tab, i) == f) &#123;</span><br><span class="line">                        <span class="keyword">if</span> (fh &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                            binCount = <span class="number">1</span>;</span><br><span class="line">                            <span class="keyword">for</span> (Node&lt;K,V&gt; e = f;; ++binCount) &#123;</span><br><span class="line">                                K ek;</span><br><span class="line">                                </span><br><span class="line">                                <span class="comment">// 节点已经存在，更新value即可</span></span><br><span class="line">                                <span class="keyword">if</span> (e.hash == hash &amp;&amp; ((ek = e.key) == key || (ek != <span class="literal">null</span> &amp;&amp; key.equals(ek)))) &#123;</span><br><span class="line">                                    oldVal = e.val;</span><br><span class="line">                                    <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line">                                        e.val = value;</span><br><span class="line">                                    <span class="keyword">break</span>;</span><br><span class="line">                                &#125;</span><br><span class="line">                                </span><br><span class="line">                                <span class="comment">// 该key对应的节点不存在,则新增节点并添加到该链表的末尾</span></span><br><span class="line">                                Node&lt;K,V&gt; pred = e;</span><br><span class="line">                                <span class="keyword">if</span> ((e = e.next) == <span class="literal">null</span>) &#123;</span><br><span class="line">                                    pred.next = <span class="keyword">new</span> <span class="title class_">Node</span>&lt;K,V&gt;(hash, key, value, <span class="literal">null</span>);</span><br><span class="line">                                    <span class="keyword">break</span>;</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">                        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (f <span class="keyword">instanceof</span> TreeBin) &#123; <span class="comment">// 红黑树节点，则往该红黑树更新或添加该节点即可</span></span><br><span class="line">                            Node&lt;K,V&gt; p;</span><br><span class="line">                            binCount = <span class="number">2</span>;</span><br><span class="line">                            <span class="keyword">if</span> ((p = ((TreeBin&lt;K,V&gt;)f).putTreeVal(hash, key, value)) != <span class="literal">null</span>) &#123;</span><br><span class="line">                                oldVal = p.val;</span><br><span class="line">                                <span class="keyword">if</span> (!onlyIfAbsent)</span><br><span class="line">                                    p.val = value;</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">                </span><br><span class="line">                <span class="comment">// 判断是否需要将链表转为红黑树</span></span><br><span class="line">                <span class="keyword">if</span> (binCount != <span class="number">0</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (binCount &gt;= TREEIFY_THRESHOLD)</span><br><span class="line">                        treeifyBin(tab, i);</span><br><span class="line">                    <span class="keyword">if</span> (oldVal != <span class="literal">null</span>)</span><br><span class="line">                        <span class="keyword">return</span> oldVal;</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><span class="line">        addCount(<span class="number">1L</span>, binCount);</span><br><span class="line">        <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// CAS算法的核心类</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> sun.misc.Unsafe U;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            U = sun.misc.Unsafe.getUnsafe();</span><br><span class="line">            ...</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> <span class="title class_">Error</span>(e);</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">static</span> <span class="keyword">final</span> &lt;K,V&gt; Node&lt;K,V&gt; <span class="title function_">tabAt</span><span class="params">(Node&lt;K,V&gt;[] tab, <span class="type">int</span> i)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> (Node&lt;K,V&gt;)U.getObjectVolatile(tab, ((<span class="type">long</span>)i &lt;&lt; ASHIFT) + ABASE);</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="comment">// CAS更新或新增链表节点</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> &lt;K,V&gt; <span class="type">boolean</span> <span class="title function_">casTabAt</span><span class="params">(Node&lt;K,V&gt;[] tab, <span class="type">int</span> i, Node&lt;K,V&gt; c, Node&lt;K,V&gt; v)</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> U.compareAndSwapObject(tab, ((<span class="type">long</span>)i &lt;&lt; ASHIFT) + ABASE, c, v);</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><ol><li><p>如果当前需要put的key对应的链表在哈希表table中还不存在，即还没添加过该key的hash值对应的链表，则调用casTabAt方法，基于CAS机制来实现添加该链表头结点到哈希表</p><p>table中，避免该线程在添加该链表头结的时候，其他线程也在添加的并发问题；如果CAS失败，则进行自旋，通过继续第2步的操作；</p></li><li><p>如果需要添加的链表已经存在哈希表table中，则通过tabAt方法，基于volatile机制，获取当前最新的链表头结点f，由于f指向的是ConcurrentHashMap的哈希表table的某条</p><p>链表的头结点，故虽然f是临时变量，由于是引用共享的该链表头结点，所以可以使用synchronized关键字来同步多个线程对该链表的访问。在synchronized(f)同步块里面则是与</p><p>HashMap一样遍历该链表，如果该key对应的链表节点已经存在，则更新，否则在链表的末尾新增该key对应的链表节点。</p></li></ol><h2 id="5-2-CountDownLatch"><a href="#5-2-CountDownLatch" class="headerlink" title="5.2 CountDownLatch"></a>5.2 CountDownLatch</h2><p>CountDownLatch允许一个或多个线程等待其他线程完成操作以后，再执行当前线程；比如我们在主线程需要开启2个其他线程，当其他的线程执行完毕以后我们再去执行主线程，针对这</p><p>个需求我们就可以使用CountDownLatch来进行实现。CountDownLatch中count down是倒着数数的意思；CountDownLatch是通过一个计数器来实现的，每当一个线程完成了自己的</p><p>任务后，可以调用countDown()方法让计数器-1，当计数器到达0时，调用CountDownLatch的await()方法的线程阻塞状态解除，继续执行。</p><p>CountDownLatch的相关方法</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">CountDownLatch</span><span class="params">(<span class="type">int</span> count)</span><span class="comment">// 初始化一个指定计数器的CountDownLatch对象</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">await</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException<span class="comment">// 让当前线程等待</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">countDown</span><span class="params">()</span><span class="comment">// 计数器进行减1</span></span><br></pre></td></tr></table></figure><p>案例演示：使用CountDownLatch完成上述需求(我们在主线程需要开启2个其他线程，当其他的线程执行完毕以后我们再去执行主线程)</p><p>实现思路：在main方法中创建一个CountDownLatch对象，把这个对象作为作为参数传递给其他的两个任务线程</p><p>线程任务类1</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CountDownLatchThread01</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// CountDownLatch类型成员变量</span></span><br><span class="line">    <span class="keyword">private</span> CountDownLatch countDownLatch ;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CountDownLatchThread01</span><span class="params">(CountDownLatch countDownLatch)</span> &#123;      <span class="comment">// 构造方法的作用：接收CountDownLatch对象</span></span><br><span class="line">        <span class="built_in">this</span>.countDownLatch = countDownLatch ;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">10000</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;10秒以后执行了CountDownLatchThread01......&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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="comment">// 调用CountDownLatch对象的countDown方法对计数器进行-1操作</span></span><br><span class="line">        countDownLatch.countDown();</span><br><span class="line"></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>线程任务类2</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CountDownLatchThread02</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// CountDownLatch类型成员变量</span></span><br><span class="line">    <span class="keyword">private</span> CountDownLatch countDownLatch ;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">CountDownLatchThread02</span><span class="params">(CountDownLatch countDownLatch)</span> &#123;      <span class="comment">// 构造方法的作用：接收CountDownLatch对象</span></span><br><span class="line">        <span class="built_in">this</span>.countDownLatch = countDownLatch ;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">3000</span>);</span><br><span class="line">            System.out.println(<span class="string">&quot;3秒以后执行了CountDownLatchThread02......&quot;</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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="comment">// 调用CountDownLatch对象的countDown方法对计数器进行-1操作</span></span><br><span class="line">        countDownLatch.countDown();</span><br><span class="line"></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><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">public</span> <span class="keyword">class</span> <span class="title class_">CountDownLatchDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//  1. 创建一个CountDownLatch对象</span></span><br><span class="line">        <span class="type">CountDownLatch</span> <span class="variable">countDownLatch</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatch</span>(<span class="number">2</span>) ;                 <span class="comment">// CountDownLatch中的计数器的默认值就是2</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">//  2. 创建线程任务类对象，并且把这个CountDownLatch对象作为构造方法的参数进行传递</span></span><br><span class="line">        <span class="type">CountDownLatchThread01</span> <span class="variable">countDownLatchThread01</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatchThread01</span>(countDownLatch) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//  3. 创建线程任务类对象，并且把这个CountDownLatch对象作为构造方法的参数进行传递</span></span><br><span class="line">        <span class="type">CountDownLatchThread02</span> <span class="variable">countDownLatchThread02</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CountDownLatchThread02</span>(countDownLatch) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">//  4. 创建线程对象，并启动线程</span></span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(countDownLatchThread01);</span><br><span class="line">        <span class="type">Thread</span> <span class="variable">t2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Thread</span>(countDownLatchThread02);</span><br><span class="line">        t1.start();</span><br><span class="line">        t2.start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">//  5. 在主线程中调用 CountDownLatch中的await让主线程处于阻塞状态</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            countDownLatch.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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="comment">//  6. 程序结束的输出</span></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>控制台输出结果</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></pre></td><td class="code"><pre><span class="line"><span class="number">3</span>秒以后执行了CountDownLatchThread02......</span><br><span class="line"><span class="number">10</span>秒以后执行了CountDownLatchThread01......</span><br><span class="line">主线程执行了.... 程序结束了......</span><br></pre></td></tr></table></figure><p>CountDownLatchThread02线程先执行完毕，此时计数器-1；CountDownLatchThread01线程执行完毕，此时计数器-1；当计数器的值为0的时候，主线程阻塞状态接触，主线程向下执行。</p><h2 id="5-3-CyclicBarrier"><a href="#5-3-CyclicBarrier" class="headerlink" title="5.3 CyclicBarrier"></a>5.3 CyclicBarrier</h2><h3 id="5-3-1-概述以及基本使用"><a href="#5-3-1-概述以及基本使用" class="headerlink" title="5.3.1 概述以及基本使用"></a>5.3.1 概述以及基本使用</h3><p>CyclicBarrier的字面意思是可循环使用（Cyclic）的屏障（Barrier）。它要做的事情是，让一组线程到达一个屏障（也可以叫同步点）时被阻塞，直到最后一个线程到达屏障时，屏障</p><p>才会开门，所有被屏障拦截的线程才会继续运行。</p><p>例如：公司召集5名员工开会，等5名员工都到了，会议开始。我们创建5个员工线程，1个开会线程，几乎同时启动，使用CyclicBarrier保证5名员工线程全部执行后，再执行开会线程。</p><p>CyclicBarrier的相关方法</p><figure class="highlight java"><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="keyword">public</span> <span class="title function_">CyclicBarrier</span><span class="params">(<span class="type">int</span> parties, Runnable barrierAction)</span>   <span class="comment">// 用于在线程到达屏障时，优先执行barrierAction，方便处理更复杂的业务场景</span></span><br><span class="line"><span class="keyword">public</span> <span class="type">int</span> <span class="title function_">await</span><span class="params">()</span><span class="comment">// 每个线程调用await方法告诉CyclicBarrier我已经到达了屏障，然后当前线程被阻塞</span></span><br></pre></td></tr></table></figure><p>案例演示：模拟员工开会</p><p>实现步骤：</p><ol><li>创建一个员工线程类(EmployeeThread),该线程类中需要定义一个CyclicBarrier类型的形式参数</li><li>创建一个开会线程类(MettingThread)</li><li>测试类<ol><li>创建CyclicBarrier对象</li><li>创建5个EmployeeThread线程对象，把第一步创建的CyclicBarrier对象作为构造方法参数传递过来</li><li>启动5个员工线程</li></ol></li></ol><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">EmployeeThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// CyclicBarrier类型的成员变量</span></span><br><span class="line">    <span class="keyword">private</span> CyclicBarrier cyclicBarrier ;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">EmployeeThread</span><span class="params">(CyclicBarrier cyclicBarrier)</span> &#123;        <span class="comment">// 使用构造方法对CyclicBarrier进行初始化</span></span><br><span class="line">        <span class="built_in">this</span>.cyclicBarrier = cyclicBarrier ;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟开会人员的随机到场</span></span><br><span class="line">            Thread.sleep((<span class="type">int</span>) (Math.random() * <span class="number">1000</span>));</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot; 到了! &quot;</span>);</span><br><span class="line">            cyclicBarrier.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#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">&#125;</span><br></pre></td></tr></table></figure><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><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">public</span> <span class="keyword">class</span> <span class="title class_">MettingThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</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>测试类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CyclicBarrierDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建CyclicBarrier对象</span></span><br><span class="line">        <span class="type">CyclicBarrier</span> <span class="variable">cyclicBarrier</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrier</span>(<span class="number">5</span> , <span class="keyword">new</span> <span class="title class_">MettingThread</span>()) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建5个EmployeeThread线程对象，把第一步创建的CyclicBarrier对象作为构造方法参数传递过来</span></span><br><span class="line">        <span class="type">EmployeeThread</span> <span class="variable">thread1</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">EmployeeThread</span>(cyclicBarrier) ;</span><br><span class="line">        <span class="type">EmployeeThread</span> <span class="variable">thread2</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">EmployeeThread</span>(cyclicBarrier) ;</span><br><span class="line">        <span class="type">EmployeeThread</span> <span class="variable">thread3</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">EmployeeThread</span>(cyclicBarrier) ;</span><br><span class="line">        <span class="type">EmployeeThread</span> <span class="variable">thread4</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">EmployeeThread</span>(cyclicBarrier) ;</span><br><span class="line">        <span class="type">EmployeeThread</span> <span class="variable">thread5</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">EmployeeThread</span>(cyclicBarrier) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 启动5个员工线程</span></span><br><span class="line">        thread1.start();</span><br><span class="line">        thread2.start();</span><br><span class="line">        thread3.start();</span><br><span class="line">        thread4.start();</span><br><span class="line">        thread5.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></pre></td></tr></table></figure><h3 id="5-3-2-使用场景"><a href="#5-3-2-使用场景" class="headerlink" title="5.3.2 使用场景"></a>5.3.2 使用场景</h3><p>使用场景：CyclicBarrier可以用于多线程计算数据，最后合并计算结果的场景。</p><p>比如：现在存在两个文件，这个两个文件中存储的是某一个员工两年的工资信息(一年一个文件)，现需要对这两个文件中的数据进行汇总；使用两个线程读取2个文件中的数据，当两个文</p><p>件中的数据都读取完毕以后，进行数据的汇总操作。</p><p>分析：要想在两个线程读取数据完毕以后进行数据的汇总，那么我们就需要定义一个任务类(该类需要实现Runnable接口)；两个线程读取完数据以后再进行数据的汇总，那么我们可以将</p><p>​ 两个线程读取到的数据先存储到一个集合中，而多线程环境下最常见的线程集合类就是ConcurrentHashMap，而这个集合需要被两个线程都可以进行使用，那么我们可以将这个集</p><p>​ 合作为我们任务类的成员变量，然后我们在这个任务类中去定义一个CyclicBarrier对象，然后在定义一个方法(count)，当调用这个count方法的时候需要去开启两个线程对象，</p><p>​ 使用这两个线程对象读取数据，把读取到的数据存储到ConcurrentHashMap对象，当一个线程读取数据完毕以后，调用CyclicBarrier的awit方法(告诉CyclicBarrier我已经</p><p>​ 到达了屏障)，然后在任务类的run方法对ConcurrentHashMap的数据进行汇总操作；</p><p>实现步骤:</p><ol><li>定义一个任务类CyclicBarrierThreadUse(实现了Runnable接口)</li><li>定义成员变量：CyclicBarrier ，ConcurrentHashMap</li></ol><figure class="highlight java"><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="keyword">private</span> <span class="type">CyclicBarrier</span> <span class="variable">cyclicBarrier</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrier</span>(<span class="number">2</span> , <span class="built_in">this</span>) ;</span><br><span class="line"><span class="keyword">private</span> ConcurrentHashMap&lt;Integer , String&gt; concurrentHashMap = <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;Integer , String&gt;() ;</span><br></pre></td></tr></table></figure><ol start="3"><li>定义一个方法count方法，在count方法中开启两个线程对象(可以使用匿名内部类的方式实现)</li><li>在run方法中对ConcurrentHashMap中的数据进行汇总</li><li>编写测试类CyclicBarrierThreadUseDemo</li><li>创建CyclicBarrierThreadUse对象，调用count方法</li></ol><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">CyclicBarrierThreadUse</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 当前我们两个线程到达了屏障点以后，我们需要立即对数据进行汇总, 因此我们需要使用第二个构造方法</span></span><br><span class="line">    <span class="comment">// 并且我们当前这个类就是一个任务类，因此我们可以直接传递参数this</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">CyclicBarrier</span> <span class="variable">cyclicBarrier</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrier</span>(<span class="number">2</span> , <span class="built_in">this</span>) ;</span><br><span class="line">    <span class="keyword">private</span> ConcurrentHashMap&lt;Integer , String&gt; concurrentHashMap = <span class="keyword">new</span> <span class="title class_">ConcurrentHashMap</span>&lt;Integer , String&gt;() ;  <span class="comment">// 存储两个线程所读取的数据</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">count</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 定义一个方法count方法，在count方法中开启两个线程对象(可以使用匿名内部类的方式实现)</span></span><br><span class="line">        <span class="comment">// 线程1</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 读取数据</span></span><br><span class="line">                <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="literal">null</span> ;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">                    bufferedReader = <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">FileReader</span>(<span class="string">&quot;D:\\salary\\2017-salary.txt&quot;</span>)) ;</span><br><span class="line">                    <span class="type">String</span> <span class="variable">line</span> <span class="operator">=</span> <span class="literal">null</span> ;</span><br><span class="line">                    <span class="keyword">while</span>((line = bufferedReader.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">                        concurrentHashMap.put(Integer.parseInt(line) , <span class="string">&quot;&quot;</span>) ;            <span class="comment">// 小的问题，工资信息不能重复</span></span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span>(bufferedReader != <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">try</span> &#123;</span><br><span class="line">                            bufferedReader.close();</span><br><span class="line">                        &#125; <span class="keyword">catch</span> (IOException e) &#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"></span><br><span class="line">                <span class="comment">// 模拟任务的执行时间</span></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    TimeUnit.SECONDS.sleep(<span class="number">5</span>) ;</span><br><span class="line">                    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;---------------------线程读取数据完毕....&quot;</span>);</span><br><span class="line">                    cyclicBarrier.await() ;         <span class="comment">//通知cyclicBarrier当前线程已经到达了屏障点</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#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><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 线程2</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">                <span class="comment">// 读取数据</span></span><br><span class="line">                <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="literal">null</span> ;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">                    bufferedReader = <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">FileReader</span>(<span class="string">&quot;D:\\salary\\2019-salary.txt&quot;</span>)) ;</span><br><span class="line">                    <span class="type">String</span> <span class="variable">line</span> <span class="operator">=</span> <span class="literal">null</span> ;</span><br><span class="line">                    <span class="keyword">while</span>((line = bufferedReader.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">                        concurrentHashMap.put(Integer.parseInt(line) , <span class="string">&quot;&quot;</span>) ;            <span class="comment">// 小的问题，工资信息不能重复</span></span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">                    e.printStackTrace();</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    <span class="keyword">if</span>(bufferedReader != <span class="literal">null</span>) &#123;</span><br><span class="line">                        <span class="keyword">try</span> &#123;</span><br><span class="line">                            bufferedReader.close();</span><br><span class="line">                        &#125; <span class="keyword">catch</span> (IOException e) &#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"></span><br><span class="line">                <span class="comment">// 模拟任务的执行时间</span></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    TimeUnit.SECONDS.sleep(<span class="number">10</span>) ;</span><br><span class="line">                    System.out.println(Thread.currentThread().getName() + <span class="string">&quot;---------------------线程读取数据完毕....&quot;</span>);</span><br><span class="line">                    cyclicBarrier.await() ;         <span class="comment">//通知cyclicBarrier当前线程已经到达了屏障点</span></span><br><span class="line">                &#125; <span class="keyword">catch</span> (Exception e) &#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><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">        &#125;).start();</span><br><span class="line"></span><br><span class="line"></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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 获取concurrentHashMap中的数据进行汇总</span></span><br><span class="line">        Enumeration&lt;Integer&gt; enumeration = concurrentHashMap.keys();        <span class="comment">// 获取concurrentHashMap中所有的键</span></span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 这个Enumeration的使用和我们之前所学习过的迭代器类似</span></span><br><span class="line"><span class="comment">         * boolean hasMoreElements(); 判断集合中是否存在下一个元素</span></span><br><span class="line"><span class="comment">         * E nextElement();           获取元素</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="type">int</span> <span class="variable">result</span> <span class="operator">=</span> <span class="number">0</span> ;</span><br><span class="line">        <span class="keyword">while</span>(enumeration.hasMoreElements()) &#123;</span><br><span class="line">            <span class="type">Integer</span> <span class="variable">integer</span> <span class="operator">=</span> enumeration.nextElement();</span><br><span class="line">            result += integer ;</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(result);</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">&#125;</span><br></pre></td></tr></table></figure><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><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">public</span> <span class="keyword">class</span> <span class="title class_">CyclicBarrierThreadUseDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建任务类的对象</span></span><br><span class="line">        <span class="type">CyclicBarrierThreadUse</span> <span class="variable">cyclicBarrierThreadUse</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CyclicBarrierThreadUse</span>();</span><br><span class="line">        </span><br><span class="line">        <span class="comment">// 调用count方法进行数据汇总</span></span><br><span class="line">        cyclicBarrierThreadUse.count();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="5-4-Semaphore"><a href="#5-4-Semaphore" class="headerlink" title="5.4 Semaphore"></a>5.4 Semaphore</h2><p>Semaphore字面意思是信号量的意思，它的作用是控制访问特定资源的线程数目。</p><p>举例：现在有一个十字路口，有多辆汽车需要进经过这个十字路口，但是我们规定同时只能有两辆汽车经过。其他汽车处于等待状态，只要某一个汽车经过了这个十字路口，其他的汽车才可以经</p><p>过，但是同时只能有两个汽车经过。如何限定经过这个十字路口车辆数目呢? 我们就可以使用Semaphore。</p><p>Semaphore的常用方法</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="title function_">Semaphore</span><span class="params">(<span class="type">int</span> <span class="keyword">permits</span>)</span><span class="keyword">permits</span> 表示许可线程的数量</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">acquire</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException表示获取许可</span><br><span class="line"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">release</span><span class="params">()</span>表示释放许可</span><br></pre></td></tr></table></figure><p>案例演示：模拟汽车通过十字路口</p><p>实现步骤：</p><ol><li>创建一个汽车的线程任务类(CarThreadRunnable),在该类中定义一个Semaphore类型的成员变量</li><li>创建测试类<ol><li>创建线程任务类对象</li><li>创建5个线程对象，并启动。(5个线程对象，相当于5辆汽车)</li></ol></li></ol><p>CarThreadRunnable类</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">public</span> <span class="keyword">class</span> <span class="title class_">CarThreadRunnable</span> <span class="keyword">implements</span> <span class="title class_">Runnable</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 创建一个Semaphore对象,限制只允许2个线程获取到许可证</span></span><br><span class="line">    <span class="keyword">private</span> <span class="type">Semaphore</span> <span class="variable">semaphore</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Semaphore</span>(<span class="number">2</span>) ;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;                         <span class="comment">// 这个run只允许2个线程同时执行</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 获取许可证</span></span><br><span class="line">            semaphore.acquire();</span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-----&gt;&gt;正在经过十字路口&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 模拟车辆经过十字路口所需要的时间</span></span><br><span class="line">            <span class="type">Random</span> <span class="variable">random</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">Random</span>();</span><br><span class="line">            <span class="type">int</span> <span class="variable">nextInt</span> <span class="operator">=</span> random.nextInt(<span class="number">7</span>);</span><br><span class="line">            TimeUnit.SECONDS.sleep(nextInt);</span><br><span class="line"></span><br><span class="line">            System.out.println(Thread.currentThread().getName() + <span class="string">&quot;-----&gt;&gt;驶出十字路口&quot;</span>);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 释放许可证</span></span><br><span class="line">            semaphore.release();</span><br><span class="line"></span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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">&#125;</span><br></pre></td></tr></table></figure><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><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="keyword">public</span> <span class="keyword">class</span> <span class="title class_">SemaphoreDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建线程任务类对象</span></span><br><span class="line">        <span class="type">CarThreadRunnable</span> <span class="variable">carThreadRunnable</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">CarThreadRunnable</span>() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建5个线程对象，并启动。</span></span><br><span class="line">        <span class="keyword">for</span>(<span class="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; <span class="number">5</span> ; x++) &#123;</span><br><span class="line">            <span class="keyword">new</span> <span class="title class_">Thread</span>(carThreadRunnable).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">&#125;</span><br></pre></td></tr></table></figure><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><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">Thread-<span class="number">0</span>-----&gt;&gt;正在经过十字路口</span><br><span class="line">Thread-<span class="number">1</span>-----&gt;&gt;正在经过十字路口</span><br><span class="line">Thread-<span class="number">1</span>-----&gt;&gt;驶出十字路口</span><br><span class="line">Thread-<span class="number">2</span>-----&gt;&gt;正在经过十字路口</span><br><span class="line">Thread-<span class="number">0</span>-----&gt;&gt;驶出十字路口</span><br><span class="line">Thread-<span class="number">3</span>-----&gt;&gt;正在经过十字路口</span><br><span class="line">Thread-<span class="number">2</span>-----&gt;&gt;驶出十字路口</span><br><span class="line">Thread-<span class="number">4</span>-----&gt;&gt;正在经过十字路口</span><br><span class="line">Thread-<span class="number">4</span>-----&gt;&gt;驶出十字路口</span><br><span class="line">Thread-<span class="number">3</span>-----&gt;&gt;驶出十字路口</span><br></pre></td></tr></table></figure><p>通过控制台输出，我们可以看到当某一个汽车”驶出”十字路口以后，就会有一个汽车立马驶入。</p><h2 id="5-5-Exchanger"><a href="#5-5-Exchanger" class="headerlink" title="5.5 Exchanger"></a>5.5 Exchanger</h2><h3 id="5-5-1-概述以及基本使用"><a href="#5-5-1-概述以及基本使用" class="headerlink" title="5.5.1 概述以及基本使用"></a>5.5.1 概述以及基本使用</h3><p>Exchanger（交换者）是一个用于线程间协作的工具类。Exchanger用于进行线程间的数据交换。</p><p>举例：比如男女双方结婚的时候，需要进行交换结婚戒指。</p><p>Exchanger常用方法</p><figure class="highlight java"><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="keyword">public</span> <span class="title function_">Exchanger</span><span class="params">()</span><span class="comment">// 构造方法</span></span><br><span class="line"><span class="keyword">public</span> V <span class="title function_">exchange</span><span class="params">(V x)</span><span class="comment">// 进行交换数据的方法，参数x表示本方数据 ，返回值v表示对方数据</span></span><br></pre></td></tr></table></figure><p>这两个线程通过exchange方法交换数据，如果第一个线程先执行exchange()方法，它会一直等待第二个线程也执行exchange方法，当两个线程都到达同步点时，这两个线程就可以交换数据，</p><p>将本线程生产出来的数据传递给对方。</p><p>案例演示：模拟交互结婚戒指</p><p>实现步骤：</p><ol><li>创建一个男方的线程类(ManThread),定义一个Exchanger类型的成员变量</li><li>创建一个女方的线程类(WomanThread),定义一个Exchanger类型的成员变量</li><li>测试类<ol><li>创建一个Exchanger对象</li><li>创建一个ManThread对象，把第一步所创建的Exchanger作为构造方法参数传递过来</li><li>创建一个WomanThread对象，把第一步所创建的Exchanger作为构造方法参数传递过来</li><li>启动两个线程</li></ol></li></ol><p>ManThread类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ManThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义Exchanger类型的变量</span></span><br><span class="line">    <span class="keyword">private</span> Exchanger&lt;String&gt; exchanger ;</span><br><span class="line">    <span class="keyword">private</span> String name ;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">ManThread</span><span class="params">(Exchange&lt;String&gt; exchanger , String name)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(name);</span><br><span class="line">        <span class="built_in">this</span>.name = name ;</span><br><span class="line">        <span class="built_in">this</span>.exchanger = exchanger ;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> exchanger.exchange(<span class="string">&quot;钻戒&quot;</span>);</span><br><span class="line">            System.out.println(name + <span class="string">&quot;----&gt;&gt;把钻戒给媳妇&quot;</span>);</span><br><span class="line">            System.out.println(name + <span class="string">&quot;----&gt;&gt;得到媳妇给的&quot;</span> + result);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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">&#125;</span><br></pre></td></tr></table></figure><p>WomanThread类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">WomanThread</span> <span class="keyword">extends</span> <span class="title class_">Thread</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 定义Exchanger类型的变量</span></span><br><span class="line">    <span class="keyword">private</span> Exchanger&lt;String&gt; exchanger ;</span><br><span class="line">    <span class="keyword">private</span> String name ;</span><br><span class="line">    <span class="keyword">public</span> <span class="title function_">WomanThread</span><span class="params">(Exchanger&lt;String&gt; exchanger , String name)</span> &#123;</span><br><span class="line">        <span class="built_in">super</span>(name) ;</span><br><span class="line">        <span class="built_in">this</span>.name = name ;</span><br><span class="line">        <span class="built_in">this</span>.exchanger = exchanger ;</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="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="type">String</span> <span class="variable">result</span> <span class="operator">=</span> exchanger.exchange(<span class="string">&quot;铝戒&quot;</span>);</span><br><span class="line">            System.out.println(name + <span class="string">&quot;----&gt;&gt;把铝戒给老公&quot;</span>);</span><br><span class="line">            System.out.println(name + <span class="string">&quot;----&gt;&gt;得到老公给的&quot;</span> + result);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#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></pre></td></tr></table></figure><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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExchangerDemo01</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建一个Exchanger对象</span></span><br><span class="line">        Exchanger&lt;String&gt; exchanger = <span class="keyword">new</span> <span class="title class_">Exchanger</span>&lt;String&gt;() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建一个ManThread对象</span></span><br><span class="line">        <span class="type">ManThread</span> <span class="variable">manThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">ManThread</span>(exchanger , <span class="string">&quot;杨过&quot;</span>) ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建一个WomanThread对象</span></span><br><span class="line">        <span class="type">WomanThread</span> <span class="variable">womanThread</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">WomanThread</span>(exchanger , <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">        manThread.start();</span><br><span class="line">        womanThread.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></pre></td></tr></table></figure><h3 id="5-5-2-使用场景"><a href="#5-5-2-使用场景" class="headerlink" title="5.5.2 使用场景"></a>5.5.2 使用场景</h3><p>使用场景：可以做数据校对工作</p><p>比如: 现在存在一个文件，该文件中存储的是某一个员工一年的工资信息，现需要将这个员工的工资信息录入到系统中，采用AB岗两人进行录入，录入到两个文件中，系统需要加载这两</p><p>个文件，并对两个文件数据进行校对，看看是否录入一致，</p><p>实现步骤：</p><ol><li>创建一个测试类(ExchangerUseDemo)</li><li>通过匿名内部类的方法创建两个线程对象</li><li>两个线程分别读取文件中的数据，然后将数据存储到各自的集合中</li><li>当每一个线程读取完数据以后，就将数据交换给对方</li><li>然后每个线程使用对方传递过来的数据与自己所录入的数据进行比对</li></ol><p>ExchangerUseDemo类</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></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title class_">ExchangerUseDemo</span> &#123;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title function_">main</span><span class="params">(String[] args)</span> &#123;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 1. 创建Exchanger对象</span></span><br><span class="line">        Exchanger&lt;ArrayList&lt;String&gt;&gt; exchanger = <span class="keyword">new</span> <span class="title class_">Exchanger</span>&lt;ArrayList&lt;String&gt;&gt;() ;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 2. 通过匿名内部类的方法创建两个线程对象</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 读取文件中的数据，然后将其存储到集合中</span></span><br><span class="line">                    ArrayList&lt;String&gt; arrayList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;String&gt;() ;</span><br><span class="line">                    <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">FileReader</span>(<span class="string">&quot;D:\\salary\\2017-salary.txt&quot;</span>)) ;</span><br><span class="line">                    <span class="type">String</span> <span class="variable">line</span> <span class="operator">=</span> <span class="literal">null</span> ;</span><br><span class="line">                    <span class="keyword">while</span>((line = bufferedReader.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">                        arrayList.add(line) ;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// arrayList.add(&quot;90000&quot;) ;</span></span><br><span class="line">                    <span class="comment">// arrayList.set(0 , &quot;90000&quot;) ;</span></span><br><span class="line">                    arrayList.remove(<span class="number">0</span>) ;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 调用Exchanger中的exchange方法完成数据的交换</span></span><br><span class="line">                    ArrayList&lt;String&gt; exchange = exchanger.exchange(arrayList);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 先比对长度</span></span><br><span class="line">                    <span class="keyword">if</span>(arrayList.size() == exchange.size()) &#123;</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="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; arrayList.size() ; x++) &#123;</span><br><span class="line"></span><br><span class="line">                            <span class="comment">// 本方数据</span></span><br><span class="line">                            <span class="type">String</span> <span class="variable">benfangElement</span> <span class="operator">=</span> arrayList.get(x);</span><br><span class="line"></span><br><span class="line">                            <span class="comment">// 对方数据</span></span><br><span class="line">                            <span class="type">String</span> <span class="variable">duifangElement</span> <span class="operator">=</span> exchange.get(x);</span><br><span class="line"></span><br><span class="line">                            <span class="comment">// 比对</span></span><br><span class="line">                            <span class="keyword">if</span>(!benfangElement.equals(duifangElement)) &#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><span class="line"></span><br><span class="line">                    &#125;<span class="keyword">else</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 class="keyword">catch</span> (Exception e) &#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">        &#125;).start();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 线程2</span></span><br><span class="line">        <span class="keyword">new</span> <span class="title class_">Thread</span>(<span class="keyword">new</span> <span class="title class_">Runnable</span>() &#123;</span><br><span class="line"></span><br><span class="line">            <span class="meta">@Override</span></span><br><span class="line">            <span class="keyword">public</span> <span class="keyword">void</span> <span class="title function_">run</span><span class="params">()</span> &#123;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 读取文件中的数据，然后将其存储到集合中</span></span><br><span class="line">                    ArrayList&lt;String&gt; arrayList = <span class="keyword">new</span> <span class="title class_">ArrayList</span>&lt;String&gt;() ;</span><br><span class="line">                    <span class="type">BufferedReader</span> <span class="variable">bufferedReader</span> <span class="operator">=</span> <span class="keyword">new</span> <span class="title class_">BufferedReader</span>(<span class="keyword">new</span> <span class="title class_">FileReader</span>(<span class="string">&quot;D:\\salary\\2017-salary.txt&quot;</span>)) ;</span><br><span class="line">                    <span class="type">String</span> <span class="variable">line</span> <span class="operator">=</span> <span class="literal">null</span> ;</span><br><span class="line">                    <span class="keyword">while</span>((line = bufferedReader.readLine()) != <span class="literal">null</span>) &#123;</span><br><span class="line">                        arrayList.add(line) ;</span><br><span class="line">                    &#125;</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 调用Exchanger中的exchange方法完成数据的交换</span></span><br><span class="line">                    ArrayList&lt;String&gt; exchange = exchanger.exchange(arrayList);</span><br><span class="line"></span><br><span class="line">                    <span class="comment">// 先比对长度</span></span><br><span class="line">                    <span class="keyword">if</span>(arrayList.size() == exchange.size()) &#123;</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="type">int</span> <span class="variable">x</span> <span class="operator">=</span> <span class="number">0</span> ; x &lt; arrayList.size() ; x++) &#123;</span><br><span class="line"></span><br><span class="line">                            <span class="comment">// 本方数据</span></span><br><span class="line">                            <span class="type">String</span> <span class="variable">benfangElement</span> <span class="operator">=</span> arrayList.get(x);</span><br><span class="line"></span><br><span class="line">                            <span class="comment">// 对方数据</span></span><br><span class="line">                            <span class="type">String</span> <span class="variable">duifangElement</span> <span class="operator">=</span> exchange.get(x);</span><br><span class="line"></span><br><span class="line">                            <span class="comment">// 比对</span></span><br><span class="line">                            <span class="keyword">if</span>(!benfangElement.equals(duifangElement)) &#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><span class="line"></span><br><span class="line">                    &#125;<span class="keyword">else</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 class="keyword">catch</span> (Exception e) &#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">        &#125;).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></pre></td></tr></table></figure>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h1 id=&quot;1-线程状态&quot;&gt;&lt;a href=&quot;#1-线程状态&quot; class=&quot;headerlink&quot; title=&quot;1 线程状态&quot;&gt;&lt;/a&gt;1 线程状态&lt;/h1&gt;&lt;h2 id=&quot;1-1-状态介绍&quot;&gt;&lt;a href=&quot;#1-1-状态介绍&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="Java" scheme="https://www.wotemo.com/categories/Java/"/>
    
    <category term="多线程" scheme="https://www.wotemo.com/categories/Java/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
    
    
    <category term="Java" scheme="https://www.wotemo.com/tags/Java/"/>
    
    <category term="多线程" scheme="https://www.wotemo.com/tags/%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
    
  </entry>
  
  <entry>
    <title>Mysql学习笔记（转）</title>
    <link href="https://www.wotemo.com/posts/2053.html"/>
    <id>https://www.wotemo.com/posts/2053.html</id>
    <published>2023-11-08T14:09:07.000Z</published>
    <updated>2024-06-12T12:08:23.442Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>声明：本页面文字完全转载，仅供个人学习使用，如有侵权请联系！</p></blockquote><h1 id="基础篇"><a href="#基础篇" class="headerlink" title="基础篇"></a>基础篇</h1><h2 id="通用语法及分类"><a href="#通用语法及分类" class="headerlink" title="通用语法及分类"></a>通用语法及分类</h2><ul><li>DDL: 数据定义语言，用来定义数据库对象（数据库、表、字段）</li><li>DML: 数据操作语言，用来对数据库表中的数据进行增删改</li><li>DQL: 数据查询语言，用来查询数据库中表的记录</li><li>DCL: 数据控制语言，用来创建数据库用户、控制数据库的控制权限</li></ul><h3 id="DDL（数据定义语言）"><a href="#DDL（数据定义语言）" class="headerlink" title="DDL（数据定义语言）"></a>DDL（数据定义语言）</h3><p>数据定义语言</p><h4 id="数据库操作"><a href="#数据库操作" class="headerlink" title="数据库操作"></a>数据库操作</h4><p>查询所有数据库：<br><code>SHOW DATABASES;</code><br>查询当前数据库：<br><code>SELECT DATABASE();</code><br>创建数据库：<br><code>CREATE DATABASE [ IF NOT EXISTS ] 数据库名 [ DEFAULT CHARSET 字符集] [COLLATE 排序规则 ];</code><br>删除数据库：<br><code>DROP DATABASE [ IF EXISTS ] 数据库名;</code><br>使用数据库：<br><code>USE 数据库名;</code></p><h5 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>UTF8字符集长度为3字节，有些符号占4字节，所以推荐用utf8mb4字符集</li></ul><h4 id="表操作"><a href="#表操作" class="headerlink" title="表操作"></a>表操作</h4><p>查询当前数据库所有表：<br><code>SHOW TABLES;</code><br>查询表结构：<br><code>DESC 表名;</code><br>查询指定表的建表语句：<br><code>SHOW CREATE TABLE 表名;</code></p><p>创建表：</p><figure class="highlight plaintext"><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">CREATE TABLE 表名(</span><br><span class="line">字段1 字段1类型 [COMMENT 字段1注释],</span><br><span class="line">字段2 字段2类型 [COMMENT 字段2注释],</span><br><span class="line">字段3 字段3类型 [COMMENT 字段3注释],</span><br><span class="line">...</span><br><span class="line">字段n 字段n类型 [COMMENT 字段n注释]</span><br><span class="line">)[ COMMENT 表注释 ];</span><br></pre></td></tr></table></figure><p><strong>最后一个字段后面没有逗号</strong></p><p>添加字段：<br><code>ALTER TABLE 表名 ADD 字段名 类型(长度) [COMMENT 注释] [约束];</code><br>例：<code>ALTER TABLE emp ADD nickname varchar(20) COMMENT &#39;昵称&#39;;</code></p><p>修改数据类型：<br><code>ALTER TABLE 表名 MODIFY 字段名 新数据类型(长度);</code><br>修改字段名和字段类型：<br><code>ALTER TABLE 表名 CHANGE 旧字段名 新字段名 类型(长度) [COMMENT 注释] [约束];</code><br>例：将emp表的nickname字段修改为username，类型为varchar(30)<br><code>ALTER TABLE emp CHANGE nickname username varchar(30) COMMENT &#39;昵称&#39;;</code></p><p>删除字段：<br><code>ALTER TABLE 表名 DROP 字段名;</code></p><p>修改表名：<br><code>ALTER TABLE 表名 RENAME TO 新表名</code></p><p>删除表：<br><code>DROP TABLE [IF EXISTS] 表名;</code><br>删除表，并重新创建该表：<br><code>TRUNCATE TABLE 表名;</code></p><h3 id="DML（数据操作语言）"><a href="#DML（数据操作语言）" class="headerlink" title="DML（数据操作语言）"></a>DML（数据操作语言）</h3><h4 id="添加数据"><a href="#添加数据" class="headerlink" title="添加数据"></a>添加数据</h4><p>指定字段：<br><code>INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...);</code><br>全部字段：<br><code>INSERT INTO 表名 VALUES (值1, 值2, ...);</code></p><p>批量添加数据：<br><code>INSERT INTO 表名 (字段名1, 字段名2, ...) VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);</code><br><code>INSERT INTO 表名 VALUES (值1, 值2, ...), (值1, 值2, ...), (值1, 值2, ...);</code></p><h5 id="注意事项-1"><a href="#注意事项-1" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>字符串和日期类型数据应该包含在引号中</li><li>插入的数据大小应该在字段的规定范围内</li></ul><h4 id="更新和删除数据"><a href="#更新和删除数据" class="headerlink" title="更新和删除数据"></a>更新和删除数据</h4><p>修改数据：<br><code>UPDATE 表名 SET 字段名1 = 值1, 字段名2 = 值2, ... [ WHERE 条件 ];</code><br>例：<br><code>UPDATE emp SET name = &#39;Jack&#39; WHERE id = 1;</code></p><p>删除数据：<br><code>DELETE FROM 表名 [ WHERE 条件 ];</code></p><h3 id="DQL（数据查询语言）"><a href="#DQL（数据查询语言）" class="headerlink" title="DQL（数据查询语言）"></a>DQL（数据查询语言）</h3><p>语法：</p><figure class="highlight plaintext"><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">SELECT</span><br><span class="line">字段列表</span><br><span class="line">FROM</span><br><span class="line">表名字段</span><br><span class="line">WHERE</span><br><span class="line">条件列表</span><br><span class="line">GROUP BY</span><br><span class="line">分组字段列表</span><br><span class="line">HAVING</span><br><span class="line">分组后的条件列表</span><br><span class="line">ORDER BY</span><br><span class="line">排序字段列表</span><br><span class="line">LIMIT</span><br><span class="line">分页参数</span><br></pre></td></tr></table></figure><h4 id="基础查询"><a href="#基础查询" class="headerlink" title="基础查询"></a>基础查询</h4><p>查询多个字段：<br><code>SELECT 字段1, 字段2, 字段3, ... FROM 表名;</code><br><code>SELECT * FROM 表名;</code></p><p>设置别名：<br><code>SELECT 字段1 [ AS 别名1 ], 字段2 [ AS 别名2 ], 字段3 [ AS 别名3 ], ... FROM 表名;</code><br><code>SELECT 字段1 [ 别名1 ], 字段2 [ 别名2 ], 字段3 [ 别名3 ], ... FROM 表名;</code></p><p>去除重复记录：<br><code>SELECT DISTINCT 字段列表 FROM 表名;</code></p><p>转义：<br><code>SELECT * FROM 表名 WHERE name LIKE &#39;/_张三&#39; ESCAPE &#39;/&#39;</code><br>&#x2F; 之后的_不作为通配符</p><h4 id="条件查询"><a href="#条件查询" class="headerlink" title="条件查询"></a>条件查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 WHERE 条件列表;</code></p><p>条件：</p><table><thead><tr><th>比较运算符</th><th>功能</th></tr></thead><tbody><tr><td>&gt;</td><td>大于</td></tr><tr><td>&gt;&#x3D;</td><td>大于等于</td></tr><tr><td>&lt;</td><td>小于</td></tr><tr><td>&lt;&#x3D;</td><td>小于等于</td></tr><tr><td>&#x3D;</td><td>等于</td></tr><tr><td>&lt;&gt; 或 !&#x3D;</td><td>不等于</td></tr><tr><td>BETWEEN … AND …</td><td>在某个范围内（含最小、最大值）</td></tr><tr><td>IN(…)</td><td>在in之后的列表中的值，多选一</td></tr><tr><td>LIKE 占位符</td><td>模糊匹配（_匹配单个字符，%匹配任意个字符）</td></tr><tr><td>IS NULL</td><td>是NULL</td></tr></tbody></table><table><thead><tr><th>逻辑运算符</th><th>功能</th></tr></thead><tbody><tr><td>AND 或 &amp;&amp;</td><td>并且（多个条件同时成立）</td></tr><tr><td>OR 或 &#124;&#124;</td><td>或者（多个条件任意一个成立）</td></tr><tr><td>NOT 或 !</td><td>非，不是</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><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">-- 年龄等于30</span><br><span class="line">select * from employee where age = 30;</span><br><span class="line">-- 年龄小于30</span><br><span class="line">select * from employee where age &lt; 30;</span><br><span class="line">-- 小于等于</span><br><span class="line">select * from employee where age &lt;= 30;</span><br><span class="line">-- 没有身份证</span><br><span class="line">select * from employee where idcard is null or idcard = &#x27;&#x27;;</span><br><span class="line">-- 有身份证</span><br><span class="line">select * from employee where idcard;</span><br><span class="line">select * from employee where idcard is not null;</span><br><span class="line">-- 不等于</span><br><span class="line">select * from employee where age != 30;</span><br><span class="line">-- 年龄在20到30之间</span><br><span class="line">select * from employee where age between 20 and 30;</span><br><span class="line">select * from employee where age &gt;= 20 and age &lt;= 30;</span><br><span class="line">-- 下面语句不报错，但查不到任何信息</span><br><span class="line">select * from employee where age between 30 and 20;</span><br><span class="line">-- 性别为女且年龄小于30</span><br><span class="line">select * from employee where age &lt; 30 and gender = &#x27;女&#x27;;</span><br><span class="line">-- 年龄等于25或30或35</span><br><span class="line">select * from employee where age = 25 or age = 30 or age = 35;</span><br><span class="line">select * from employee where age in (25, 30, 35);</span><br><span class="line">-- 姓名为两个字</span><br><span class="line">select * from employee where name like &#x27;__&#x27;;</span><br><span class="line">-- 身份证最后为X</span><br><span class="line">select * from employee where idcard like &#x27;%X&#x27;;</span><br></pre></td></tr></table></figure><h4 id="聚合查询（聚合函数）"><a href="#聚合查询（聚合函数）" class="headerlink" title="聚合查询（聚合函数）"></a>聚合查询（聚合函数）</h4><p>常见聚合函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>count</td><td>统计数量</td></tr><tr><td>max</td><td>最大值</td></tr><tr><td>min</td><td>最小值</td></tr><tr><td>avg</td><td>平均值</td></tr><tr><td>sum</td><td>求和</td></tr></tbody></table><p>语法：<br><code>SELECT 聚合函数(字段列表) FROM 表名;</code><br>例：<br><code>SELECT count(id) from employee where workaddress = &quot;广东省&quot;;</code></p><h4 id="分组查询"><a href="#分组查询" class="headerlink" title="分组查询"></a>分组查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 [ WHERE 条件 ] GROUP BY 分组字段名 [ HAVING 分组后的过滤条件 ];</code></p><p>where 和 having 的区别：</p><ul><li>执行时机不同：where是分组之前进行过滤，不满足where条件不参与分组；having是分组后对结果进行过滤。</li><li>判断条件不同：where不能对聚合函数进行判断，而having可以。</li></ul><p>例子：</p><figure class="highlight plaintext"><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><br><span class="line">select count(*) from employee group by gender;</span><br><span class="line">-- 根据性别分组，统计男性和女性数量</span><br><span class="line">select gender, count(*) from employee group by gender;</span><br><span class="line">-- 根据性别分组，统计男性和女性的平均年龄</span><br><span class="line">select gender, avg(age) from employee group by gender;</span><br><span class="line">-- 年龄小于45，并根据工作地址分组</span><br><span class="line">select workaddress, count(*) from employee where age &lt; 45 group by workaddress;</span><br><span class="line">-- 年龄小于45，并根据工作地址分组，获取员工数量大于等于3的工作地址</span><br><span class="line">select workaddress, count(*) address_count from employee where age &lt; 45 group by workaddress having address_count &gt;= 3;</span><br></pre></td></tr></table></figure><h5 id="注意事项-2"><a href="#注意事项-2" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>执行顺序：where &gt; 聚合函数 &gt; having</li><li>分组之后，查询的字段一般为聚合函数和分组字段，查询其他字段无任何意义</li></ul><h4 id="排序查询"><a href="#排序查询" class="headerlink" title="排序查询"></a>排序查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 ORDER BY 字段1 排序方式1, 字段2 排序方式2;</code></p><p>排序方式：</p><ul><li>ASC: 升序（默认）</li><li>DESC: 降序</li></ul><p>例子：</p><figure class="highlight plaintext"><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><br><span class="line">SELECT * FROM employee ORDER BY age ASC;</span><br><span class="line">SELECT * FROM employee ORDER BY age;</span><br><span class="line">-- 两字段排序，根据年龄升序排序，入职时间降序排序</span><br><span class="line">SELECT * FROM employee ORDER BY age ASC, entrydate DESC;</span><br></pre></td></tr></table></figure><h5 id="注意事项-3"><a href="#注意事项-3" class="headerlink" title="注意事项"></a>注意事项</h5><p>如果是多字段排序，当第一个字段值相同时，才会根据第二个字段进行排序</p><h4 id="分页查询"><a href="#分页查询" class="headerlink" title="分页查询"></a>分页查询</h4><p>语法：<br><code>SELECT 字段列表 FROM 表名 LIMIT 起始索引, 查询记录数;</code></p><p>例子：</p><figure class="highlight plaintext"><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">-- 查询第一页数据，展示10条</span><br><span class="line">SELECT * FROM employee LIMIT 0, 10;</span><br><span class="line">-- 查询第二页</span><br><span class="line">SELECT * FROM employee LIMIT 10, 10;</span><br></pre></td></tr></table></figure><h5 id="注意事项-4"><a href="#注意事项-4" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>起始索引从0开始，起始索引 &#x3D; （查询页码 - 1） * 每页显示记录数</li><li>分页查询是数据库的方言，不同数据库有不同实现，MySQL是LIMIT</li><li>如果查询的是第一页数据，起始索引可以省略，直接简写 LIMIT 10</li></ul><h4 id="DQL执行顺序"><a href="#DQL执行顺序" class="headerlink" title="DQL执行顺序"></a>DQL执行顺序</h4><p>FROM -&gt; WHERE -&gt; GROUP BY -&gt; SELECT -&gt; ORDER BY -&gt; LIMIT</p><h3 id="DCL"><a href="#DCL" class="headerlink" title="DCL"></a>DCL</h3><h4 id="管理用户"><a href="#管理用户" class="headerlink" title="管理用户"></a>管理用户</h4><p>查询用户：</p><figure class="highlight plaintext"><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">USE mysql;</span><br><span class="line">SELECT * FROM user;</span><br></pre></td></tr></table></figure><p>创建用户:<br><code>CREATE USER &#39;用户名&#39;@&#39;主机名&#39; IDENTIFIED BY &#39;密码&#39;;</code></p><p>修改用户密码：<br><code>ALTER USER &#39;用户名&#39;@&#39;主机名&#39; IDENTIFIED WITH mysql_native_password BY &#39;新密码&#39;;</code></p><p>删除用户：<br><code>DROP USER &#39;用户名&#39;@&#39;主机名&#39;;</code></p><p>例子：</p><figure class="highlight plaintext"><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">-- 创建用户test，只能在当前主机localhost访问</span><br><span class="line">create user &#x27;test&#x27;@&#x27;localhost&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">-- 创建用户test，能在任意主机访问</span><br><span class="line">create user &#x27;test&#x27;@&#x27;%&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">create user &#x27;test&#x27; identified by &#x27;123456&#x27;;</span><br><span class="line">-- 修改密码</span><br><span class="line">alter user &#x27;test&#x27;@&#x27;localhost&#x27; identified with mysql_native_password by &#x27;1234&#x27;;</span><br><span class="line">-- 删除用户</span><br><span class="line">drop user &#x27;test&#x27;@&#x27;localhost&#x27;;</span><br></pre></td></tr></table></figure><h5 id="注意事项-5"><a href="#注意事项-5" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>主机名可以使用 % 通配</li></ul><h4 id="权限控制"><a href="#权限控制" class="headerlink" title="权限控制"></a>权限控制</h4><p>常用权限：</p><table><thead><tr><th>权限</th><th>说明</th></tr></thead><tbody><tr><td>ALL, ALL PRIVILEGES</td><td>所有权限</td></tr><tr><td>SELECT</td><td>查询数据</td></tr><tr><td>INSERT</td><td>插入数据</td></tr><tr><td>UPDATE</td><td>修改数据</td></tr><tr><td>DELETE</td><td>删除数据</td></tr><tr><td>ALTER</td><td>修改表</td></tr><tr><td>DROP</td><td>删除数据库&#x2F;表&#x2F;视图</td></tr><tr><td>CREATE</td><td>创建数据库&#x2F;表</td></tr></tbody></table><p>更多权限请看<a href="#%E6%9D%83%E9%99%90%E4%B8%80%E8%A7%88%E8%A1%A8" title="权限一览表">权限一览表</a></p><p>查询权限：<br><code>SHOW GRANTS FOR &#39;用户名&#39;@&#39;主机名&#39;;</code></p><p>授予权限：<br><code>GRANT 权限列表 ON 数据库名.表名 TO &#39;用户名&#39;@&#39;主机名&#39;;</code></p><p>撤销权限：<br><code>REVOKE 权限列表 ON 数据库名.表名 FROM &#39;用户名&#39;@&#39;主机名&#39;;</code></p><h5 id="注意事项-6"><a href="#注意事项-6" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>多个权限用逗号分隔</li><li>授权时，数据库名和表名可以用 * 进行通配，代表所有</li></ul><h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><ul><li>字符串函数</li><li>数值函数</li><li>日期函数</li><li>流程函数</li></ul><h3 id="字符串函数"><a href="#字符串函数" class="headerlink" title="字符串函数"></a>字符串函数</h3><p>常用函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>CONCAT(s1, s2, …, sn)</td><td>字符串拼接，将s1, s2, …, sn拼接成一个字符串</td></tr><tr><td>LOWER(str)</td><td>将字符串全部转为小写</td></tr><tr><td>UPPER(str)</td><td>将字符串全部转为大写</td></tr><tr><td>LPAD(str, n, pad)</td><td>左填充，用字符串pad对str的左边进行填充，达到n个字符串长度</td></tr><tr><td>RPAD(str, n, pad)</td><td>右填充，用字符串pad对str的右边进行填充，达到n个字符串长度</td></tr><tr><td>TRIM(str)</td><td>去掉字符串头部和尾部的空格</td></tr><tr><td>SUBSTRING(str, start, len)</td><td>返回从字符串str从start位置起的len个长度的字符串</td></tr><tr><td>REPLACE(column, source, replace)</td><td>替换字符串</td></tr></tbody></table><p>使用示例：</p><figure class="highlight plaintext"><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><br><span class="line">SELECT CONCAT(&#x27;Hello&#x27;, &#x27;World&#x27;);</span><br><span class="line">-- 小写</span><br><span class="line">SELECT LOWER(&#x27;Hello&#x27;);</span><br><span class="line">-- 大写</span><br><span class="line">SELECT UPPER(&#x27;Hello&#x27;);</span><br><span class="line">-- 左填充</span><br><span class="line">SELECT LPAD(&#x27;01&#x27;, 5, &#x27;-&#x27;);</span><br><span class="line">-- 右填充</span><br><span class="line">SELECT RPAD(&#x27;01&#x27;, 5, &#x27;-&#x27;);</span><br><span class="line">-- 去除空格</span><br><span class="line">SELECT TRIM(&#x27; Hello World &#x27;);</span><br><span class="line">-- 切片（起始索引为1）</span><br><span class="line">SELECT SUBSTRING(&#x27;Hello World&#x27;, 1, 5);</span><br></pre></td></tr></table></figure><h3 id="数值函数"><a href="#数值函数" class="headerlink" title="数值函数"></a>数值函数</h3><p>常见函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>CEIL(x)</td><td>向上取整</td></tr><tr><td>FLOOR(x)</td><td>向下取整</td></tr><tr><td>MOD(x, y)</td><td>返回x&#x2F;y的模</td></tr><tr><td>RAND()</td><td>返回0~1内的随机数</td></tr><tr><td>ROUND(x, y)</td><td>求参数x的四舍五入值，保留y位小数</td></tr></tbody></table><h3 id="日期函数"><a href="#日期函数" class="headerlink" title="日期函数"></a>日期函数</h3><p>常用函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>CURDATE()</td><td>返回当前日期</td></tr><tr><td>CURTIME()</td><td>返回当前时间</td></tr><tr><td>NOW()</td><td>返回当前日期和时间</td></tr><tr><td>YEAR(date)</td><td>获取指定date的年份</td></tr><tr><td>MONTH(date)</td><td>获取指定date的月份</td></tr><tr><td>DAY(date)</td><td>获取指定date的日期</td></tr><tr><td>DATE_ADD(date, INTERVAL expr type)</td><td>返回一个日期&#x2F;时间值加上一个时间间隔expr后的时间值</td></tr><tr><td>DATEDIFF(date1, date2)</td><td>返回起始时间date1和结束时间date2之间的天数</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><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">-- DATE_ADD</span><br><span class="line">SELECT DATE_ADD(NOW(), INTERVAL 70 YEAR);</span><br></pre></td></tr></table></figure><h3 id="流程函数"><a href="#流程函数" class="headerlink" title="流程函数"></a>流程函数</h3><p>常用函数：</p><table><thead><tr><th>函数</th><th>功能</th></tr></thead><tbody><tr><td>IF(value, t, f)</td><td>如果value为true，则返回t，否则返回f</td></tr><tr><td>IFNULL(value1, value2)</td><td>如果value1不为空，返回value1，否则返回value2</td></tr><tr><td>CASE WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END</td><td>如果val1为true，返回res1，… 否则返回default默认值</td></tr><tr><td>CASE [ expr ] WHEN [ val1 ] THEN [ res1 ] … ELSE [ default ] END</td><td>如果expr的值等于val1，返回res1，… 否则返回default默认值</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><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">select</span><br><span class="line">name,</span><br><span class="line">(case when age &gt; 30 then &#x27;中年&#x27; else &#x27;青年&#x27; end)</span><br><span class="line">from employee;</span><br><span class="line">select</span><br><span class="line">name,</span><br><span class="line">(case workaddress when &#x27;北京市&#x27; then &#x27;一线城市&#x27; when &#x27;上海市&#x27; then &#x27;一线城市&#x27; else &#x27;二线城市&#x27; end) as &#x27;工作地址&#x27;</span><br><span class="line">from employee;</span><br></pre></td></tr></table></figure><h2 id="约束"><a href="#约束" class="headerlink" title="约束"></a>约束</h2><p>分类：</p><table><thead><tr><th>约束</th><th>描述</th><th>关键字</th></tr></thead><tbody><tr><td>非空约束</td><td>限制该字段的数据不能为null</td><td>NOT NULL</td></tr><tr><td>唯一约束</td><td>保证该字段的所有数据都是唯一、不重复的</td><td>UNIQUE</td></tr><tr><td>主键约束</td><td>主键是一行数据的唯一标识，要求非空且唯一</td><td>PRIMARY KEY</td></tr><tr><td>默认约束</td><td>保存数据时，如果未指定该字段的值，则采用默认值</td><td>DEFAULT</td></tr><tr><td>检查约束（8.0.1版本后）</td><td>保证字段值满足某一个条件</td><td>CHECK</td></tr><tr><td>外键约束</td><td>用来让两张图的数据之间建立连接，保证数据的一致性和完整性</td><td>FOREIGN KEY</td></tr></tbody></table><p>约束是作用于表中字段上的，可以再创建表&#x2F;修改表的时候添加约束。</p><h3 id="常用约束"><a href="#常用约束" class="headerlink" title="常用约束"></a>常用约束</h3><table><thead><tr><th>约束条件</th><th>关键字</th></tr></thead><tbody><tr><td>主键</td><td>PRIMARY KEY</td></tr><tr><td>自动增长</td><td>AUTO_INCREMENT</td></tr><tr><td>不为空</td><td>NOT NULL</td></tr><tr><td>唯一</td><td>UNIQUE</td></tr><tr><td>逻辑条件</td><td>CHECK</td></tr><tr><td>默认值</td><td>DEFAULT</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><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">create table user(</span><br><span class="line">id int primary key auto_increment,</span><br><span class="line">name varchar(10) not null unique,</span><br><span class="line">age int check(age &gt; 0 and age &lt; 120),</span><br><span class="line">status char(1) default &#x27;1&#x27;,</span><br><span class="line">gender char(1)</span><br><span class="line">);</span><br></pre></td></tr></table></figure><h3 id="外键约束"><a href="#外键约束" class="headerlink" title="外键约束"></a>外键约束</h3><p>添加外键：</p><figure class="highlight plaintext"><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">CREATE TABLE 表名(</span><br><span class="line">字段名 字段类型,</span><br><span class="line">...</span><br><span class="line">[CONSTRAINT] [外键名称] FOREIGN KEY(外键字段名) REFERENCES 主表(主表列名)</span><br><span class="line">);</span><br><span class="line">ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段名) REFERENCES 主表(主表列名);</span><br><span class="line"></span><br><span class="line">-- 例子</span><br><span class="line">alter table emp add constraint fk_emp_dept_id foreign key(dept_id) references dept(id);</span><br></pre></td></tr></table></figure><p>删除外键：<br><code>ALTER TABLE 表名 DROP FOREIGN KEY 外键名;</code></p><h4 id="删除-更新行为"><a href="#删除-更新行为" class="headerlink" title="删除&#x2F;更新行为"></a>删除&#x2F;更新行为</h4><table><thead><tr><th>行为</th><th>说明</th></tr></thead><tbody><tr><td>NO ACTION</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则不允许删除&#x2F;更新（与RESTRICT一致）</td></tr><tr><td>RESTRICT</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则不允许删除&#x2F;更新（与NO ACTION一致）</td></tr><tr><td>CASCADE</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则也删除&#x2F;更新外键在子表中的记录</td></tr><tr><td>SET NULL</td><td>当在父表中删除&#x2F;更新对应记录时，首先检查该记录是否有对应外键，如果有则设置子表中该外键值为null（要求该外键允许为null）</td></tr><tr><td>SET DEFAULT</td><td>父表有变更时，子表将外键设为一个默认值（Innodb不支持）</td></tr></tbody></table><p>更改删除&#x2F;更新行为：<br><code>ALTER TABLE 表名 ADD CONSTRAINT 外键名称 FOREIGN KEY (外键字段) REFERENCES 主表名(主表字段名) ON UPDATE 行为 ON DELETE 行为;</code></p><h2 id="多表查询"><a href="#多表查询" class="headerlink" title="多表查询"></a>多表查询</h2><h3 id="多表关系"><a href="#多表关系" class="headerlink" title="多表关系"></a>多表关系</h3><ul><li>一对多（多对一）</li><li>多对多</li><li>一对一</li></ul><h4 id="一对多"><a href="#一对多" class="headerlink" title="一对多"></a>一对多</h4><p>案例：部门与员工<br>关系：一个部门对应多个员工，一个员工对应一个部门<br>实现：在多的一方建立外键，指向一的一方的主键</p><h4 id="多对多"><a href="#多对多" class="headerlink" title="多对多"></a>多对多</h4><p>案例：学生与课程<br>关系：一个学生可以选多门课程，一门课程也可以供多个学生选修<br>实现：建立第三张中间表，中间表至少包含两个外键，分别关联两方主键</p><h4 id="一对一"><a href="#一对一" class="headerlink" title="一对一"></a>一对一</h4><p>案例：用户与用户详情<br>关系：一对一关系，多用于单表拆分，将一张表的基础字段放在一张表中，其他详情字段放在另一张表中，以提升操作效率<br>实现：在任意一方加入外键，关联另外一方的主键，并且设置外键为唯一的（UNIQUE）</p><h3 id="查询"><a href="#查询" class="headerlink" title="查询"></a>查询</h3><p>合并查询（笛卡尔积，会展示所有组合结果）：<br><code>select * from employee, dept;</code></p><blockquote><p>笛卡尔积：两个集合A集合和B集合的所有组合情况（在多表查询时，需要消除无效的笛卡尔积）</p></blockquote><p>消除无效笛卡尔积：<br><code>select * from employee, dept where employee.dept = dept.id;</code></p><h3 id="内连接查询"><a href="#内连接查询" class="headerlink" title="内连接查询"></a>内连接查询</h3><p>内连接查询的是两张表交集的部分</p><p>隐式内连接：<br><code>SELECT 字段列表 FROM 表1, 表2 WHERE 条件 ...;</code></p><p>显式内连接：<br><code>SELECT 字段列表 FROM 表1 [ INNER ] JOIN 表2 ON 连接条件 ...;</code></p><p>显式性能比隐式高</p><p>例子：</p><figure class="highlight plaintext"><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><br><span class="line">-- 隐式</span><br><span class="line">select e.name, d.name from employee as e, dept as d where e.dept = d.id;</span><br><span class="line">-- 显式</span><br><span class="line">select e.name, d.name from employee as e inner join dept as d on e.dept = d.id;</span><br></pre></td></tr></table></figure><h3 id="外连接查询"><a href="#外连接查询" class="headerlink" title="外连接查询"></a>外连接查询</h3><p>左外连接：<br>查询左表所有数据，以及两张表交集部分数据<br><code>SELECT 字段列表 FROM 表1 LEFT [ OUTER ] JOIN 表2 ON 条件 ...;</code><br>相当于查询表1的所有数据，包含表1和表2交集部分数据</p><p>右外连接：<br>查询右表所有数据，以及两张表交集部分数据<br><code>SELECT 字段列表 FROM 表1 RIGHT [ OUTER ] JOIN 表2 ON 条件 ...;</code></p><p>例子：</p><figure class="highlight plaintext"><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><br><span class="line">select e.*, d.name from employee as e left outer join dept as d on e.dept = d.id;</span><br><span class="line">select d.name, e.* from dept d left outer join emp e on e.dept = d.id;  -- 这条语句与下面的语句效果一样</span><br><span class="line">-- 右</span><br><span class="line">select d.name, e.* from employee as e right outer join dept as d on e.dept = d.id;</span><br></pre></td></tr></table></figure><p>左连接可以查询到没有dept的employee，右连接可以查询到没有employee的dept</p><h3 id="自连接查询"><a href="#自连接查询" class="headerlink" title="自连接查询"></a>自连接查询</h3><p>当前表与自身的连接查询，自连接必须使用表别名</p><p>语法：<br><code>SELECT 字段列表 FROM 表A 别名A JOIN 表A 别名B ON 条件 ...;</code></p><p>自连接查询，可以是内连接查询，也可以是外连接查询</p><p>例子：</p><figure class="highlight plaintext"><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><br><span class="line">select a.name, b.name from employee a, employee b where a.manager = b.id;</span><br><span class="line">-- 没有领导的也查询出来</span><br><span class="line">select a.name, b.name from employee a left join employee b on a.manager = b.id;</span><br></pre></td></tr></table></figure><h3 id="联合查询-union-union-all"><a href="#联合查询-union-union-all" class="headerlink" title="联合查询 union, union all"></a>联合查询 union, union all</h3><p>把多次查询的结果合并，形成一个新的查询集</p><p>语法：</p><figure class="highlight plaintext"><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">SELECT 字段列表 FROM 表A ...</span><br><span class="line">UNION [ALL]</span><br><span class="line">SELECT 字段列表 FROM 表B ...</span><br></pre></td></tr></table></figure><h4 id="注意事项-7"><a href="#注意事项-7" class="headerlink" title="注意事项"></a>注意事项</h4><ul><li>UNION ALL 会有重复结果，UNION 不会</li><li>联合查询比使用or效率高，不会使索引失效</li></ul><h3 id="子查询"><a href="#子查询" class="headerlink" title="子查询"></a>子查询</h3><p>SQL语句中嵌套SELECT语句，称谓嵌套查询，又称子查询。<br><code>SELECT * FROM t1 WHERE column1 = ( SELECT column1 FROM t2);</code><br><strong>子查询外部的语句可以是 INSERT &#x2F; UPDATE &#x2F; DELETE &#x2F; SELECT 的任何一个</strong></p><p>根据子查询结果可以分为：</p><ul><li>标量子查询（子查询结果为单个值）</li><li>列子查询（子查询结果为一列）</li><li>行子查询（子查询结果为一行）</li><li>表子查询（子查询结果为多行多列）</li></ul><p>根据子查询位置可分为：</p><ul><li>WHERE 之后</li><li>FROM 之后</li><li>SELECT 之后</li></ul><h4 id="标量子查询"><a href="#标量子查询" class="headerlink" title="标量子查询"></a>标量子查询</h4><p>子查询返回的结果是单个值（数字、字符串、日期等）。<br>常用操作符：- &lt; &gt; &gt; &gt;&#x3D; &lt; &lt;&#x3D;</p><p>例子：</p><figure class="highlight plaintext"><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><br><span class="line">select id from dept where name = &#x27;销售部&#x27;;</span><br><span class="line">-- 根据销售部部门ID，查询员工信息</span><br><span class="line">select * from employee where dept = 4;</span><br><span class="line">-- 合并（子查询）</span><br><span class="line">select * from employee where dept = (select id from dept where name = &#x27;销售部&#x27;);</span><br><span class="line"></span><br><span class="line">-- 查询xxx入职之后的员工信息</span><br><span class="line">select * from employee where entrydate &gt; (select entrydate from employee where name = &#x27;xxx&#x27;);</span><br></pre></td></tr></table></figure><h4 id="列子查询"><a href="#列子查询" class="headerlink" title="列子查询"></a>列子查询</h4><p>返回的结果是一列（可以是多行）。</p><p>常用操作符：</p><table><thead><tr><th>操作符</th><th>描述</th></tr></thead><tbody><tr><td>IN</td><td>在指定的集合范围内，多选一</td></tr><tr><td>NOT IN</td><td>不在指定的集合范围内</td></tr><tr><td>ANY</td><td>子查询返回列表中，有任意一个满足即可</td></tr><tr><td>SOME</td><td>与ANY等同，使用SOME的地方都可以使用ANY</td></tr><tr><td>ALL</td><td>子查询返回列表的所有值都必须满足</td></tr></tbody></table><p>例子：</p><figure class="highlight plaintext"><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><br><span class="line">select * from employee where dept in (select id from dept where name = &#x27;销售部&#x27; or name = &#x27;市场部&#x27;);</span><br><span class="line">-- 查询比财务部所有人工资都高的员工信息</span><br><span class="line">select * from employee where salary &gt; all(select salary from employee where dept = (select id from dept where name = &#x27;财务部&#x27;));</span><br><span class="line">-- 查询比研发部任意一人工资高的员工信息</span><br><span class="line">select * from employee where salary &gt; any (select salary from employee where dept = (select id from dept where name = &#x27;研发部&#x27;));</span><br></pre></td></tr></table></figure><h4 id="行子查询"><a href="#行子查询" class="headerlink" title="行子查询"></a>行子查询</h4><p>返回的结果是一行（可以是多列）。<br>常用操作符：&#x3D;, &lt;, &gt;, IN, NOT IN</p><p>例子：</p><figure class="highlight plaintext"><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">-- 查询与xxx的薪资及直属领导相同的员工信息</span><br><span class="line">select * from employee where (salary, manager) = (12500, 1);</span><br><span class="line">select * from employee where (salary, manager) = (select salary, manager from employee where name = &#x27;xxx&#x27;);</span><br></pre></td></tr></table></figure><h4 id="表子查询"><a href="#表子查询" class="headerlink" title="表子查询"></a>表子查询</h4><p>返回的结果是多行多列<br>常用操作符：IN</p><p>例子：</p><figure class="highlight plaintext"><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">-- 查询与xxx1，xxx2的职位和薪资相同的员工</span><br><span class="line">select * from employee where (job, salary) in (select job, salary from employee where name = &#x27;xxx1&#x27; or name = &#x27;xxx2&#x27;);</span><br><span class="line">-- 查询入职日期是2006-01-01之后的员工，及其部门信息</span><br><span class="line">select e.*, d.* from (select * from employee where entrydate &gt; &#x27;2006-01-01&#x27;) as e left join dept as d on e.dept = d.id;</span><br></pre></td></tr></table></figure><h2 id="事务"><a href="#事务" class="headerlink" title="事务"></a>事务</h2><p>事务是一组操作的集合，事务会把所有操作作为一个整体一起向系统提交或撤销操作请求，即这些操作要么同时成功，要么同时失败。</p><p>基本操作：</p><figure class="highlight plaintext"><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">-- 1. 查询张三账户余额</span><br><span class="line">select * from account where name = &#x27;张三&#x27;;</span><br><span class="line">-- 2. 将张三账户余额-1000</span><br><span class="line">update account set money = money - 1000 where name = &#x27;张三&#x27;;</span><br><span class="line">-- 此语句出错后张三钱减少但是李四钱没有增加</span><br><span class="line">模拟sql语句错误</span><br><span class="line">-- 3. 将李四账户余额+1000</span><br><span class="line">update account set money = money + 1000 where name = &#x27;李四&#x27;;</span><br><span class="line"></span><br><span class="line">-- 查看事务提交方式</span><br><span class="line">SELECT @@AUTOCOMMIT;</span><br><span class="line">-- 设置事务提交方式，1为自动提交，0为手动提交，该设置只对当前会话有效</span><br><span class="line">SET @@AUTOCOMMIT = 0;</span><br><span class="line">-- 提交事务</span><br><span class="line">COMMIT;</span><br><span class="line">-- 回滚事务</span><br><span class="line">ROLLBACK;</span><br><span class="line"></span><br><span class="line">-- 设置手动提交后上面代码改为：</span><br><span class="line">select * from account where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money - 1000 where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money + 1000 where name = &#x27;李四&#x27;;</span><br><span class="line">commit;</span><br></pre></td></tr></table></figure><p>操作方式二：</p><p>开启事务：<br><code>START TRANSACTION 或 BEGIN TRANSACTION;</code><br>提交事务：<br><code>COMMIT;</code><br>回滚事务：<br><code>ROLLBACK;</code></p><p>操作实例：</p><figure class="highlight plaintext"><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">start transaction;</span><br><span class="line">select * from account where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money - 1000 where name = &#x27;张三&#x27;;</span><br><span class="line">update account set money = money + 1000 where name = &#x27;李四&#x27;;</span><br><span class="line">commit;</span><br></pre></td></tr></table></figure><h3 id="四大特性ACID"><a href="#四大特性ACID" class="headerlink" title="四大特性ACID"></a>四大特性ACID</h3><ul><li>原子性(Atomicity)：事务是不可分割的最小操作但愿，要么全部成功，要么全部失败</li><li>一致性(Consistency)：事务完成时，必须使所有数据都保持一致状态</li><li>隔离性(Isolation)：数据库系统提供的隔离机制，保证事务在不受外部并发操作影响的独立环境下运行</li><li>持久性(Durability)：事务一旦提交或回滚，它对数据库中的数据的改变就是永久的</li></ul><h3 id="并发事务"><a href="#并发事务" class="headerlink" title="并发事务"></a>并发事务</h3><table><thead><tr><th>问题</th><th>描述</th></tr></thead><tbody><tr><td>脏读</td><td>一个事务读到另一个事务还没提交的数据</td></tr><tr><td>不可重复读</td><td>一个事务先后读取同一条记录，但两次读取的数据不同</td></tr><tr><td>幻读</td><td>一个事务按照条件查询数据时，没有对应的数据行，但是再插入数据时，又发现这行数据已经存在</td></tr></tbody></table><blockquote><p>这三个问题的详细演示：<a href="https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55cd">https://www.bilibili.com/video/BV1Kr4y1i7ru?p=55cd</a> </p></blockquote><p>并发事务隔离级别：</p><table><thead><tr><th>隔离级别</th><th>脏读</th><th>不可重复读</th><th>幻读</th></tr></thead><tbody><tr><td>Read uncommitted</td><td>√</td><td>√</td><td>√</td></tr><tr><td>Read committed</td><td>×</td><td>√</td><td>√</td></tr><tr><td>Repeatable Read(默认)</td><td>×</td><td>×</td><td>√</td></tr><tr><td>Serializable</td><td>×</td><td>×</td><td>×</td></tr></tbody></table><ul><li>√表示在当前隔离级别下该问题会出现</li><li>Serializable 性能最低；Read uncommitted 性能最高，数据安全性最差</li></ul><p>查看事务隔离级别：<br><code>SELECT @@TRANSACTION_ISOLATION;</code><br>设置事务隔离级别：<br><code>SET [ SESSION | GLOBAL ] TRANSACTION ISOLATION LEVEL &#123;READ UNCOMMITTED | READ COMMITTED | REPEATABLE READ | SERIALIZABLE &#125;;</code><br>SESSION 是会话级别，表示只针对当前会话有效，GLOBAL 表示对所有会话有效</p><h1 id="进阶篇"><a href="#进阶篇" class="headerlink" title="进阶篇"></a>进阶篇</h1><h2 id="存储引擎"><a href="#存储引擎" class="headerlink" title="存储引擎"></a>存储引擎</h2><p>MySQL体系结构：</p><p><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/MySQL%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84_20220315034329549927.png" alt="结构图" title="结构图"><br><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/MySQL%E4%BD%93%E7%B3%BB%E7%BB%93%E6%9E%84%E5%B1%82%E7%BA%A7%E5%90%AB%E4%B9%89_20220315034359342837.png" alt="层级描述" title="层级描述"></p><p>存储引擎就是存储数据、建立索引、更新&#x2F;查询数据等技术的实现方式。存储引擎是基于表而不是基于库的，所以存储引擎也可以被称为表引擎。<br>默认存储引擎是InnoDB。</p><p>相关操作：</p><figure class="highlight plaintext"><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><br><span class="line">show create table account;</span><br><span class="line">-- 建表时指定存储引擎</span><br><span class="line">CREATE TABLE 表名(</span><br><span class="line">...</span><br><span class="line">) ENGINE=INNODB;</span><br><span class="line">-- 查看当前数据库支持的存储引擎</span><br><span class="line">show engines;</span><br></pre></td></tr></table></figure><h3 id="InnoDB"><a href="#InnoDB" class="headerlink" title="InnoDB"></a>InnoDB</h3><p>InnoDB 是一种兼顾高可靠性和高性能的通用存储引擎，在 MySQL 5.5 之后，InnoDB 是默认的 MySQL 引擎。</p><p>特点：</p><ul><li>DML 操作遵循 ACID 模型，支持<strong>事务</strong></li><li><strong>行级锁</strong>，提高并发访问性能</li><li>支持<strong>外键</strong>约束，保证数据的完整性和正确性</li></ul><p>文件：</p><ul><li>xxx.ibd: xxx代表表名，InnoDB 引擎的每张表都会对应这样一个表空间文件，存储该表的表结构（frm、sdi）、数据和索引。</li></ul><p>参数：innodb_file_per_table，决定多张表共享一个表空间还是每张表对应一个表空间</p><p>知识点：</p><p>查看 Mysql 变量：<br><code>show variables like &#39;innodb_file_per_table&#39;;</code></p><p>从idb文件提取表结构数据：<br>（在cmd运行）<br><code>ibd2sdi xxx.ibd</code></p><p>InnoDB 逻辑存储结构：<br><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/%E9%80%BB%E8%BE%91%E5%AD%98%E5%82%A8%E7%BB%93%E6%9E%84_20220316030616590001.png" alt="InnoDB逻辑存储结构" title="InnoDB逻辑存储结构"></p><h3 id="MyISAM"><a href="#MyISAM" class="headerlink" title="MyISAM"></a>MyISAM</h3><p>MyISAM 是 MySQL 早期的默认存储引擎。</p><p>特点：</p><ul><li>不支持事务，不支持外键</li><li>支持表锁，不支持行锁</li><li>访问速度快</li></ul><p>文件：</p><ul><li>xxx.sdi: 存储表结构信息</li><li>xxx.MYD: 存储数据</li><li>xxx.MYI: 存储索引</li></ul><h3 id="Memory"><a href="#Memory" class="headerlink" title="Memory"></a>Memory</h3><p>Memory 引擎的表数据是存储在内存中的，受硬件问题、断电问题的影响，只能将这些表作为临时表或缓存使用。</p><p>特点：</p><ul><li>存放在内存中，速度快</li><li>hash索引（默认）</li></ul><p>文件：</p><ul><li>xxx.sdi: 存储表结构信息</li></ul><h3 id="存储引擎特点"><a href="#存储引擎特点" class="headerlink" title="存储引擎特点"></a>存储引擎特点</h3><table><thead><tr><th>特点</th><th>InnoDB</th><th>MyISAM</th><th>Memory</th></tr></thead><tbody><tr><td>存储限制</td><td>64TB</td><td>有</td><td>有</td></tr><tr><td>事务安全</td><td>支持</td><td>-</td><td>-</td></tr><tr><td>锁机制</td><td>行锁</td><td>表锁</td><td>表锁</td></tr><tr><td>B+tree索引</td><td>支持</td><td>支持</td><td>支持</td></tr><tr><td>Hash索引</td><td>-</td><td>-</td><td>支持</td></tr><tr><td>全文索引</td><td>支持（5.6版本之后）</td><td>支持</td><td>-</td></tr><tr><td>空间使用</td><td>高</td><td>低</td><td>N&#x2F;A</td></tr><tr><td>内存使用</td><td>高</td><td>低</td><td>中等</td></tr><tr><td>批量插入速度</td><td>低</td><td>高</td><td>高</td></tr><tr><td>支持外键</td><td>支持</td><td>-</td><td>-</td></tr></tbody></table><h3 id="存储引擎的选择"><a href="#存储引擎的选择" class="headerlink" title="存储引擎的选择"></a>存储引擎的选择</h3><p>在选择存储引擎时，应该根据应用系统的特点选择合适的存储引擎。对于复杂的应用系统，还可以根据实际情况选择多种存储引擎进行组合。</p><ul><li>InnoDB: 如果应用对事物的完整性有比较高的要求，在并发条件下要求数据的一致性，数据操作除了插入和查询之外，还包含很多的更新、删除操作，则 InnoDB 是比较合适的选择</li><li>MyISAM: 如果应用是以读操作和插入操作为主，只有很少的更新和删除操作，并且对事务的完整性、并发性要求不高，那这个存储引擎是非常合适的。</li><li>Memory: 将所有数据保存在内存中，访问速度快，通常用于临时表及缓存。Memory 的缺陷是对表的大小有限制，太大的表无法缓存在内存中，而且无法保障数据的安全性</li></ul><p>电商中的足迹和评论适合使用 MyISAM 引擎，缓存适合使用 Memory 引擎。</p><h2 id="性能分析"><a href="#性能分析" class="headerlink" title="性能分析"></a>性能分析</h2><h3 id="查看执行频次"><a href="#查看执行频次" class="headerlink" title="查看执行频次"></a>查看执行频次</h3><p>查看当前数据库的 INSERT, UPDATE, DELETE, SELECT 访问频次：<br><code>SHOW GLOBAL STATUS LIKE &#39;Com_______&#39;;</code> 或者 <code>SHOW SESSION STATUS LIKE &#39;Com_______&#39;;</code><br>例：<code>show global status like &#39;Com_______&#39;</code></p><h3 id="慢查询日志"><a href="#慢查询日志" class="headerlink" title="慢查询日志"></a>慢查询日志</h3><p>慢查询日志记录了所有执行时间超过指定参数（long_query_time，单位：秒，默认10秒）的所有SQL语句的日志。<br>MySQL的慢查询日志默认没有开启，需要在MySQL的配置文件（&#x2F;etc&#x2F;my.cnf）中配置如下信息：<br>    # 开启慢查询日志开关<br>    slow_query_log&#x3D;1<br>    # 设置慢查询日志的时间为2秒，SQL语句执行时间超过2秒，就会视为慢查询，记录慢查询日志<br>    long_query_time&#x3D;2<br>更改后记得重启MySQL服务，日志文件位置：&#x2F;var&#x2F;lib&#x2F;mysql&#x2F;localhost-slow.log</p><p>查看慢查询日志开关状态：<br><code>show variables like &#39;slow_query_log&#39;;</code></p><h3 id="profile"><a href="#profile" class="headerlink" title="profile"></a>profile</h3><p>show profile 能在做SQL优化时帮我们了解时间都耗费在哪里。通过 have_profiling 参数，能看到当前 MySQL 是否支持 profile 操作：<br><code>SELECT @@have_profiling;</code><br>profiling 默认关闭，可以通过set语句在session&#x2F;global级别开启 profiling：<br><code>SET profiling = 1;</code><br>查看所有语句的耗时：<br><code>show profiles;</code><br>查看指定query_id的SQL语句各个阶段的耗时：<br><code>show profile for query query_id;</code><br>查看指定query_id的SQL语句CPU的使用情况<br><code>show profile cpu for query query_id;</code></p><h3 id="explain"><a href="#explain" class="headerlink" title="explain"></a>explain</h3><p>EXPLAIN 或者 DESC 命令获取 MySQL 如何执行 SELECT 语句的信息，包括在 SELECT 语句执行过程中表如何连接和连接的顺序。<br>语法：<br>    # 直接在select语句之前加上关键字 explain &#x2F; desc<br>    EXPLAIN SELECT 字段列表 FROM 表名 HWERE 条件;</p><p>EXPLAIN 各字段含义：</p><ul><li>id：select 查询的序列号，表示查询中执行 select 子句或者操作表的顺序（id相同，执行顺序从上到下；id不同，值越大越先执行）</li><li>select_type：表示 SELECT 的类型，常见取值有 SIMPLE（简单表，即不适用表连接或者子查询）、PRIMARY（主查询，即外层的查询）、UNION（UNION中的第二个或者后面的查询语句）、SUBQUERY（SELECT&#x2F;WHERE之后包含了子查询）等</li><li>type：表示连接类型，性能由好到差的连接类型为 NULL、system、const、eq_ref、ref、range、index、all</li><li>possible_key：可能应用在这张表上的索引，一个或多个</li><li>Key：实际使用的索引，如果为 NULL，则没有使用索引</li><li>Key_len：表示索引中使用的字节数，该值为索引字段最大可能长度，并非实际使用长度，在不损失精确性的前提下，长度越短越好</li><li>rows：MySQL认为必须要执行的行数，在InnoDB引擎的表中，是一个估计值，可能并不总是准确的</li><li>filtered：表示返回结果的行数占需读取行数的百分比，filtered的值越大越好</li></ul><h2 id="索引"><a href="#索引" class="headerlink" title="索引"></a>索引</h2><p>索引是帮助 MySQL <strong>高效获取数据</strong>的<strong>数据结构（有序）</strong>。在数据之外，数据库系统还维护着满足特定查找算法的数据结构，这些数据结构以某种方式引用（指向）数据，这样就可以在这些数据结构上实现高级查询算法，这种数据结构就是索引。</p><p>优缺点：</p><p>优点：</p><ul><li>提高数据检索效率，降低数据库的IO成本</li><li>通过索引列对数据进行排序，降低数据排序的成本，降低CPU的消耗</li></ul><p>缺点：</p><ul><li>索引列也是要占用空间的</li><li>索引大大提高了查询效率，但降低了更新的速度，比如 INSERT、UPDATE、DELETE</li></ul><h3 id="索引结构"><a href="#索引结构" class="headerlink" title="索引结构"></a>索引结构</h3><table><thead><tr><th>索引结构</th><th>描述</th></tr></thead><tbody><tr><td>B+Tree</td><td>最常见的索引类型，大部分引擎都支持B+树索引</td></tr><tr><td>Hash</td><td>底层数据结构是用哈希表实现，只有精确匹配索引列的查询才有效，不支持范围查询</td></tr><tr><td>R-Tree(空间索引)</td><td>空间索引是 MyISAM 引擎的一个特殊索引类型，主要用于地理空间数据类型，通常使用较少</td></tr><tr><td>Full-Text(全文索引)</td><td>是一种通过建立倒排索引，快速匹配文档的方式，类似于 Lucene, Solr, ES</td></tr></tbody></table><table><thead><tr><th>索引</th><th>InnoDB</th><th>MyISAM</th><th>Memory</th></tr></thead><tbody><tr><td>B+Tree索引</td><td>支持</td><td>支持</td><td>支持</td></tr><tr><td>Hash索引</td><td>不支持</td><td>不支持</td><td>支持</td></tr><tr><td>R-Tree索引</td><td>不支持</td><td>支持</td><td>不支持</td></tr><tr><td>Full-text</td><td>5.6版本后支持</td><td>支持</td><td>不支持</td></tr></tbody></table><h4 id="B-Tree"><a href="#B-Tree" class="headerlink" title="B-Tree"></a>B-Tree</h4><p><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/%E4%BA%8C%E5%8F%89%E6%A0%91_20220316153214227108.png" alt="二叉树" title="二叉树"></p><p>二叉树的缺点可以用红黑树来解决：<br><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/%E7%BA%A2%E9%BB%91%E6%A0%91_20220316163142686602.png" alt="红黑树" title="红黑树"><br>红黑树也存在大数据量情况下，层级较深，检索速度慢的问题。</p><p>为了解决上述问题，可以使用 B-Tree 结构。<br>B-Tree (多路平衡查找树) 以一棵最大度数（max-degree，指一个节点的子节点个数）为5（5阶）的 b-tree 为例（每个节点最多存储4个key，5个指针）</p><p><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/B-Tree%E7%BB%93%E6%9E%84_20220316163813441163.png" alt="B-Tree结构" title="B-Tree结构"></p><blockquote><p>B-Tree 的数据插入过程动画参照：<a href="https://www.bilibili.com/video/BV1Kr4y1i7ru?p=68">https://www.bilibili.com/video/BV1Kr4y1i7ru?p=68</a><br>演示地址：<a href="https://www.cs.usfca.edu/~galles/visualization/BTree.html">https://www.cs.usfca.edu/~galles/visualization/BTree.html</a></p></blockquote><h4 id="B-Tree-1"><a href="#B-Tree-1" class="headerlink" title="B+Tree"></a>B+Tree</h4><p>结构图：</p><p><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/B+Tree%E7%BB%93%E6%9E%84%E5%9B%BE_20220316170700591277.png" alt="B+Tree结构图" title="B+Tree结构图"></p><blockquote><p>演示地址：<a href="https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html">https://www.cs.usfca.edu/~galles/visualization/BPlusTree.html</a></p></blockquote><p>与 B-Tree 的区别：</p><ul><li>所有的数据都会出现在叶子节点</li><li>叶子节点形成一个单向链表</li></ul><p>MySQL 索引数据结构对经典的 B+Tree 进行了优化。在原 B+Tree 的基础上，增加一个指向相邻叶子节点的链表指针，就形成了带有顺序指针的 B+Tree，提高区间访问的性能。</p><p><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/%E7%BB%93%E6%9E%84%E5%9B%BE_20220316171730865611.png" alt="MySQL B+Tree 结构图" title="MySQL B+Tree 结构图"></p><h4 id="Hash"><a href="#Hash" class="headerlink" title="Hash"></a>Hash</h4><p>哈希索引就是采用一定的hash算法，将键值换算成新的hash值，映射到对应的槽位上，然后存储在hash表中。<br>如果两个（或多个）键值，映射到一个相同的槽位上，他们就产生了hash冲突（也称为hash碰撞），可以通过链表来解决。</p><p><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/Hash%E7%B4%A2%E5%BC%95%E5%8E%9F%E7%90%86%E5%9B%BE_20220317143226150679.png" alt="Hash索引原理图" title="Hash索引原理图"></p><p>特点：</p><ul><li>Hash索引只能用于对等比较（&#x3D;、in），不支持范围查询（betwwn、&gt;、&lt;、…）</li><li>无法利用索引完成排序操作</li><li>查询效率高，通常只需要一次检索就可以了，效率通常要高于 B+Tree 索引</li></ul><p>存储引擎支持：</p><ul><li>Memory</li><li>InnoDB: 具有自适应hash功能，hash索引是存储引擎根据 B+Tree 索引在指定条件下自动构建的</li></ul><h4 id="面试题"><a href="#面试题" class="headerlink" title="面试题"></a>面试题</h4><ol><li>为什么 InnoDB 存储引擎选择使用 B+Tree 索引结构？</li></ol><ul><li>相对于二叉树，层级更少，搜索效率高</li><li>对于 B-Tree，无论是叶子节点还是非叶子节点，都会保存数据，这样导致一页中存储的键值减少，指针也跟着减少，要同样保存大量数据，只能增加树的高度，导致性能降低</li><li>相对于 Hash 索引，B+Tree 支持范围匹配及排序操作</li></ul><h3 id="索引分类"><a href="#索引分类" class="headerlink" title="索引分类"></a>索引分类</h3><table><thead><tr><th>分类</th><th>含义</th><th>特点</th><th>关键字</th></tr></thead><tbody><tr><td>主键索引</td><td>针对于表中主键创建的索引</td><td>默认自动创建，只能有一个</td><td>PRIMARY</td></tr><tr><td>唯一索引</td><td>避免同一个表中某数据列中的值重复</td><td>可以有多个</td><td>UNIQUE</td></tr><tr><td>常规索引</td><td>快速定位特定数据</td><td>可以有多个</td><td></td></tr><tr><td>全文索引</td><td>全文索引查找的是文本中的关键词，而不是比较索引中的值</td><td>可以有多个</td><td>FULLTEXT</td></tr></tbody></table><p>在 InnoDB 存储引擎中，根据索引的存储形式，又可以分为以下两种：</p><table><thead><tr><th>分类</th><th>含义</th><th>特点</th></tr></thead><tbody><tr><td>聚集索引(Clustered Index)</td><td>将数据存储与索引放一块，索引结构的叶子节点保存了行数据</td><td>必须有，而且只有一个</td></tr><tr><td>二级索引(Secondary Index)</td><td>将数据与索引分开存储，索引结构的叶子节点关联的是对应的主键</td><td>可以存在多个</td></tr></tbody></table><p>演示图：</p><p><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/%E5%8E%9F%E7%90%86%E5%9B%BE_20220318194454880073.png" alt="大致原理" title="大致原理"><br><img src="https://learning-logs-1253130399.cos.ap-guangzhou.myqcloud.com/editor/%E6%BC%94%E7%A4%BA%E5%9B%BE_20220319215403721066.png" alt="演示图" title="演示图"></p><p>聚集索引选取规则：</p><ul><li>如果存在主键，主键索引就是聚集索引</li><li>如果不存在主键，将使用第一个唯一(UNIQUE)索引作为聚集索引</li><li>如果表没有主键或没有合适的唯一索引，则 InnoDB 会自动生成一个 rowid 作为隐藏的聚集索引</li></ul><h4 id="思考题"><a href="#思考题" class="headerlink" title="思考题"></a>思考题</h4><p>1. 以下 SQL 语句，哪个执行效率高？为什么？</p><figure class="highlight plaintext"><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">select * from user where id = 10;</span><br><span class="line">select * from user where name = &#x27;Arm&#x27;;</span><br><span class="line">-- 备注：id为主键，name字段创建的有索引</span><br></pre></td></tr></table></figure><p>答：第一条语句，因为第二条需要回表查询，相当于两个步骤。</p><p>2. InnoDB 主键索引的 B+Tree 高度为多少？</p><p>答：假设一行数据大小为1k，一页中可以存储16行这样的数据。InnoDB 的指针占用6个字节的空间，主键假设为bigint，占用字节数为8.<br>可得公式：<code>n * 8 + (n + 1) * 6 = 16 * 1024</code>，其中 8 表示 bigint 占用的字节数，n 表示当前节点存储的key的数量，(n + 1) 表示指针数量（比key多一个）。算出n约为1170。</p><p>如果树的高度为2，那么他能存储的数据量大概为：<code>1171 * 16 = 18736</code>；<br>如果树的高度为3，那么他能存储的数据量大概为：<code>1171 * 1171 * 16 = 21939856</code>。</p><p>另外，如果有成千上万的数据，那么就要考虑分表，涉及运维篇知识。</p><h3 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h3><p>创建索引：<br><code>CREATE [ UNIQUE | FULLTEXT ] INDEX index_name ON table_name (index_col_name, ...);</code><br>如果不加 CREATE 后面不加索引类型参数，则创建的是常规索引</p><p>查看索引：<br><code>SHOW INDEX FROM table_name;</code></p><p>删除索引：<br><code>DROP INDEX index_name ON table_name;</code></p><p>案例：</p><figure class="highlight plaintext"><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">-- name字段为姓名字段，该字段的值可能会重复，为该字段创建索引</span><br><span class="line">create index idx_user_name on tb_user(name);</span><br><span class="line">-- phone手机号字段的值非空，且唯一，为该字段创建唯一索引</span><br><span class="line">create unique index idx_user_phone on tb_user (phone);</span><br><span class="line">-- 为profession, age, status创建联合索引</span><br><span class="line">create index idx_user_pro_age_stat on tb_user(profession, age, status);</span><br><span class="line">-- 为email建立合适的索引来提升查询效率</span><br><span class="line">create index idx_user_email on tb_user(email);</span><br><span class="line"></span><br><span class="line">-- 删除索引</span><br><span class="line">drop index idx_user_email on tb_user;</span><br></pre></td></tr></table></figure><h3 id="使用规则"><a href="#使用规则" class="headerlink" title="使用规则"></a>使用规则</h3><h4 id="最左前缀法则"><a href="#最左前缀法则" class="headerlink" title="最左前缀法则"></a>最左前缀法则</h4><p>如果索引关联了多列（联合索引），要遵守最左前缀法则，最左前缀法则指的是查询从索引的最左列开始，并且不跳过索引中的列。<br>如果跳跃某一列，索引将部分失效（后面的字段索引失效）。</p><p>联合索引中，出现范围查询（&lt;, &gt;），范围查询右侧的列索引失效。可以用&gt;&#x3D;或者&lt;&#x3D;来规避索引失效问题。</p><h4 id="索引失效情况"><a href="#索引失效情况" class="headerlink" title="索引失效情况"></a>索引失效情况</h4><ol><li>在索引列上进行运算操作，索引将失效。如：<code>explain select * from tb_user where substring(phone, 10, 2) = &#39;15&#39;;</code></li><li>字符串类型字段使用时，不加引号，索引将失效。如：<code>explain select * from tb_user where phone = 17799990015;</code>，此处phone的值没有加引号</li><li>模糊查询中，如果仅仅是尾部模糊匹配，索引不会是失效；如果是头部模糊匹配，索引失效。如：<code>explain select * from tb_user where profession like &#39;%工程&#39;;</code>，前后都有 % 也会失效。</li><li>用 or 分割开的条件，如果 or 其中一个条件的列没有索引，那么涉及的索引都不会被用到。</li><li>如果 MySQL 评估使用索引比全表更慢，则不使用索引。</li></ol><h4 id="SQL-提示"><a href="#SQL-提示" class="headerlink" title="SQL 提示"></a>SQL 提示</h4><p>是优化数据库的一个重要手段，简单来说，就是在SQL语句中加入一些人为的提示来达到优化操作的目的。</p><p>例如，使用索引：<br><code>explain select * from tb_user use index(idx_user_pro) where profession=&quot;软件工程&quot;;</code><br>不使用哪个索引：<br><code>explain select * from tb_user ignore index(idx_user_pro) where profession=&quot;软件工程&quot;;</code><br>必须使用哪个索引：<br><code>explain select * from tb_user force index(idx_user_pro) where profession=&quot;软件工程&quot;;</code></p><p>use 是建议，实际使用哪个索引 MySQL 还会自己权衡运行速度去更改，force就是无论如何都强制使用该索引。</p><h4 id="覆盖索引-回表查询"><a href="#覆盖索引-回表查询" class="headerlink" title="覆盖索引&amp;回表查询"></a>覆盖索引&amp;回表查询</h4><p>尽量使用覆盖索引（查询使用了索引，并且需要返回的列，在该索引中已经全部能找到），减少 select *。</p><p>explain 中 extra 字段含义：<br><code>using index condition</code>：查找使用了索引，但是需要回表查询数据<br><code>using where; using index;</code>：查找使用了索引，但是需要的数据都在索引列中能找到，所以不需要回表查询</p><p>如果在聚集索引中直接能找到对应的行，则直接返回行数据，只需要一次查询，哪怕是select *；如果在辅助索引中找聚集索引，如<code>select id, name from xxx where name=&#39;xxx&#39;;</code>，也只需要通过辅助索引(name)查找到对应的id，返回name和name索引对应的id即可，只需要一次查询；如果是通过辅助索引查找其他字段，则需要回表查询，如<code>select id, name, gender from xxx where name=&#39;xxx&#39;;</code></p><p>所以尽量不要用<code>select *</code>，容易出现回表查询，降低效率，除非有联合索引包含了所有字段</p><p>面试题：一张表，有四个字段（id, username, password, status），由于数据量大，需要对以下SQL语句进行优化，该如何进行才是最优方案：<br><code>select id, username, password from tb_user where username=&#39;itcast&#39;;</code></p><p>解：给username和password字段建立联合索引，则不需要回表查询，直接覆盖索引</p><h4 id="前缀索引"><a href="#前缀索引" class="headerlink" title="前缀索引"></a>前缀索引</h4><p>当字段类型为字符串（varchar, text等）时，有时候需要索引很长的字符串，这会让索引变得很大，查询时，浪费大量的磁盘IO，影响查询效率，此时可以只降字符串的一部分前缀，建立索引，这样可以大大节约索引空间，从而提高索引效率。</p><p>语法：<code>create index idx_xxxx on table_name(columnn(n));</code><br>前缀长度：可以根据索引的选择性来决定，而选择性是指不重复的索引值（基数）和数据表的记录总数的比值，索引选择性越高则查询效率越高，唯一索引的选择性是1，这是最好的索引选择性，性能也是最好的。<br>求选择性公式：</p><figure class="highlight plaintext"><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">select count(distinct email) / count(*) from tb_user;</span><br><span class="line">select count(distinct substring(email, 1, 5)) / count(*) from tb_user;</span><br></pre></td></tr></table></figure><p>show index 里面的sub_part可以看到接取的长度</p><h4 id="单列索引-联合索引"><a href="#单列索引-联合索引" class="headerlink" title="单列索引&amp;联合索引"></a>单列索引&amp;联合索引</h4><p>单列索引：即一个索引只包含单个列<br>联合索引：即一个索引包含了多个列<br>在业务场景中，如果存在多个查询条件，考虑针对于查询字段建立索引时，建议建立联合索引，而非单列索引。</p><p>单列索引情况：<br><code>explain select id, phone, name from tb_user where phone = &#39;17799990010&#39; and name = &#39;韩信&#39;;</code><br>这句只会用到phone索引字段</p><h5 id="注意事项-8"><a href="#注意事项-8" class="headerlink" title="注意事项"></a>注意事项</h5><ul><li>多条件联合查询时，MySQL优化器会评估哪个字段的索引效率更高，会选择该索引完成本次查询</li></ul><h3 id="设计原则"><a href="#设计原则" class="headerlink" title="设计原则"></a>设计原则</h3><ol><li>针对于数据量较大，且查询比较频繁的表建立索引</li><li>针对于常作为查询条件（where）、排序（order by）、分组（group by）操作的字段建立索引</li><li>尽量选择区分度高的列作为索引，尽量建立唯一索引，区分度越高，使用索引的效率越高</li><li>如果是字符串类型的字段，字段长度较长，可以针对于字段的特点，建立前缀索引</li><li>尽量使用联合索引，减少单列索引，查询时，联合索引很多时候可以覆盖索引，节省存储空间，避免回表，提高查询效率</li><li>要控制索引的数量，索引并不是多多益善，索引越多，维护索引结构的代价就越大，会影响增删改的效率</li><li>如果索引列不能存储NULL值，请在创建表时使用NOT NULL约束它。当优化器知道每列是否包含NULL值时，它可以更好地确定哪个索引最有效地用于查询</li></ol><h2 id="SQL-优化"><a href="#SQL-优化" class="headerlink" title="SQL 优化"></a>SQL 优化</h2><h3 id="插入数据"><a href="#插入数据" class="headerlink" title="插入数据"></a>插入数据</h3><p>普通插入：</p><ol><li>采用批量插入（一次插入的数据不建议超过1000条）</li><li>手动提交事务</li><li>主键顺序插入</li></ol><p>大批量插入：<br>如果一次性需要插入大批量数据，使用insert语句插入性能较低，此时可以使用MySQL数据库提供的load指令插入。</p><figure class="highlight plaintext"><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"># 客户端连接服务端时，加上参数 --local-infile（这一行在bash/cmd界面输入）</span><br><span class="line">mysql --local-infile -u root -p</span><br><span class="line"># 设置全局参数local_infile为1，开启从本地加载文件导入数据的开关</span><br><span class="line">set global local_infile = 1;</span><br><span class="line">select @@local_infile;</span><br><span class="line"># 执行load指令将准备好的数据，加载到表结构中</span><br><span class="line">load data local infile &#x27;/root/sql1.log&#x27; into table &#x27;tb_user&#x27; fields terminated by &#x27;,&#x27; lines terminated by &#x27;\n&#x27;;</span><br></pre></td></tr></table></figure><h3 id="主键优化"><a href="#主键优化" class="headerlink" title="主键优化"></a>主键优化</h3><p>数据组织方式：在InnoDB存储引擎中，表数据都是根据主键顺序组织存放的，这种存储方式的表称为索引组织表（Index organized table, IOT）</p><p>页分裂：页可以为空，也可以填充一般，也可以填充100%，每个页包含了2-N行数据（如果一行数据过大，会行溢出），根据主键排列。<br>页合并：当删除一行记录时，实际上记录并没有被物理删除，只是记录被标记（flaged）为删除并且它的空间变得允许被其他记录声明使用。当页中删除的记录到达 MERGE_THRESHOLD（默认为页的50%），InnoDB会开始寻找最靠近的页（前后）看看是否可以将这两个页合并以优化空间使用。</p><p>MERGE_THRESHOLD：合并页的阈值，可以自己设置，在创建表或创建索引时指定</p><blockquote><p>文字说明不够清晰明了，具体可以看视频里的PPT演示过程：<a href="https://www.bilibili.com/video/BV1Kr4y1i7ru?p=90">https://www.bilibili.com/video/BV1Kr4y1i7ru?p=90</a></p></blockquote><p>主键设计原则：</p><ul><li>满足业务需求的情况下，尽量降低主键的长度</li><li>插入数据时，尽量选择顺序插入，选择使用 AUTO_INCREMENT 自增主键</li><li>尽量不要使用 UUID 做主键或者是其他的自然主键，如身份证号</li><li>业务操作时，避免对主键的修改</li></ul><h3 id="order-by优化"><a href="#order-by优化" class="headerlink" title="order by优化"></a>order by优化</h3><ol><li>Using filesort：通过表的索引或全表扫描，读取满足条件的数据行，然后在排序缓冲区 sort buffer 中完成排序操作，所有不是通过索引直接返回排序结果的排序都叫 FileSort 排序</li><li>Using index：通过有序索引顺序扫描直接返回有序数据，这种情况即为 using index，不需要额外排序，操作效率高</li></ol><p>如果order by字段全部使用升序排序或者降序排序，则都会走索引，但是如果一个字段升序排序，另一个字段降序排序，则不会走索引，explain的extra信息显示的是<code>Using index, Using filesort</code>，如果要优化掉Using filesort，则需要另外再创建一个索引，如：<code>create index idx_user_age_phone_ad on tb_user(age asc, phone desc);</code>，此时使用<code>select id, age, phone from tb_user order by age asc, phone desc;</code>会全部走索引</p><p>总结：</p><ul><li>根据排序字段建立合适的索引，多字段排序时，也遵循最左前缀法则</li><li>尽量使用覆盖索引</li><li>多字段排序，一个升序一个降序，此时需要注意联合索引在创建时的规则（ASC&#x2F;DESC）</li><li>如果不可避免出现filesort，大数据量排序时，可以适当增大排序缓冲区大小 sort_buffer_size（默认256k）</li></ul><h3 id="group-by优化"><a href="#group-by优化" class="headerlink" title="group by优化"></a>group by优化</h3><ul><li>在分组操作时，可以通过索引来提高效率</li><li>分组操作时，索引的使用也是满足最左前缀法则的</li></ul><p>如索引为<code>idx_user_pro_age_stat</code>，则句式可以是<code>select ... where profession order by age</code>，这样也符合最左前缀法则</p><h3 id="limit优化"><a href="#limit优化" class="headerlink" title="limit优化"></a>limit优化</h3><p>常见的问题如<code>limit 2000000, 10</code>，此时需要 MySQL 排序前2000000条记录，但仅仅返回2000000 - 2000010的记录，其他记录丢弃，查询排序的代价非常大。<br>优化方案：一般分页查询时，通过创建覆盖索引能够比较好地提高性能，可以通过覆盖索引加子查询形式进行优化</p><p>例如：</p><figure class="highlight plaintext"><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><br><span class="line">select * from tb_sku limit 9000000, 10;</span><br><span class="line">-- 通过覆盖索引加快速度，直接通过主键索引进行排序及查询</span><br><span class="line">select id from tb_sku order by id limit 9000000, 10;</span><br><span class="line">-- 下面的语句是错误的，因为 MySQL 不支持 in 里面使用 limit</span><br><span class="line">-- select * from tb_sku where id in (select id from tb_sku order by id limit 9000000, 10);</span><br><span class="line">-- 通过连表查询即可实现第一句的效果，并且能达到第二句的速度</span><br><span class="line">select * from tb_sku as s, (select id from tb_sku order by id limit 9000000, 10) as a where s.id = a.id;</span><br></pre></td></tr></table></figure><h3 id="count优化"><a href="#count优化" class="headerlink" title="count优化"></a>count优化</h3><p>MyISAM 引擎把一个表的总行数存在了磁盘上，因此执行 count(*) 的时候会直接返回这个数，效率很高（前提是不适用where）；<br>InnoDB 在执行 count(*) 时，需要把数据一行一行地从引擎里面读出来，然后累计计数。<br>优化方案：自己计数，如创建key-value表存储在内存或硬盘，或者是用redis</p><p>count的几种用法：</p><ul><li>如果count函数的参数（count里面写的那个字段）不是NULL（字段值不为NULL），累计值就加一，最后返回累计值</li><li>用法：count(*)、count(主键)、count(字段)、count(1)</li><li>count(主键)跟count(*)一样，因为主键不能为空；count(字段)只计算字段值不为NULL的行；count(1)引擎会为每行添加一个1，然后就count这个1，返回结果也跟count(*)一样；count(null)返回0</li></ul><p>各种用法的性能：</p><ul><li>count(主键)：InnoDB引擎会遍历整张表，把每行的主键id值都取出来，返回给服务层，服务层拿到主键后，直接按行进行累加（主键不可能为空）</li><li>count(字段)：没有not null约束的话，InnoDB引擎会遍历整张表把每一行的字段值都取出来，返回给服务层，服务层判断是否为null，不为null，计数累加；有not null约束的话，InnoDB引擎会遍历整张表把每一行的字段值都取出来，返回给服务层，直接按行进行累加</li><li>count(1)：InnoDB 引擎遍历整张表，但不取值。服务层对于返回的每一层，放一个数字 1 进去，直接按行进行累加</li><li>count(*)：InnoDB 引擎并不会把全部字段取出来，而是专门做了优化，不取值，服务层直接按行进行累加</li></ul><p>按效率排序：count(字段) &lt; count(主键) &lt; count(1) &lt; count(*)，所以尽量使用 count(*)</p><h3 id="update优化（避免行锁升级为表锁）"><a href="#update优化（避免行锁升级为表锁）" class="headerlink" title="update优化（避免行锁升级为表锁）"></a>update优化（避免行锁升级为表锁）</h3><p>InnoDB 的行锁是针对索引加的锁，不是针对记录加的锁，并且该索引不能失效，否则会从行锁升级为表锁。</p><p>如以下两条语句：<br><code>update student set no = &#39;123&#39; where id = 1;</code>，这句由于id有主键索引，所以只会锁这一行；<br><code>update student set no = &#39;123&#39; where name = &#39;test&#39;;</code>，这句由于name没有索引，所以会把整张表都锁住进行数据更新，解决方法是给name字段添加索引</p><h1 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h1><h2 id="整型"><a href="#整型" class="headerlink" title="整型"></a>整型</h2><table><thead><tr><th>类型名称</th><th>取值范围</th><th>大小</th></tr></thead><tbody><tr><td>TINYINT</td><td>-128〜127</td><td>1个字节</td></tr><tr><td>SMALLINT</td><td>-32768〜32767</td><td>2个宇节</td></tr><tr><td>MEDIUMINT</td><td>-8388608〜8388607</td><td>3个字节</td></tr><tr><td>INT (INTEGHR)</td><td>-2147483648〜2147483647</td><td>4个字节</td></tr><tr><td>BIGINT</td><td>-9223372036854775808〜9223372036854775807</td><td>8个字节</td></tr></tbody></table><p>无符号在数据类型后加 unsigned 关键字。</p><h2 id="浮点型"><a href="#浮点型" class="headerlink" title="浮点型"></a>浮点型</h2><table><thead><tr><th>类型名称</th><th>说明</th><th>存储需求</th></tr></thead><tbody><tr><td>FLOAT</td><td>单精度浮点数</td><td>4 个字节</td></tr><tr><td>DOUBLE</td><td>双精度浮点数</td><td>8 个字节</td></tr><tr><td>DECIMAL (M, D)，DEC</td><td>压缩的“严格”定点数</td><td>M+2 个字节</td></tr></tbody></table><h2 id="日期和时间"><a href="#日期和时间" class="headerlink" title="日期和时间"></a>日期和时间</h2><table><thead><tr><th>类型名称</th><th>日期格式</th><th>日期范围</th><th>存储需求</th></tr></thead><tbody><tr><td>YEAR</td><td>YYYY</td><td>1901 ~ 2155</td><td>1 个字节</td></tr><tr><td>TIME</td><td>HH:MM:SS</td><td>-838:59:59 ~ 838:59:59</td><td>3 个字节</td></tr><tr><td>DATE</td><td>YYYY-MM-DD</td><td>1000-01-01 ~ 9999-12-3</td><td>3 个字节</td></tr><tr><td>DATETIME</td><td>YYYY-MM-DD HH:MM:SS</td><td>1000-01-01 00:00:00 ~ 9999-12-31 23:59:59</td><td>8 个字节</td></tr><tr><td>TIMESTAMP</td><td>YYYY-MM-DD HH:MM:SS</td><td>1980-01-01 00:00:01 UTC ~ 2040-01-19 03:14:07 UTC</td><td>4 个字节</td></tr></tbody></table><h2 id="字符串"><a href="#字符串" class="headerlink" title="字符串"></a>字符串</h2><table><thead><tr><th>类型名称</th><th>说明</th><th>存储需求</th></tr></thead><tbody><tr><td>CHAR(M)</td><td>固定长度非二进制字符串</td><td>M 字节，1&lt;&#x3D;M&lt;&#x3D;255</td></tr><tr><td>VARCHAR(M)</td><td>变长非二进制字符串</td><td>L+1字节，在此，L&lt; &#x3D; M和 1&lt;&#x3D;M&lt;&#x3D;255</td></tr><tr><td>TINYTEXT</td><td>非常小的非二进制字符串</td><td>L+1字节，在此，L&lt;2^8</td></tr><tr><td>TEXT</td><td>小的非二进制字符串</td><td>L+2字节，在此，L&lt;2^16</td></tr><tr><td>MEDIUMTEXT</td><td>中等大小的非二进制字符串</td><td>L+3字节，在此，L&lt;2^24</td></tr><tr><td>LONGTEXT</td><td>大的非二进制字符串</td><td>L+4字节，在此，L&lt;2^32</td></tr><tr><td>ENUM</td><td>枚举类型，只能有一个枚举字符串值</td><td>1或2个字节，取决于枚举值的数目 (最大值为65535)</td></tr><tr><td>SET</td><td>一个设置，字符串对象可以有零个或 多个SET成员</td><td>1、2、3、4或8个字节，取决于集合 成员的数量（最多64个成员）</td></tr></tbody></table><h2 id="二进制类型"><a href="#二进制类型" class="headerlink" title="二进制类型"></a>二进制类型</h2><table><thead><tr><th>类型名称</th><th>说明</th><th>存储需求</th></tr></thead><tbody><tr><td>BIT(M)</td><td>位字段类型</td><td>大约 (M+7)&#x2F;8 字节</td></tr><tr><td>BINARY(M)</td><td>固定长度二进制字符串</td><td>M 字节</td></tr><tr><td>VARBINARY (M)</td><td>可变长度二进制字符串</td><td>M+1 字节</td></tr><tr><td>TINYBLOB (M)</td><td>非常小的BLOB</td><td>L+1 字节，在此，L&lt;2^8</td></tr><tr><td>BLOB (M)</td><td>小 BLOB</td><td>L+2 字节，在此，L&lt;2^16</td></tr><tr><td>MEDIUMBLOB (M)</td><td>中等大小的BLOB</td><td>L+3 字节，在此，L&lt;2^24</td></tr><tr><td>LONGBLOB (M)</td><td>非常大的BLOB</td><td>L+4 字节，在此，L&lt;2^32</td></tr></tbody></table><h1 id="权限一览表"><a href="#权限一览表" class="headerlink" title="权限一览表"></a>权限一览表</h1><blockquote><p>具体权限的作用详见<a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html" title="官方文档">官方文档</a></p></blockquote><p>GRANT 和 REVOKE 允许的静态权限</p><table><thead><tr><th align="left">Privilege</th><th align="left">Grant Table Column</th><th align="left">Context</th></tr></thead><tbody><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_all"><code>ALL [PRIVILEGES]</code></a></td><td align="left">Synonym for “all privileges”</td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_alter"><code>ALTER</code></a></td><td align="left"><code>Alter_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_alter-routine"><code>ALTER ROUTINE</code></a></td><td align="left"><code>Alter_routine_priv</code></td><td align="left">Stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create"><code>CREATE</code></a></td><td align="left"><code>Create_priv</code></td><td align="left">Databases, tables, or indexes</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-role"><code>CREATE ROLE</code></a></td><td align="left"><code>Create_role_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-routine"><code>CREATE ROUTINE</code></a></td><td align="left"><code>Create_routine_priv</code></td><td align="left">Stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-tablespace"><code>CREATE TABLESPACE</code></a></td><td align="left"><code>Create_tablespace_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-temporary-tables"><code>CREATE TEMPORARY TABLES</code></a></td><td align="left"><code>Create_tmp_table_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-user"><code>CREATE USER</code></a></td><td align="left"><code>Create_user_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_create-view"><code>CREATE VIEW</code></a></td><td align="left"><code>Create_view_priv</code></td><td align="left">Views</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_delete"><code>DELETE</code></a></td><td align="left"><code>Delete_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_drop"><code>DROP</code></a></td><td align="left"><code>Drop_priv</code></td><td align="left">Databases, tables, or views</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_drop-role"><code>DROP ROLE</code></a></td><td align="left"><code>Drop_role_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_event"><code>EVENT</code></a></td><td align="left"><code>Event_priv</code></td><td align="left">Databases</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_execute"><code>EXECUTE</code></a></td><td align="left"><code>Execute_priv</code></td><td align="left">Stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_file"><code>FILE</code></a></td><td align="left"><code>File_priv</code></td><td align="left">File access on server host</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_grant-option"><code>GRANT OPTION</code></a></td><td align="left"><code>Grant_priv</code></td><td align="left">Databases, tables, or stored routines</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_index"><code>INDEX</code></a></td><td align="left"><code>Index_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_insert"><code>INSERT</code></a></td><td align="left"><code>Insert_priv</code></td><td align="left">Tables or columns</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_lock-tables"><code>LOCK TABLES</code></a></td><td align="left"><code>Lock_tables_priv</code></td><td align="left">Databases</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_process"><code>PROCESS</code></a></td><td align="left"><code>Process_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_proxy"><code>PROXY</code></a></td><td align="left">See <code>proxies_priv</code> table</td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_references"><code>REFERENCES</code></a></td><td align="left"><code>References_priv</code></td><td align="left">Databases or tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_reload"><code>RELOAD</code></a></td><td align="left"><code>Reload_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-client"><code>REPLICATION CLIENT</code></a></td><td align="left"><code>Repl_client_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-slave"><code>REPLICATION SLAVE</code></a></td><td align="left"><code>Repl_slave_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_select"><code>SELECT</code></a></td><td align="left"><code>Select_priv</code></td><td align="left">Tables or columns</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_show-databases"><code>SHOW DATABASES</code></a></td><td align="left"><code>Show_db_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_show-view"><code>SHOW VIEW</code></a></td><td align="left"><code>Show_view_priv</code></td><td align="left">Views</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_shutdown"><code>SHUTDOWN</code></a></td><td align="left"><code>Shutdown_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_super"><code>SUPER</code></a></td><td align="left"><code>Super_priv</code></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_trigger"><code>TRIGGER</code></a></td><td align="left"><code>Trigger_priv</code></td><td align="left">Tables</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_update"><code>UPDATE</code></a></td><td align="left"><code>Update_priv</code></td><td align="left">Tables or columns</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_usage"><code>USAGE</code></a></td><td align="left">Synonym for “no privileges”</td><td align="left">Server administration</td></tr></tbody></table><p>GRANT 和 REVOKE 允许的动态权限</p><table><thead><tr><th align="left">Privilege</th><th align="left">Context</th></tr></thead><tbody><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_application-password-admin"><code>APPLICATION_PASSWORD_ADMIN</code></a></td><td align="left">Dual password administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_audit-abort-exempt"><code>AUDIT_ABORT_EXEMPT</code></a></td><td align="left">Allow queries blocked by audit log filter</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_audit-admin"><code>AUDIT_ADMIN</code></a></td><td align="left">Audit log administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_authentication-policy-admin"><code>AUTHENTICATION_POLICY_ADMIN</code></a></td><td align="left">Authentication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_backup-admin"><code>BACKUP_ADMIN</code></a></td><td align="left">Backup administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_binlog-admin"><code>BINLOG_ADMIN</code></a></td><td align="left">Backup and Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_binlog-encryption-admin"><code>BINLOG_ENCRYPTION_ADMIN</code></a></td><td align="left">Backup and Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_clone-admin"><code>CLONE_ADMIN</code></a></td><td align="left">Clone administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_connection-admin"><code>CONNECTION_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_encryption-key-admin"><code>ENCRYPTION_KEY_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_firewall-admin"><code>FIREWALL_ADMIN</code></a></td><td align="left">Firewall administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_firewall-exempt"><code>FIREWALL_EXEMPT</code></a></td><td align="left">Firewall administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_firewall-user"><code>FIREWALL_USER</code></a></td><td align="left">Firewall administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-optimizer-costs"><code>FLUSH_OPTIMIZER_COSTS</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-status"><code>FLUSH_STATUS</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-tables"><code>FLUSH_TABLES</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_flush-user-resources"><code>FLUSH_USER_RESOURCES</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_group-replication-admin"><code>GROUP_REPLICATION_ADMIN</code></a></td><td align="left">Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_group-replication-stream"><code>GROUP_REPLICATION_STREAM</code></a></td><td align="left">Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_innodb-redo-log-archive"><code>INNODB_REDO_LOG_ARCHIVE</code></a></td><td align="left">Redo log archiving administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_ndb-stored-user"><code>NDB_STORED_USER</code></a></td><td align="left">NDB Cluster</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_passwordless-user-admin"><code>PASSWORDLESS_USER_ADMIN</code></a></td><td align="left">Authentication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_persist-ro-variables-admin"><code>PERSIST_RO_VARIABLES_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-applier"><code>REPLICATION_APPLIER</code></a></td><td align="left"><code>PRIVILEGE_CHECKS_USER</code> for a replication channel</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_replication-slave-admin"><code>REPLICATION_SLAVE_ADMIN</code></a></td><td align="left">Replication administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_resource-group-admin"><code>RESOURCE_GROUP_ADMIN</code></a></td><td align="left">Resource group administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_resource-group-user"><code>RESOURCE_GROUP_USER</code></a></td><td align="left">Resource group administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_role-admin"><code>ROLE_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_session-variables-admin"><code>SESSION_VARIABLES_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_set-user-id"><code>SET_USER_ID</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_show-routine"><code>SHOW_ROUTINE</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_system-user"><code>SYSTEM_USER</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_system-variables-admin"><code>SYSTEM_VARIABLES_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_table-encryption-admin"><code>TABLE_ENCRYPTION_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_version-token-admin"><code>VERSION_TOKEN_ADMIN</code></a></td><td align="left">Server administration</td></tr><tr><td align="left"><a href="https://dev.mysql.com/doc/refman/8.0/en/privileges-provided.html#priv_xa-recover-admin"><code>XA_RECOVER_ADMIN</code></a></td><td align="left">Server administration</td></tr></tbody></table><h1 id="图形化界面工具"><a href="#图形化界面工具" class="headerlink" title="图形化界面工具"></a>图形化界面工具</h1><ul><li>Workbench(免费): <a href="http://dev.mysql.com/downloads/workbench/">http://dev.mysql.com/downloads/workbench/</a></li><li>navicat(收费，试用版30天): <a href="https://www.navicat.com/en/download/navicat-for-mysql">https://www.navicat.com/en/download/navicat-for-mysql</a></li><li>Sequel Pro(开源免费，仅支持Mac OS): <a href="http://www.sequelpro.com/">http://www.sequelpro.com/</a></li><li>HeidiSQL(免费): <a href="http://www.heidisql.com/">http://www.heidisql.com/</a></li><li>phpMyAdmin(免费): <a href="https://www.phpmyadmin.net/">https://www.phpmyadmin.net/</a></li><li>SQLyog: <a href="https://sqlyog.en.softonic.com/">https://sqlyog.en.softonic.com/</a></li></ul><h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><h1 id="小技巧"><a href="#小技巧" class="headerlink" title="小技巧"></a>小技巧</h1><ol><li>在SQL语句之后加上<code>\G</code>会将结果的表格形式转换成行文本形式</li><li>查看Mysql数据库占用空间：</li></ol><figure class="highlight plaintext"><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">SELECT table_schema &quot;Database Name&quot;</span><br><span class="line">     , SUM(data_length + index_length) / (1024 * 1024) &quot;Database Size in MB&quot;</span><br><span class="line">FROM information_schema.TABLES</span><br><span class="line">GROUP BY table_schema;</span><br></pre></td></tr></table></figure><h1 id="后续内容"><a href="#后续内容" class="headerlink" title="后续内容"></a>后续内容</h1><p>后续内容因为跟当前学习、工作计划有冲突，所以后续课程的学习计划会无限期推后。<br>目前的工作重点放在重做一个学习笔记网站，当然这是边做边学的，开发过程中遇到的难点和知识点我也会记录下来供大家学习。<br><strong>在此感谢B站同样爱学习的同学 @守心-人 提供的后续课程笔记，大家有条件一定要去给个star，你的每一个star和点赞都是我们前进的动力</strong><br><strong><a href="https://github.com/Buildings-Lei/mysql_note/blob/main/README.md">https://github.com/Buildings-Lei/mysql_note&#x2F;blob&#x2F;main&#x2F;README.md</a></strong></p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;声明：本页面文字完全转载，仅供个人学习使用，如有侵权请联系！&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;基础篇&quot;&gt;&lt;a href=&quot;#基础篇&quot; class=&quot;headerlink&quot; title=&quot;基础篇&quot;&gt;&lt;/a&gt;基础篇&lt;/h1&gt;&lt;h2</summary>
        
      
    
    
    
    <category term="Mysql" scheme="https://www.wotemo.com/categories/Mysql/"/>
    
    <category term="Database" scheme="https://www.wotemo.com/categories/Mysql/Database/"/>
    
    
    <category term="Mysql" scheme="https://www.wotemo.com/tags/Mysql/"/>
    
    <category term="Database" scheme="https://www.wotemo.com/tags/Database/"/>
    
  </entry>
  
  <entry>
    <title>生日快乐，沃特陌</title>
    <link href="https://www.wotemo.com/posts/6927.html"/>
    <id>https://www.wotemo.com/posts/6927.html</id>
    <published>2023-10-04T16:00:00.000Z</published>
    <updated>2023-10-04T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="Happy-Birthday-To-Wotemo"><a href="#Happy-Birthday-To-Wotemo" class="headerlink" title="Happy Birthday To Wotemo"></a><a href="https://birthday.wotemo.com/">Happy Birthday To Wotemo</a></h1><p>These pages are designed to celebrate <strong>Wotemo’s birthday</strong>, which is on <strong>10.05</strong>.</p><p>If you visit this <a href="https://birthday.wotemo.com/">website</a> on October 5th, you can enter directly without entering a password.</p><p>Or ask Wotemo to get the password.</p><p>Hope getting all of your blessings.</p><p>By the way, there is a comment function at the bottom of the page.</p><p>😘😘😘</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h1 id=&quot;Happy-Birthday-To-Wotemo&quot;&gt;&lt;a href=&quot;#Happy-Birthday-To-Wotemo&quot; class=&quot;headerlink&quot; title=&quot;Happy Birthday To Wotemo&quot;&gt;&lt;/a&gt;&lt;a</summary>
        
      
    
    
    
    
    <category term="生日" scheme="https://www.wotemo.com/tags/%E7%94%9F%E6%97%A5/"/>
    
    <category term="沃特陌" scheme="https://www.wotemo.com/tags/%E6%B2%83%E7%89%B9%E9%99%8C/"/>
    
  </entry>
  
  <entry>
    <title>使用AI为我们自动生成PPT</title>
    <link href="https://www.wotemo.com/posts/7641.html"/>
    <id>https://www.wotemo.com/posts/7641.html</id>
    <published>2023-09-25T15:33:45.000Z</published>
    <updated>2023-09-25T15:33:45.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>随着人工智障，哦不，人工智能的发展，PPT也不用我们做了😁</p></blockquote><p>下面，我将为大家介绍两款AI自动生成PPT的<strong>Office插件</strong></p><p>（事实上，这两款插件是配合起来使用的）</p><h1 id="一-ChatPPT"><a href="#一-ChatPPT" class="headerlink" title="一. ChatPPT"></a>一. ChatPPT</h1><p><img src="https://img.wotemo.com/img/chatppt_index.png" alt="ChatPPT"></p><h2 id="1-下载ChatPPT安装包"><a href="#1-下载ChatPPT安装包" class="headerlink" title="1. 下载ChatPPT安装包"></a>1. 下载ChatPPT安装包</h2><p>在<a href="https://chat-ppt.com/">ChatPPT</a>官网直接点击下载即可</p><p>（记得不要修改安装路径，否则可能会出现插件在office中显示不出来的问题）</p><h2 id="2-打开Office-PowerPoint"><a href="#2-打开Office-PowerPoint" class="headerlink" title="2. 打开Office PowerPoint"></a>2. 打开Office PowerPoint</h2><p>新建一个空白文档，你就能在上方看见ChatPPT（包括Motion Go，如果你勾选了Motion Go的话）</p><h1 id="二-Motion-Go"><a href="#二-Motion-Go" class="headerlink" title="二. Motion Go"></a>二. Motion Go</h1><p><img src="https://img.wotemo.com/img/motiongo_index.png" alt="MotionGo"></p><h2 id="1-下载Motion-Go安装包"><a href="#1-下载Motion-Go安装包" class="headerlink" title="1. 下载Motion Go安装包"></a>1. 下载Motion Go安装包</h2><p>在<a href="http://motion.yoo-ai.com/">Motion Go</a>官网直接点击下载即可</p><p>（记得不要修改安装路径，否则可能会出现插件在office中显示不出来的问题）</p><h2 id="2-打开Office-PowerPoint-1"><a href="#2-打开Office-PowerPoint-1" class="headerlink" title="2. 打开Office PowerPoint"></a>2. 打开Office PowerPoint</h2><p>新建一个空白文档，你就能在上方看见Motion Go（包括ChatPPT）</p><p><img src="https://img.wotemo.com/img/word_motion_go_chatppt.png" alt="WordMotionGoChatPPT"></p><h1 id="三-使用方法"><a href="#三-使用方法" class="headerlink" title="三. 使用方法"></a>三. 使用方法</h1><h2 id="1-调教ChatGPT或文心一言"><a href="#1-调教ChatGPT或文心一言" class="headerlink" title="1. 调教ChatGPT或文心一言"></a>1. 调教ChatGPT或文心一言</h2><p><a href="https://github.com/PlexPt/awesome-chatgpt-prompts-zh">Github</a>上有专门的调教方法，如</p><h4 id="充当论文润色者"><a href="#充当论文润色者" class="headerlink" title="充当论文润色者"></a>充当论文润色者</h4><blockquote><p>请你充当一名论文编辑专家，在论文评审的角度去修改论文摘要部分，使其更加流畅，优美。下面是具体要求：</p></blockquote><ol><li>能让读者快速获得文章的要点或精髓，让文章引人入胜；能让读者了解全文中的重要信息、分析和论点；帮助读者记住论文的要点</li><li>字数限制在300字以下</li><li>请你在摘要中明确指出您的模型和方法的创新点，强调您的贡献。</li><li>用简洁、明了的语言描述您的方法和结果，以便评审更容易理解论文</li></ol><blockquote><p>下文是论文的摘要部分，请你修改它：xxxxx</p></blockquote><p><img src="https://img.wotemo.com/img/plexpt.png" alt="plexpt"></p><p>让文心一言给你写一个PPT大纲</p><p>我：</p><p><code>你给我写一个人工智能行业分析的ppt大纲</code></p><p>文心一言：</p><p><code>第一页</code><br><code>标题：人工智能行业概述</code><br><code>内容：</code></p><ol><li><code>人工智能的定义和历史发展</code></li><li><code>人工智能的技术基础和应用领域</code></li><li><code>人工智能在当今社会中的重要性和应用价值</code></li></ol><p><code>第二页</code><br><code>标题：人工智能技术基础</code><br><code>内容：</code></p><ol><li><code>人工智能中的机器学习、深度学习和自然语言处理等技术</code></li><li><code>人工智能技术应用的现状和未来的发展趋势</code></li><li><code>人工智能技术在自动化和智能化方面的应用前景</code></li></ol><p><code>第三页</code><br><code>标题：人工智能应用领域</code><br><code>内容：</code></p><ol><li><code>人工智能在工业制造领域的应用</code></li><li><code>人工智能在智能客服和智能推荐系统中的应用</code></li><li><code>人工智能在医疗、金融、农业等各个领域的应用</code></li></ol><p><code>第四页</code><br><code>标题：人工智能产业链分析</code><br><code>内容：</code></p><ol><li><code>人工智能产业链的构成和各环节发展情况</code></li><li><code>人工智能产业中的主要企业和产品</code></li><li><code>人工智能产业发展的趋势和未来展望</code></li></ol><p><code>第五页</code><br><code>标题：人工智能技术发展与市场趋势</code><br><code>内容：</code></p><ol><li><code>人工智能技术的发展趋势及主要技术流派</code></li><li><code>人工智能市场的发展趋势和未来发展方向</code></li><li><code>人工智能技术在全球范围内的竞争格局和发展态势</code></li></ol><p><code>第六页</code><br><code>标题：人工智能应用场景和案例分析</code><br><code>内容：</code></p><ol><li><code>人工智能应用的主要场景及其应用效果</code></li><li><code>人工智能应用案例的分析和启示</code></li><li><code>人工智能技术在企业中的应用和推广前景</code></li></ol><p><code>第七页</code><br><code>标题：人工智能发展面临的挑战和机遇</code><br><code>内容：</code></p><ol><li><code>人工智能发展面临的挑战和难点</code></li><li><code>人工智能技术发展所带来的机遇和前景</code></li><li><code>企业和个人如何把握和利用人工智能技术的发展趋势和应用前景</code></li></ol><p><code>第八页</code><br><code>标题：结语</code><br><code>内容：</code></p><ol><li><code>总结人工智能技术的发展趋势和应用前景</code></li><li><code>对企业和个人如何把握和利用人工智能技术的发展提出建议和展望。</code></li></ol><h2 id="2-与ChatPPT交流"><a href="#2-与ChatPPT交流" class="headerlink" title="2. 与ChatPPT交流"></a>2. 与ChatPPT交流</h2><p>根据你的需求相应的让文心一言生成对应的PPT大纲，然后将这个大纲转述给ChatPPT即可</p><h2 id="3-使用Motion-Go进行优化"><a href="#3-使用Motion-Go进行优化" class="headerlink" title="3. 使用Motion Go进行优化"></a>3. 使用Motion Go进行优化</h2><p>Motion Go有许多丰富的插件，在ChatPPT生成完PPT后，你可以使用Motion Go进行更精细的调整</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;随着人工智障，哦不，人工智能的发展，PPT也不用我们做了😁&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;下面，我将为大家介绍两款AI自动生成PPT的&lt;strong&gt;Office插件&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;（事实上，这两款插件是配合起来使用</summary>
        
      
    
    
    
    <category term="AI" scheme="https://www.wotemo.com/categories/AI/"/>
    
    <category term="PPT" scheme="https://www.wotemo.com/categories/AI/PPT/"/>
    
    
    <category term="AI" scheme="https://www.wotemo.com/tags/AI/"/>
    
    <category term="PPT" scheme="https://www.wotemo.com/tags/PPT/"/>
    
    <category term="Motion Go" scheme="https://www.wotemo.com/tags/Motion-Go/"/>
    
    <category term="ChatPPT" scheme="https://www.wotemo.com/tags/ChatPPT/"/>
    
    <category term="ChatGPT" scheme="https://www.wotemo.com/tags/ChatGPT/"/>
    
    <category term="文心一言" scheme="https://www.wotemo.com/tags/%E6%96%87%E5%BF%83%E4%B8%80%E8%A8%80/"/>
    
  </entry>
  
  <entry>
    <title>深入链表，将链表进行升序排列</title>
    <link href="https://www.wotemo.com/posts/33503.html"/>
    <id>https://www.wotemo.com/posts/33503.html</id>
    <published>2023-09-23T08:15:33.000Z</published>
    <updated>2023-09-23T08:15:33.000Z</updated>
    
    <content type="html"><![CDATA[<p>历经数天磨练（Bug快把我侵蚀），我终于将这款<strong>将链表合并然后进行升序排列程序</strong>做出来啦~撒花✿✿ヽ(°▽°)ノ✿</p><h3 id="一-代码组成"><a href="#一-代码组成" class="headerlink" title="一.代码组成"></a>一.代码组成</h3><h4 id="本代码是两个程序"><a href="#本代码是两个程序" class="headerlink" title="本代码是两个程序"></a>本代码是两个程序</h4><ol><li>第一个程序是先将两个单链表合并，然后再对链表进行插入排序</li><li>第二个程序是先将两个单链表进行排序，然后再将两个有序单链表进行合并</li></ol><p>两个程序演示如下（仔细看，有区别）：</p><p><img src="https://img.wotemo.com/img/lianbiao3.png" alt="单链表排序"></p><p><img src="https://img.wotemo.com/img/lianbiao4.png" alt="有序链表排序"></p><h3 id="二-代码部分"><a href="#二-代码部分" class="headerlink" title="二.代码部分"></a>二.代码部分</h3><h4 id="话不多说，上代码："><a href="#话不多说，上代码：" class="headerlink" title="话不多说，上代码："></a>话不多说，上代码：</h4><h4 id="第一个程序：单链表排序合并-cpp"><a href="#第一个程序：单链表排序合并-cpp" class="headerlink" title="第一个程序：单链表排序合并.cpp"></a>第一个程序：单链表排序合并.cpp</h4><h5 id="第一部分"><a href="#第一部分" class="headerlink" title="第一部分"></a>第一部分</h5><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个链表节点的结构体</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> &#123;</span><br><span class="line">    <span class="type">int</span> num;          <span class="comment">// 存储整数数据</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* next; <span class="comment">// 指向下一个节点的指针</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h5 id="第二部分"><a href="#第二部分" class="headerlink" title="第二部分"></a>第二部分</h5><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建链表的函数</span></span><br><span class="line"><span class="function">node* <span class="title">create</span><span class="params">(<span class="type">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * tail = <span class="literal">nullptr</span>, * tmp = <span class="literal">nullptr</span>;</span><br><span class="line">    node* list[<span class="number">10000</span>] = &#123; <span class="literal">nullptr</span> &#125;; <span class="comment">// 存储链表节点的数组，最多支持10000个节点</span></span><br><span class="line">    <span class="type">int</span> int_num = <span class="number">1</span>, i = <span class="number">0</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;请输入链表,以0结尾:&quot;</span> &lt;&lt; endl;</span><br><span class="line">    cin &gt;&gt; int_num;</span><br><span class="line">    <span class="keyword">while</span> (int_num != <span class="number">0</span>) &#123;</span><br><span class="line">        list[i] = <span class="keyword">new</span> <span class="built_in">node</span>(&#123; int_num, <span class="literal">nullptr</span> &#125;); <span class="comment">// 创建新节点并存储数据</span></span><br><span class="line">        cin &gt;&gt; int_num;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt;= i - <span class="number">2</span>; j++) &#123;</span><br><span class="line">        list[j]-&gt;next = list[j + <span class="number">1</span>]; <span class="comment">// 链接节点，构建链表</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> list[<span class="number">0</span>]; <span class="comment">// 返回链表的头节点指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第三部分"><a href="#第三部分" class="headerlink" title="第三部分"></a>第三部分</h5><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">// 输出链表内容的函数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(node* list0)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list0 == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;链表为空&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (list0 != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; list0-&gt;num &lt;&lt; <span class="string">&quot; &quot;</span>; <span class="comment">// 输出节点的数据</span></span><br><span class="line">        list0 = list0-&gt;next; <span class="comment">// 移动到下一个节点</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第四部分"><a href="#第四部分" class="headerlink" title="第四部分"></a>第四部分</h5><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="function">node* <span class="title">merge</span><span class="params">(node* list1, node* list2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list1 == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list2; <span class="comment">// 如果第一个链表为空，直接返回第二个链表</span></span><br><span class="line">    &#125;</span><br><span class="line">    node* list_sum = list1;</span><br><span class="line">    <span class="keyword">while</span> (list1-&gt;next != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        list1 = list1-&gt;next; <span class="comment">// 移动到第一个链表的最后一个节点</span></span><br><span class="line">    &#125;</span><br><span class="line">    list1-&gt;next = list2; <span class="comment">// 将第二个链表连接到第一个链表的尾部</span></span><br><span class="line">    <span class="keyword">return</span> list_sum; <span class="comment">// 返回合并后的链表头节点指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第五部分"><a href="#第五部分" class="headerlink" title="第五部分"></a>第五部分</h5><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用插入排序对链表进行升序排序的函数（自己写的）</span></span><br><span class="line"><span class="function">node* <span class="title">sort_myself</span><span class="params">(node* list12)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list12 == <span class="literal">nullptr</span> || list12-&gt;next == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list12; <span class="comment">// 如果链表为空或只有一个节点，直接返回</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * curr = <span class="literal">nullptr</span>;</span><br><span class="line">    curr = list12;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (curr != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        node* next = curr-&gt;next;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">nullptr</span> || curr-&gt;num &lt; head-&gt;num) &#123;</span><br><span class="line">            curr-&gt;next = head;</span><br><span class="line">            head = curr; <span class="comment">// 插入当前节点到已排序部分的合适位置</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">struct</span> node* prev = head;</span><br><span class="line">            <span class="keyword">while</span> (prev-&gt;next != <span class="literal">nullptr</span> &amp;&amp; curr-&gt;num &gt; prev-&gt;next-&gt;num) &#123;</span><br><span class="line">                prev = prev-&gt;next; <span class="comment">// 找到合适的插入位置</span></span><br><span class="line">            &#125;</span><br><span class="line">            curr-&gt;next = prev-&gt;next;</span><br><span class="line">            prev-&gt;next = curr; <span class="comment">// 插入当前节点</span></span><br><span class="line">        &#125;</span><br><span class="line">        curr = next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head; <span class="comment">// 返回排序后的链表头节点指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第六部分"><a href="#第六部分" class="headerlink" title="第六部分"></a>第六部分</h5><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用插入排序对链表进行升序排序的函数（ChatGPT写的）</span></span><br><span class="line"><span class="function">node* <span class="title">sort_chatgpt</span><span class="params">(node* list12)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list12 == <span class="literal">nullptr</span> || list12-&gt;next == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list12; <span class="comment">// 如果链表为空或只有一个节点，直接返回</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* curr = list12;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (curr != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">node</span>* next = curr-&gt;next;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">nullptr</span> || curr-&gt;num &lt; head-&gt;num) &#123;</span><br><span class="line">            curr-&gt;next = head;</span><br><span class="line">            head = curr; <span class="comment">// 插入当前节点到已排序部分的合适位置</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">struct</span> node* prev = head;</span><br><span class="line">            <span class="keyword">while</span> (prev-&gt;next != <span class="literal">nullptr</span> &amp;&amp; curr-&gt;num &gt; prev-&gt;next-&gt;num) &#123;</span><br><span class="line">                prev = prev-&gt;next; <span class="comment">// 找到合适的插入位置</span></span><br><span class="line">            &#125;</span><br><span class="line">            curr-&gt;next = prev-&gt;next;</span><br><span class="line">            prev-&gt;next = curr; <span class="comment">// 插入当前节点</span></span><br><span class="line">        &#125;</span><br><span class="line">        curr = next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head; <span class="comment">// 返回排序后的链表头节点指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第七部分"><a href="#第七部分" class="headerlink" title="第七部分"></a>第七部分</h5><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="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;声明：\n链表数据个数上限为10000\n还有，数不要输太大，不然会整数溢出&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list1 = <span class="built_in">create</span>();</span><br><span class="line">    node* list2 = <span class="built_in">create</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;第一个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list1);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;第二个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list2);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;将两链表合并：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list12 = <span class="built_in">merge</span>(list1, list2);</span><br><span class="line">    <span class="built_in">output</span>(list12);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;将链表以升序排列：&quot;</span> &lt;&lt; endl &lt;&lt; <span class="string">&quot;我写的：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list_all_myself = <span class="built_in">sort_myself</span>(list12);</span><br><span class="line">    <span class="built_in">output</span>(list_all_myself);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;ChatGPT写的：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list_all_gpt = <span class="built_in">sort_chatgpt</span>(list12);</span><br><span class="line">    <span class="built_in">output</span>(list_all_gpt);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="全部代码"><a href="#全部代码" class="headerlink" title="全部代码"></a>全部代码</h5><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个链表节点的结构体</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> &#123;</span><br><span class="line">    <span class="type">int</span> num;          <span class="comment">// 存储整数数据</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* next; <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="function">node* <span class="title">create</span><span class="params">(<span class="type">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * tail = <span class="literal">nullptr</span>, * tmp = <span class="literal">nullptr</span>;</span><br><span class="line">    node* list[<span class="number">10000</span>] = &#123; <span class="literal">nullptr</span> &#125;; <span class="comment">// 存储链表节点的数组，最多支持10000个节点</span></span><br><span class="line">    <span class="type">int</span> int_num = <span class="number">1</span>, i = <span class="number">0</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;请输入链表,以0结尾:&quot;</span> &lt;&lt; endl;</span><br><span class="line">    cin &gt;&gt; int_num;</span><br><span class="line">    <span class="keyword">while</span> (int_num != <span class="number">0</span>) &#123;</span><br><span class="line">        list[i] = <span class="keyword">new</span> <span class="built_in">node</span>(&#123; int_num, <span class="literal">nullptr</span> &#125;); <span class="comment">// 创建新节点并存储数据</span></span><br><span class="line">        cin &gt;&gt; int_num;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt;= i - <span class="number">2</span>; j++) &#123;</span><br><span class="line">        list[j]-&gt;next = list[j + <span class="number">1</span>]; <span class="comment">// 链接节点，构建链表</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> list[<span class="number">0</span>]; <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="function"><span class="type">void</span> <span class="title">output</span><span class="params">(node* list0)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list0 == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;链表为空&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (list0 != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; list0-&gt;num &lt;&lt; <span class="string">&quot; &quot;</span>; <span class="comment">// 输出节点的数据</span></span><br><span class="line">        list0 = list0-&gt;next; <span class="comment">// 移动到下一个节点</span></span><br><span class="line">    &#125;</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="comment">// 合并两个链表的函数</span></span><br><span class="line"><span class="function">node* <span class="title">merge</span><span class="params">(node* list1, node* list2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list1 == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list2; <span class="comment">// 如果第一个链表为空，直接返回第二个链表</span></span><br><span class="line">    &#125;</span><br><span class="line">    node* list_sum = list1;</span><br><span class="line">    <span class="keyword">while</span> (list1-&gt;next != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        list1 = list1-&gt;next; <span class="comment">// 移动到第一个链表的最后一个节点</span></span><br><span class="line">    &#125;</span><br><span class="line">    list1-&gt;next = list2; <span class="comment">// 将第二个链表连接到第一个链表的尾部</span></span><br><span class="line">    <span class="keyword">return</span> list_sum; <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="function">node* <span class="title">sort_myself</span><span class="params">(node* list12)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list12 == <span class="literal">nullptr</span> || list12-&gt;next == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list12; <span class="comment">// 如果链表为空或只有一个节点，直接返回</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * curr = <span class="literal">nullptr</span>;</span><br><span class="line">    curr = list12;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (curr != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        node* next = curr-&gt;next;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">nullptr</span> || curr-&gt;num &lt; head-&gt;num) &#123;</span><br><span class="line">            curr-&gt;next = head;</span><br><span class="line">            head = curr; <span class="comment">// 插入当前节点到已排序部分的合适位置</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">struct</span> node* prev = head;</span><br><span class="line">            <span class="keyword">while</span> (prev-&gt;next != <span class="literal">nullptr</span> &amp;&amp; curr-&gt;num &gt; prev-&gt;next-&gt;num) &#123;</span><br><span class="line">                prev = prev-&gt;next; <span class="comment">// 找到合适的插入位置</span></span><br><span class="line">            &#125;</span><br><span class="line">            curr-&gt;next = prev-&gt;next;</span><br><span class="line">            prev-&gt;next = curr; <span class="comment">// 插入当前节点</span></span><br><span class="line">        &#125;</span><br><span class="line">        curr = next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head; <span class="comment">// 返回排序后的链表头节点指针</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用插入排序对链表进行升序排序的函数（ChatGPT写的）</span></span><br><span class="line"><span class="function">node* <span class="title">sort_chatgpt</span><span class="params">(node* list12)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list12 == <span class="literal">nullptr</span> || list12-&gt;next == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list12; <span class="comment">// 如果链表为空或只有一个节点，直接返回</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* curr = list12;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (curr != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">node</span>* next = curr-&gt;next;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">nullptr</span> || curr-&gt;num &lt; head-&gt;num) &#123;</span><br><span class="line">            curr-&gt;next = head;</span><br><span class="line">            head = curr; <span class="comment">// 插入当前节点到已排序部分的合适位置</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">struct</span> node* prev = head;</span><br><span class="line">            <span class="keyword">while</span> (prev-&gt;next != <span class="literal">nullptr</span> &amp;&amp; curr-&gt;num &gt; prev-&gt;next-&gt;num) &#123;</span><br><span class="line">                prev = prev-&gt;next; <span class="comment">// 找到合适的插入位置</span></span><br><span class="line">            &#125;</span><br><span class="line">            curr-&gt;next = prev-&gt;next;</span><br><span class="line">            prev-&gt;next = curr; <span class="comment">// 插入当前节点</span></span><br><span class="line">        &#125;</span><br><span class="line">        curr = next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head; <span class="comment">// 返回排序后的链表头节点指针</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;声明：\n链表数据个数上限为10000\n还有，数不要输太大，不然会整数溢出&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list1 = <span class="built_in">create</span>();</span><br><span class="line">    node* list2 = <span class="built_in">create</span>();</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;第一个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list1);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;第二个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list2);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;将两链表合并：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list12 = <span class="built_in">merge</span>(list1, list2);</span><br><span class="line">    <span class="built_in">output</span>(list12);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;将链表以升序排列：&quot;</span> &lt;&lt; endl &lt;&lt; <span class="string">&quot;我写的：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list_all_myself = <span class="built_in">sort_myself</span>(list12);</span><br><span class="line">    <span class="built_in">output</span>(list_all_myself);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;ChatGPT写的：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list_all_gpt = <span class="built_in">sort_chatgpt</span>(list12);</span><br><span class="line">    <span class="built_in">output</span>(list_all_gpt);</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="第二个程序：两个有序链表排序合并-cpp"><a href="#第二个程序：两个有序链表排序合并-cpp" class="headerlink" title="第二个程序：两个有序链表排序合并.cpp"></a>第二个程序：两个有序链表排序合并.cpp</h4><h5 id="第一部分-1"><a href="#第一部分-1" class="headerlink" title="第一部分"></a>第一部分</h5><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="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个链表节点的结构体</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> &#123;</span><br><span class="line">    <span class="type">int</span> num;          <span class="comment">// 存储整数数据</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* next; <span class="comment">// 指向下一个节点的指针</span></span><br><span class="line">&#125;;</span><br></pre></td></tr></table></figure><h5 id="第二部分-1"><a href="#第二部分-1" class="headerlink" title="第二部分"></a>第二部分</h5><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建链表的函数</span></span><br><span class="line"><span class="function">node* <span class="title">create</span><span class="params">(<span class="type">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * tail = <span class="literal">nullptr</span>, * tmp = <span class="literal">nullptr</span>;</span><br><span class="line">    node* list[<span class="number">10000</span>] = &#123; <span class="literal">nullptr</span> &#125;; <span class="comment">// 存储链表节点的数组，最多支持10000个节点</span></span><br><span class="line">    <span class="type">int</span> int_num = <span class="number">1</span>, i = <span class="number">0</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;请输入链表,以0结尾:&quot;</span> &lt;&lt; endl;</span><br><span class="line">    cin &gt;&gt; int_num;</span><br><span class="line">    <span class="keyword">while</span> (int_num != <span class="number">0</span>) &#123;</span><br><span class="line">        list[i] = <span class="keyword">new</span> <span class="built_in">node</span>(&#123; int_num, <span class="literal">nullptr</span> &#125;); <span class="comment">// 创建新节点并存储数据</span></span><br><span class="line">        cin &gt;&gt; int_num;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt;= i - <span class="number">2</span>; j++) &#123;</span><br><span class="line">        list[j]-&gt;next = list[j + <span class="number">1</span>]; <span class="comment">// 链接节点，构建链表</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> list[<span class="number">0</span>]; <span class="comment">// 返回链表的头节点指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第三部分-1"><a href="#第三部分-1" class="headerlink" title="第三部分"></a>第三部分</h5><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">// 输出链表内容的函数</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">output</span><span class="params">(node* list0)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list0 == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;链表为空&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (list0 != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; list0-&gt;num &lt;&lt; <span class="string">&quot; &quot;</span>; <span class="comment">// 输出节点的数据</span></span><br><span class="line">        list0 = list0-&gt;next; <span class="comment">// 移动到下一个节点</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第四部分-1"><a href="#第四部分-1" class="headerlink" title="第四部分"></a>第四部分</h5><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 使用插入排序对链表进行升序排序的函数</span></span><br><span class="line"><span class="function">node* <span class="title">sort</span><span class="params">(node* list12)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list12 == <span class="literal">nullptr</span> || list12-&gt;next == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list12; <span class="comment">// 如果链表为空或只有一个节点，直接返回</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * curr = <span class="literal">nullptr</span>;</span><br><span class="line">    curr = list12;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (curr != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">node</span>* next = curr-&gt;next;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">nullptr</span> || curr-&gt;num &lt; head-&gt;num) &#123;</span><br><span class="line">            curr-&gt;next = head;</span><br><span class="line">            head = curr; <span class="comment">// 插入当前节点到已排序部分的合适位置</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">struct</span> node* prev = head;</span><br><span class="line">            <span class="keyword">while</span> (prev-&gt;next != <span class="literal">nullptr</span> &amp;&amp; curr-&gt;num &gt; prev-&gt;next-&gt;num) &#123;</span><br><span class="line">                prev = prev-&gt;next; <span class="comment">// 找到合适的插入位置</span></span><br><span class="line">            &#125;</span><br><span class="line">            curr-&gt;next = prev-&gt;next;</span><br><span class="line">            prev-&gt;next = curr; <span class="comment">// 插入当前节点</span></span><br><span class="line">        &#125;</span><br><span class="line">        curr = next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head; <span class="comment">// 返回排序后的链表头节点指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第五部分-1"><a href="#第五部分-1" class="headerlink" title="第五部分"></a>第五部分</h5><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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 合并两个已排序链表的函数</span></span><br><span class="line"><span class="function">node* <span class="title">merge</span><span class="params">(node* list1, node* list2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * tail = <span class="literal">nullptr</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 选择头节点</span></span><br><span class="line">    <span class="keyword">if</span> (list1-&gt;num &lt;= list2-&gt;num) &#123;</span><br><span class="line">        head = list1;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        head = list2;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    tail = head; <span class="comment">// 初始化尾节点</span></span><br><span class="line"></span><br><span class="line">    list1 = list1-&gt;next;</span><br><span class="line">    list2 = list2-&gt;next;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历两个链表并合并</span></span><br><span class="line">    <span class="keyword">while</span> (list1 &amp;&amp; list2) &#123;</span><br><span class="line">        <span class="keyword">if</span> (list1-&gt;num &lt;= list2-&gt;num) &#123;</span><br><span class="line">            tail-&gt;next = list1;</span><br><span class="line">            tail = list1;</span><br><span class="line">            list1 = list1-&gt;next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            tail-&gt;next = list2;</span><br><span class="line">            tail = list2;</span><br><span class="line">            list2 = list2-&gt;next;</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">while</span> (list1) &#123;</span><br><span class="line">        tail-&gt;next = list1;</span><br><span class="line">        tail = list1;</span><br><span class="line">        list1 = list1-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (list2) &#123;</span><br><span class="line">        tail-&gt;next = list2;</span><br><span class="line">        tail = list2;</span><br><span class="line">        list2 = list2-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> head; <span class="comment">// 返回合并后的链表头节点指针</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="第六部分-1"><a href="#第六部分-1" class="headerlink" title="第六部分"></a>第六部分</h5><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="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;声明：\n链表数据个数上限为10000\n还有，数不要输太大，不然会整数溢出&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list1 = <span class="built_in">sort</span>(<span class="built_in">create</span>()); <span class="comment">// 创建并排序第一个链表</span></span><br><span class="line">    node* list2 = <span class="built_in">sort</span>(<span class="built_in">create</span>()); <span class="comment">// 创建并排序第二个链表</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;第一个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list1);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;第二个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list2);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;将两个链表合并，然后按升序排列：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(<span class="built_in">merge</span>(list1, list2));</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h5 id="全部代码-1"><a href="#全部代码-1" class="headerlink" title="全部代码"></a>全部代码</h5><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><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></pre></td><td class="code"><pre><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 定义一个链表节点的结构体</span></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span> &#123;</span><br><span class="line">    <span class="type">int</span> num;          <span class="comment">// 存储整数数据</span></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* next; <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="function">node* <span class="title">create</span><span class="params">(<span class="type">void</span>)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * tail = <span class="literal">nullptr</span>, * tmp = <span class="literal">nullptr</span>;</span><br><span class="line">    node* list[<span class="number">10000</span>] = &#123; <span class="literal">nullptr</span> &#125;; <span class="comment">// 存储链表节点的数组，最多支持10000个节点</span></span><br><span class="line">    <span class="type">int</span> int_num = <span class="number">1</span>, i = <span class="number">0</span>;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;请输入链表,以0结尾:&quot;</span> &lt;&lt; endl;</span><br><span class="line">    cin &gt;&gt; int_num;</span><br><span class="line">    <span class="keyword">while</span> (int_num != <span class="number">0</span>) &#123;</span><br><span class="line">        list[i] = <span class="keyword">new</span> <span class="built_in">node</span>(&#123; int_num, <span class="literal">nullptr</span> &#125;); <span class="comment">// 创建新节点并存储数据</span></span><br><span class="line">        cin &gt;&gt; int_num;</span><br><span class="line">        i++;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (<span class="type">int</span> j = <span class="number">0</span>; j &lt;= i - <span class="number">2</span>; j++) &#123;</span><br><span class="line">        list[j]-&gt;next = list[j + <span class="number">1</span>]; <span class="comment">// 链接节点，构建链表</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> list[<span class="number">0</span>]; <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="function"><span class="type">void</span> <span class="title">output</span><span class="params">(node* list0)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list0 == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; <span class="string">&quot;链表为空&quot;</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (list0 != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        cout &lt;&lt; list0-&gt;num &lt;&lt; <span class="string">&quot; &quot;</span>; <span class="comment">// 输出节点的数据</span></span><br><span class="line">        list0 = list0-&gt;next; <span class="comment">// 移动到下一个节点</span></span><br><span class="line">    &#125;</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="comment">// 使用插入排序对链表进行升序排序的函数</span></span><br><span class="line"><span class="function">node* <span class="title">sort</span><span class="params">(node* list12)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (list12 == <span class="literal">nullptr</span> || list12-&gt;next == <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">return</span> list12; <span class="comment">// 如果链表为空或只有一个节点，直接返回</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * curr = <span class="literal">nullptr</span>;</span><br><span class="line">    curr = list12;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (curr != <span class="literal">nullptr</span>) &#123;</span><br><span class="line">        <span class="keyword">struct</span> <span class="title class_">node</span>* next = curr-&gt;next;</span><br><span class="line">        <span class="keyword">if</span> (head == <span class="literal">nullptr</span> || curr-&gt;num &lt; head-&gt;num) &#123;</span><br><span class="line">            curr-&gt;next = head;</span><br><span class="line">            head = curr; <span class="comment">// 插入当前节点到已排序部分的合适位置</span></span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">struct</span> node* prev = head;</span><br><span class="line">            <span class="keyword">while</span> (prev-&gt;next != <span class="literal">nullptr</span> &amp;&amp; curr-&gt;num &gt; prev-&gt;next-&gt;num) &#123;</span><br><span class="line">                prev = prev-&gt;next; <span class="comment">// 找到合适的插入位置</span></span><br><span class="line">            &#125;</span><br><span class="line">            curr-&gt;next = prev-&gt;next;</span><br><span class="line">            prev-&gt;next = curr; <span class="comment">// 插入当前节点</span></span><br><span class="line">        &#125;</span><br><span class="line">        curr = next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> head; <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="function">node* <span class="title">merge</span><span class="params">(node* list1, node* list2)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">struct</span> <span class="title class_">node</span>* head = <span class="literal">nullptr</span>, * tail = <span class="literal">nullptr</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 选择头节点</span></span><br><span class="line">    <span class="keyword">if</span> (list1-&gt;num &lt;= list2-&gt;num) &#123;</span><br><span class="line">        head = list1;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        head = list2;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    tail = head; <span class="comment">// 初始化尾节点</span></span><br><span class="line"></span><br><span class="line">    list1 = list1-&gt;next;</span><br><span class="line">    list2 = list2-&gt;next;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历两个链表并合并</span></span><br><span class="line">    <span class="keyword">while</span> (list1 &amp;&amp; list2) &#123;</span><br><span class="line">        <span class="keyword">if</span> (list1-&gt;num &lt;= list2-&gt;num) &#123;</span><br><span class="line">            tail-&gt;next = list1;</span><br><span class="line">            tail = list1;</span><br><span class="line">            list1 = list1-&gt;next;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            tail-&gt;next = list2;</span><br><span class="line">            tail = list2;</span><br><span class="line">            list2 = list2-&gt;next;</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">while</span> (list1) &#123;</span><br><span class="line">        tail-&gt;next = list1;</span><br><span class="line">        tail = list1;</span><br><span class="line">        list1 = list1-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">while</span> (list2) &#123;</span><br><span class="line">        tail-&gt;next = list2;</span><br><span class="line">        tail = list2;</span><br><span class="line">        list2 = list2-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">return</span> head; <span class="comment">// 返回合并后的链表头节点指针</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="type">int</span> <span class="title">main</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;声明：\n链表数据个数上限为10000\n还有，数不要输太大，不然会整数溢出&quot;</span> &lt;&lt; endl;</span><br><span class="line">    node* list1 = <span class="built_in">sort</span>(<span class="built_in">create</span>()); <span class="comment">// 创建并排序第一个链表</span></span><br><span class="line">    node* list2 = <span class="built_in">sort</span>(<span class="built_in">create</span>()); <span class="comment">// 创建并排序第二个链表</span></span><br><span class="line">    cout &lt;&lt; <span class="string">&quot;第一个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list1);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;第二个链表为：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(list2);</span><br><span class="line">    cout &lt;&lt; endl &lt;&lt; <span class="string">&quot;将两个链表合并，然后按升序排列：&quot;</span> &lt;&lt; endl;</span><br><span class="line">    <span class="built_in">output</span>(<span class="built_in">merge</span>(list1, list2));</span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>写这两段代码可不容易（头发没了😭😭😭</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;历经数天磨练（Bug快把我侵蚀），我终于将这款&lt;strong&gt;将链表合并然后进行升序排列程序&lt;/strong&gt;做出来啦~撒花✿✿ヽ(°▽°)ノ✿&lt;/p&gt;
&lt;h3 id=&quot;一-代码组成&quot;&gt;&lt;a href=&quot;#一-代码组成&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="c++" scheme="https://www.wotemo.com/categories/c/"/>
    
    <category term="链表" scheme="https://www.wotemo.com/categories/c/%E9%93%BE%E8%A1%A8/"/>
    
    
    <category term="c++" scheme="https://www.wotemo.com/tags/c/"/>
    
    <category term="链表" scheme="https://www.wotemo.com/tags/%E9%93%BE%E8%A1%A8/"/>
    
    <category term="结构体" scheme="https://www.wotemo.com/tags/%E7%BB%93%E6%9E%84%E4%BD%93/"/>
    
    <category term="函数" scheme="https://www.wotemo.com/tags/%E5%87%BD%E6%95%B0/"/>
    
    <category term="指针" scheme="https://www.wotemo.com/tags/%E6%8C%87%E9%92%88/"/>
    
    <category term="插入排序" scheme="https://www.wotemo.com/tags/%E6%8F%92%E5%85%A5%E6%8E%92%E5%BA%8F/"/>
    
  </entry>
  
  <entry>
    <title>初学链表，尝试插入、删除操作</title>
    <link href="https://www.wotemo.com/posts/8812.html"/>
    <id>https://www.wotemo.com/posts/8812.html</id>
    <published>2023-09-19T13:33:33.000Z</published>
    <updated>2023-09-19T13:33:33.000Z</updated>
    
    <content type="html"><![CDATA[<blockquote><p>一个在3后面插入4，然后删除所有4的程序，哈哈哈</p></blockquote><h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p><code>添4删4，4不4有冰</code></p><p>在链表这里卡了老长时间，终于没有Bug了:)</p><hr><h1 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h1><p>如图<br><img src="https://img.wotemo.com/img/lianbiao1.png" alt="程序运行1"></p><p><img src="https://img.wotemo.com/img/lianbiao2.png" alt="程序运行2"></p><h1 id="一、代码介绍"><a href="#一、代码介绍" class="headerlink" title="一、代码介绍"></a>一、代码介绍</h1><p>这个程序可以输入一串数字(以-1结尾代表结束)，然后在3后面插入4，然后删除所有的4，使用了c++的函数，结构体，指针，链表<br>用到了单向链表的插入和删除等功能</p><h1 id="二、代码部分"><a href="#二、代码部分" class="headerlink" title="二、代码部分"></a>二、代码部分</h1><h2 id="1-插入函数"><a href="#1-插入函数" class="headerlink" title="1.插入函数"></a>1.插入函数</h2><p>代码如下：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在链表中插入一个节点，节点值为num2+1</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">insert</span><span class="params">(node* head, <span class="type">int</span> num2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    node *tmp = <span class="literal">nullptr</span>, *tail = <span class="literal">nullptr</span>;</span><br><span class="line">    tail = head-&gt;next;</span><br><span class="line">    <span class="comment">// 遍历链表，找到节点值为num2的位置</span></span><br><span class="line">    <span class="keyword">while</span> (tail-&gt;next != <span class="literal">nullptr</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        tmp = <span class="keyword">new</span> <span class="built_in">node</span>(&#123;num2 + <span class="number">1</span>, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">        <span class="keyword">if</span> (tail-&gt;num == num2)</span><br><span class="line">        &#123;</span><br><span class="line">            tmp-&gt;next = tail-&gt;next;</span><br><span class="line">            tail-&gt;next = tmp;</span><br><span class="line">        &#125;</span><br><span class="line">        tail = tail-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    tmp = <span class="keyword">new</span> <span class="built_in">node</span>(&#123;num2 + <span class="number">1</span>, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">    <span class="keyword">if</span> (tail-&gt;num == num2)</span><br><span class="line">    &#123;</span><br><span class="line">        tmp-&gt;next = tail-&gt;next;</span><br><span class="line">        tail-&gt;next = tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="2-删除函数"><a href="#2-删除函数" class="headerlink" title="2.删除函数"></a>2.删除函数</h2><p>代码如下：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="type">void</span> <span class="title">del</span><span class="params">(node* head, <span class="type">int</span> num)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    node *tmp = <span class="literal">nullptr</span>, *tail = <span class="literal">nullptr</span>;</span><br><span class="line">    tail = head;</span><br><span class="line">    <span class="comment">// 遍历链表，找到节点值为num的位置并删除</span></span><br><span class="line">    <span class="keyword">while</span> (tail-&gt;next-&gt;next != <span class="literal">nullptr</span> &amp;&amp; tail-&gt;next != <span class="literal">nullptr</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (tail-&gt;next-&gt;num == num)</span><br><span class="line">        &#123;</span><br><span class="line">            tmp = tail-&gt;next;</span><br><span class="line">            tail-&gt;next = tail-&gt;next-&gt;next;</span><br><span class="line">            <span class="keyword">delete</span> tmp;</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">            tail = tail-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (tail-&gt;next-&gt;num == num)</span><br><span class="line">    &#123;</span><br><span class="line">        tmp = tail-&gt;next;</span><br><span class="line">        tail-&gt;next = <span class="literal">nullptr</span>;</span><br><span class="line">        <span class="keyword">delete</span> tmp;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p><code>注意tail=tail-&gt;next一定要放在else里面，要不然就会像我一样找了半天Bug</code></p><h2 id="3-完整代码"><a href="#3-完整代码" class="headerlink" title="3.完整代码"></a>3.完整代码</h2><p>代码如下：</p><figure class="highlight cpp"><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></pre></td><td class="code"><pre><span class="line">@[TOC](初学链表，尝试插入、删除、循环)</span><br><span class="line">## 前言</span><br><span class="line">### 在链表这里卡了老长时间，终于没有Bug了:)</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">```cpp</span><br><span class="line"><span class="meta">#<span class="keyword">include</span> <span class="string">&lt;iostream&gt;</span></span></span><br><span class="line"><span class="keyword">using</span> <span class="keyword">namespace</span> std;</span><br><span class="line"></span><br><span class="line"><span class="keyword">struct</span> <span class="title class_">node</span></span><br><span class="line">&#123;</span><br><span class="line">    <span class="type">int</span> num;</span><br><span class="line">    node *next;</span><br><span class="line">&#125;;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 在链表中插入一个节点，节点值为num2+1</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">insert</span><span class="params">(node* head, <span class="type">int</span> num2)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    node *tmp = <span class="literal">nullptr</span>, *tail = <span class="literal">nullptr</span>;</span><br><span class="line">    tail = head-&gt;next;</span><br><span class="line">    <span class="comment">// 遍历链表，找到节点值为num2的位置</span></span><br><span class="line">    <span class="keyword">while</span> (tail-&gt;next != <span class="literal">nullptr</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        tmp = <span class="keyword">new</span> <span class="built_in">node</span>(&#123;num2 + <span class="number">1</span>, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">        <span class="keyword">if</span> (tail-&gt;num == num2)</span><br><span class="line">        &#123;</span><br><span class="line">            tmp-&gt;next = tail-&gt;next;</span><br><span class="line">            tail-&gt;next = tmp;</span><br><span class="line">        &#125;</span><br><span class="line">        tail = tail-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    tmp = <span class="keyword">new</span> <span class="built_in">node</span>(&#123;num2 + <span class="number">1</span>, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">    <span class="keyword">if</span> (tail-&gt;num == num2)</span><br><span class="line">    &#123;</span><br><span class="line">        tmp-&gt;next = tail-&gt;next;</span><br><span class="line">        tail-&gt;next = tmp;</span><br><span class="line">    &#125;</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="comment">// 在链表中删除指定节点</span></span><br><span class="line"><span class="function"><span class="type">void</span> <span class="title">del</span><span class="params">(node* head, <span class="type">int</span> num)</span></span></span><br><span class="line"><span class="function"></span>&#123;</span><br><span class="line">    node *tmp = <span class="literal">nullptr</span>, *tail = <span class="literal">nullptr</span>;</span><br><span class="line">    tail = head;</span><br><span class="line">    <span class="comment">// 遍历链表，找到节点值为num的位置并删除</span></span><br><span class="line">    <span class="keyword">while</span> (tail-&gt;next-&gt;next != <span class="literal">nullptr</span> &amp;&amp; tail-&gt;next != <span class="literal">nullptr</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">if</span> (tail-&gt;next-&gt;num == num)</span><br><span class="line">        &#123;</span><br><span class="line">            tmp = tail-&gt;next;</span><br><span class="line">            tail-&gt;next = tail-&gt;next-&gt;next;</span><br><span class="line">            <span class="keyword">delete</span> tmp;</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">            tail = tail-&gt;next;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (tail-&gt;next-&gt;num == num)</span><br><span class="line">    &#123;</span><br><span class="line">        tmp = tail-&gt;next;</span><br><span class="line">        tail-&gt;next = <span class="literal">nullptr</span>;</span><br><span class="line">        <span class="keyword">delete</span> tmp;</span><br><span class="line">    &#125;</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="function"><span class="type">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">    node *head = <span class="literal">nullptr</span>, *tail = <span class="literal">nullptr</span>, *tmp = <span class="literal">nullptr</span>;</span><br><span class="line">    <span class="type">int</span> x = <span class="number">-1</span>;</span><br><span class="line">    cin &gt;&gt; x;</span><br><span class="line">    head = <span class="keyword">new</span> <span class="built_in">node</span>(&#123;x, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">    tail = head;</span><br><span class="line">    <span class="keyword">while</span> (x != <span class="number">-1</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        tmp = <span class="keyword">new</span> <span class="built_in">node</span>(&#123;x, <span class="literal">nullptr</span>&#125;);</span><br><span class="line">        tail-&gt;next = tmp;</span><br><span class="line">        tail = tmp;</span><br><span class="line">        cin &gt;&gt; x;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="built_in">insert</span>(head, <span class="number">3</span>); <span class="comment">// 在链表中插入节点值为4的节点</span></span><br><span class="line">    <span class="built_in">del</span>(head, <span class="number">4</span>); <span class="comment">// 删除链表中节点值为4的节点</span></span><br><span class="line"></span><br><span class="line">    tmp = head-&gt;next;</span><br><span class="line">    <span class="keyword">while</span> (tmp != <span class="literal">nullptr</span>)</span><br><span class="line">    &#123;</span><br><span class="line">        cout &lt;&lt; (*tmp).num &lt;&lt; <span class="string">&quot; &quot;</span>; <span class="comment">// 输出链表节点的值</span></span><br><span class="line">        tmp = tmp-&gt;next;</span><br><span class="line">    &#125;</span><br><span class="line">    </span><br><span class="line">    <span class="keyword">return</span> <span class="number">0</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><hr><h1 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h1><p>链表是个好东西~~</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;blockquote&gt;
&lt;p&gt;一个在3后面插入4，然后删除所有4的程序，哈哈哈&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h1 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot;</summary>
        
      
    
    
    
    <category term="c++" scheme="https://www.wotemo.com/categories/c/"/>
    
    <category term="链表" scheme="https://www.wotemo.com/categories/c/%E9%93%BE%E8%A1%A8/"/>
    
    
    <category term="c++" scheme="https://www.wotemo.com/tags/c/"/>
    
    <category term="链表" scheme="https://www.wotemo.com/tags/%E9%93%BE%E8%A1%A8/"/>
    
    <category term="结构体" scheme="https://www.wotemo.com/tags/%E7%BB%93%E6%9E%84%E4%BD%93/"/>
    
    <category term="函数" scheme="https://www.wotemo.com/tags/%E5%87%BD%E6%95%B0/"/>
    
    <category term="指针" scheme="https://www.wotemo.com/tags/%E6%8C%87%E9%92%88/"/>
    
  </entry>
  
  <entry>
    <title>一个Python &amp; Pygame开发的贪吃蛇小游戏</title>
    <link href="https://www.wotemo.com/posts/2186.html"/>
    <id>https://www.wotemo.com/posts/2186.html</id>
    <published>2022-08-14T03:48:22.000Z</published>
    <updated>2023-08-23T07:33:45.000Z</updated>
    
    <content type="html"><![CDATA[<h1 id="SnakeGame"><a href="#SnakeGame" class="headerlink" title="SnakeGame"></a>SnakeGame</h1><p>一个Python &amp; Pygame开发的贪吃蛇小游戏</p><p>由<a href="https://www.wotemo.com/">Wotemo</a>自主编写</p><h3 id="共有单机版，联机版和最新一步到位联机版三种"><a href="#共有单机版，联机版和最新一步到位联机版三种" class="headerlink" title="共有单机版，联机版和最新一步到位联机版三种"></a>共有单机版，联机版和最新一步到位联机版三种</h3><ul><li><h4 id="单机版不能保存得分"><a href="#单机版不能保存得分" class="headerlink" title="单机版不能保存得分"></a><strong>单机版不能保存得分</strong></h4></li><li><h4 id="联机版可以保存得分"><a href="#联机版可以保存得分" class="headerlink" title="联机版可以保存得分"></a><strong>联机版可以保存得分</strong></h4></li><li><h4 id="最新一步到位联机版可以保存得分"><a href="#最新一步到位联机版可以保存得分" class="headerlink" title="最新一步到位联机版可以保存得分"></a><strong>最新一步到位联机版可以保存得分</strong></h4></li></ul><h3 id="注："><a href="#注：" class="headerlink" title="注："></a>注：</h3><ul><li><h4 id="最新一步到位联机版使用的是Leancould读写数据"><a href="#最新一步到位联机版使用的是Leancould读写数据" class="headerlink" title="最新一步到位联机版使用的是Leancould读写数据"></a>最新一步到位联机版使用的是<a href="https://leancloud.app/">Leancould</a>读写数据</h4><h4 id="不懂Leancould的可以去百度一下"><a href="#不懂Leancould的可以去百度一下" class="headerlink" title="不懂Leancould的可以去百度一下"></a>不懂Leancould的可以去百度一下</h4></li><li><h4 id="联机版需要flask框架"><a href="#联机版需要flask框架" class="headerlink" title="联机版需要flask框架"></a>联机版需要flask框架</h4></li></ul><p>本文件夹中的SnakeUsers已安装好虚拟环境和Flask</p><hr><h3 id="Flask基本安装方法："><a href="#Flask基本安装方法：" class="headerlink" title="Flask基本安装方法："></a>Flask基本安装方法：</h3><p><strong>（联机版看，大神无视)</strong></p><ol><li>打开powershall</li><li>cd到SnakeUsers文件夹 cd C:\SnakeUsers</li><li>构建虚拟环境 py -3 -m venv venv</li><li>激活虚拟环境 venv\Scripts\activate</li><li>安装Flask pip install Flask</li><li>导出 <code>FLASK_APP</code> 环境变量 $env:FLASK_APP &#x3D; “main.py”</li><li>运行服务器 flask run –host&#x3D;0.0.0.0</li></ol><p><img src="https://img.wotemo.com/img/snakegame_over.png" alt="进程展示--游戏结束"></p><h3 id="下面展示最新一步到位联机版的Python代码"><a href="#下面展示最新一步到位联机版的Python代码" class="headerlink" title="下面展示最新一步到位联机版的Python代码"></a>下面展示最新一步到位联机版的Python代码</h3><p><strong>（三个完整版代码可以在<a href="https://github.com/wtmxxx/SnakeGame">Github</a>中找到)</strong></p><h4 id="第一部分：main-py"><a href="#第一部分：main-py" class="headerlink" title="第一部分：main.py"></a>第一部分：main.py</h4><h3 id="这个部分是程序的主代码，"><a href="#这个部分是程序的主代码，" class="headerlink" title="这个部分是程序的主代码，"></a>这个部分是程序的主代码，</h3><figure class="highlight python"><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><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"><span class="string">这可能不是一个游戏.This may not be a game.</span></span><br><span class="line"><span class="string">MADE BY Wotemo.</span></span><br><span class="line"><span class="string">&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> pygame</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> sys</span><br><span class="line"><span class="keyword">import</span> requests</span><br><span class="line"></span><br><span class="line">pygame.init()</span><br><span class="line"></span><br><span class="line">window_width, window_height = <span class="number">600</span>, <span class="number">600</span></span><br><span class="line"></span><br><span class="line">window = pygame.display.set_mode((window_width, window_height))</span><br><span class="line">pygame.display.set_caption(<span class="string">&#x27;贪吃蛇 | Wotemo&#x27;</span>)</span><br><span class="line">icon = pygame.image.load(<span class="string">&#x27;resources/snake.ico&#x27;</span>)</span><br><span class="line">pygame.display.set_icon(icon)</span><br><span class="line"></span><br><span class="line">pygame.mixer.music.load(<span class="string">&#x27;resources/bgmusic.mp3&#x27;</span>)</span><br><span class="line">pygame.mixer.music.play(-<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">font_source = <span class="string">&#x27;resources/君子怀旧仿宋体.ttf&#x27;</span></span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;本代码由沃特陌(Wotemo)编写，未经授权，严禁转载&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27; _      ______  ____________  _______&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;| | /| / / __ \/_  __/ __/  |/  / __ \\&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;| |/ |/ / /_/ / / / / _// /|_/ / /_/ /&#x27;</span>)</span><br><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;|__/|__/\____/ /_/ /___/_/  /_/\____/&#x27;</span>)</span><br><span class="line">window.fill((<span class="number">229</span>, <span class="number">231</span>, <span class="number">206</span>))</span><br><span class="line">pygame.display.flip()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">draw_bg</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;画背景&quot;&quot;&quot;</span></span><br><span class="line">    line_height = <span class="number">75</span></span><br><span class="line">    line_width = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> line_height &lt;= window_height:</span><br><span class="line">        pygame.draw.line(window, (<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>), (<span class="number">0</span>, line_height), (window_width, line_height))</span><br><span class="line">        line_height += <span class="number">25</span></span><br><span class="line">    <span class="keyword">while</span> line_width &lt;= window_width:</span><br><span class="line">        pygame.draw.line(window, (<span class="number">0</span>, <span class="number">0</span>, <span class="number">0</span>), (line_width, <span class="number">75</span>), (line_width, window_height))</span><br><span class="line">        line_width += <span class="number">25</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fonts</span>(<span class="params">font_pos, content, font_style, is_true, font_color, font_size</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;便捷生成字体的函数&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 以上分别对应：文字显示位置()元组，</span></span><br><span class="line">    <span class="comment"># 文字内容&#x27;&#x27;字符串，</span></span><br><span class="line">    <span class="comment"># 字体样式&#x27;&#x27;字符串路径，</span></span><br><span class="line">    <span class="comment"># 是否是平滑字体True布尔值，</span></span><br><span class="line">    <span class="comment"># 字体颜色()元组，</span></span><br><span class="line">    <span class="comment"># 字体大小1整数</span></span><br><span class="line">    fonts_temp = pygame.font.Font(font_style, font_size)</span><br><span class="line">    window.blit(fonts_temp.render(content, is_true, font_color), font_pos)</span><br><span class="line">    pygame.display.update()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">fonts_mid</span>(<span class="params">font_pos, content, font_style, is_true, font_color, font_size</span>):</span><br><span class="line">    <span class="string">&quot;&quot;&quot;便捷生成字体的函数&quot;&quot;&quot;</span></span><br><span class="line">    <span class="comment"># 以上分别对应：文字显示位置()元组，</span></span><br><span class="line">    <span class="comment"># 文字内容&#x27;&#x27;字符串，</span></span><br><span class="line">    <span class="comment"># 字体样式&#x27;&#x27;字符串路径，</span></span><br><span class="line">    <span class="comment"># 是否是平滑字体True布尔值，</span></span><br><span class="line">    <span class="comment"># 字体颜色()元组，</span></span><br><span class="line">    <span class="comment"># 字体大小1整数</span></span><br><span class="line">    fonts_temp = pygame.font.Font(font_style, font_size)</span><br><span class="line">    fonts_temp_two = fonts_temp.render(content, is_true, font_color)</span><br><span class="line">    font_pos_temp = fonts_temp_two.get_size()</span><br><span class="line">    w = font_pos_temp[<span class="number">0</span>]</span><br><span class="line">    h = font_pos_temp[<span class="number">1</span>]</span><br><span class="line">    w_mid = (window_width - w) / <span class="number">2</span></span><br><span class="line">    h_mid = (window_height - h) / <span class="number">2</span></span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">type</span>(font_pos[<span class="number">0</span>]) == <span class="built_in">int</span>:</span><br><span class="line">        w_mid = font_pos[<span class="number">0</span>]</span><br><span class="line">    <span class="keyword">if</span> <span class="built_in">type</span>(font_pos[<span class="number">1</span>]) == <span class="built_in">int</span>:</span><br><span class="line">        h_mid = font_pos[<span class="number">1</span>]</span><br><span class="line">    font_pos_final = (w_mid, h_mid)</span><br><span class="line">    window.blit(fonts_temp_two, font_pos_final)</span><br><span class="line">    pygame.display.update()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> InputBox</span><br><span class="line">fonts_mid((<span class="literal">False</span>,<span class="number">200</span>), <span class="string">&#x27;请输入您的用户名&#x27;</span>, font_source, <span class="literal">True</span>, (<span class="number">47</span>, <span class="number">125</span>, <span class="number">242</span>), <span class="number">25</span>)</span><br><span class="line">userinputs = InputBox.main()</span><br><span class="line">window.fill((<span class="number">229</span>, <span class="number">231</span>, <span class="number">206</span>))</span><br><span class="line">draw_bg()</span><br><span class="line"><span class="comment"># 替换为您的appid和appkey</span></span><br><span class="line">appid = <span class="string">&#x27;bf0o5ie2uZLSWGcDEuneCFEB-MdYXbMMI&#x27;</span></span><br><span class="line">appkey = <span class="string">&#x27;XIch9T9F9YYtlJBvQkBTYO2A&#x27;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">create_user</span>(<span class="params">username</span>):</span><br><span class="line">    headers = &#123;</span><br><span class="line">        <span class="string">&#x27;X-LC-Id&#x27;</span>: appid,</span><br><span class="line">        <span class="string">&#x27;X-LC-Key&#x27;</span>: appkey,</span><br><span class="line">        <span class="string">&#x27;Content-Type&#x27;</span>: <span class="string">&#x27;application/json&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 构建API请求URL</span></span><br><span class="line">    url = <span class="string">&#x27;https://snakegame.wotemo.com/1.1/classes/Snakegame&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 构建请求体</span></span><br><span class="line">    data = &#123;</span><br><span class="line">        <span class="string">&#x27;user&#x27;</span>: username</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 发送POST请求</span></span><br><span class="line">    response = requests.post(url, headers=headers, json=data)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 处理响应数据</span></span><br><span class="line">    <span class="keyword">if</span> response.status_code == <span class="number">201</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;用户名&#x27;<span class="subst">&#123;username&#125;</span>&#x27;创建成功&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;未能创建用户名&#x27;<span class="subst">&#123;username&#125;</span>&#x27;&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">get_score</span>(<span class="params">username</span>):</span><br><span class="line">    headers = &#123;</span><br><span class="line">        <span class="string">&#x27;X-LC-Id&#x27;</span>: appid,</span><br><span class="line">        <span class="string">&#x27;X-LC-Key&#x27;</span>: appkey</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment"># 构建API请求URL</span></span><br><span class="line">    url = <span class="string">f&#x27;https://snakegame.wotemo.com/1.1/classes/Snakegame?where=&#123;&#123;&quot;user&quot;:&quot;<span class="subst">&#123;username&#125;</span>&quot;&#125;&#125;&amp;keys=score&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 发送GET请求</span></span><br><span class="line">    response = requests.get(url, headers=headers)</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 处理响应数据</span></span><br><span class="line">    <span class="keyword">if</span> response.status_code == <span class="number">200</span>:</span><br><span class="line">        data = response.json()</span><br><span class="line">        <span class="keyword">if</span> <span class="string">&#x27;results&#x27;</span> <span class="keyword">in</span> data <span class="keyword">and</span> <span class="built_in">len</span>(data[<span class="string">&#x27;results&#x27;</span>]) &gt; <span class="number">0</span>:</span><br><span class="line">            score = data[<span class="string">&#x27;results&#x27;</span>][<span class="number">0</span>][<span class="string">&#x27;score&#x27;</span>]</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;用户名为&#x27;<span class="subst">&#123;username&#125;</span>&#x27;的分数为: <span class="subst">&#123;score&#125;</span>&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> score</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            create_user(userinputs[<span class="number">0</span>])</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;用户名&#x27;<span class="subst">&#123;username&#125;</span>&#x27;无对应分数&quot;</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="number">0</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;未能获取分数&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">update_score</span>(<span class="params">username,new_score</span>):</span><br><span class="line">    headers = &#123;</span><br><span class="line">        <span class="string">&#x27;X-LC-Id&#x27;</span>: appid,</span><br><span class="line">        <span class="string">&#x27;X-LC-Key&#x27;</span>: appkey,</span><br><span class="line">        <span class="string">&#x27;Content-Type&#x27;</span>: <span class="string">&#x27;application/json&#x27;</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment"># 构建API请求URL</span></span><br><span class="line">    url = <span class="string">f&#x27;https://snakegame.wotemo.com/1.1/classes/Snakegame?where=&#123;&#123;&quot;user&quot;:&quot;<span class="subst">&#123;username&#125;</span>&quot;&#125;&#125;&#x27;</span></span><br><span class="line"></span><br><span class="line">    <span class="comment"># 发送GET请求，获取用户记录的objectId</span></span><br><span class="line">    response = requests.get(url, headers=headers)</span><br><span class="line">    data = response.json()</span><br><span class="line">    <span class="keyword">if</span> <span class="string">&#x27;results&#x27;</span> <span class="keyword">in</span> data <span class="keyword">and</span> <span class="built_in">len</span>(data[<span class="string">&#x27;results&#x27;</span>]) &gt; <span class="number">0</span>:</span><br><span class="line">        object_id = data[<span class="string">&#x27;results&#x27;</span>][<span class="number">0</span>][<span class="string">&#x27;objectId&#x27;</span>]</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 构建更新请求URL</span></span><br><span class="line">        update_url = <span class="string">f&#x27;https://snakegame.wotemo.com/1.1/classes/Snakegame/<span class="subst">&#123;object_id&#125;</span>&#x27;</span></span><br><span class="line"></span><br><span class="line">        <span class="comment"># 构建请求体</span></span><br><span class="line">        data = &#123;</span><br><span class="line">            <span class="string">&#x27;score&#x27;</span>: new_score</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 发送PUT请求，更新分数值</span></span><br><span class="line">        update_response = requests.put(update_url, headers=headers, json=data)</span><br><span class="line"></span><br><span class="line">        <span class="comment"># 处理响应数据</span></span><br><span class="line">        <span class="keyword">if</span> update_response.status_code == <span class="number">200</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;用户名为 &#x27;<span class="subst">&#123;username&#125;</span>&#x27;的分数更新成功！！！&quot;</span>)</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&quot;未能更新用户名为&#x27;<span class="subst">&#123;username&#125;</span>&#x27;的分数！&quot;</span>)</span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&quot;用户名&#x27;<span class="subst">&#123;username&#125;</span>&#x27;未创建&quot;</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span>:</span><br><span class="line">    score_int = get_score(userinputs[<span class="number">0</span>])</span><br><span class="line">    pygame.time.wait(<span class="number">600</span>)</span><br><span class="line"><span class="keyword">except</span>:</span><br><span class="line">    score_int = <span class="number">0</span></span><br><span class="line">window.fill((<span class="number">229</span>, <span class="number">231</span>, <span class="number">206</span>))</span><br><span class="line">scores = pygame.font.Font(font_source, <span class="number">50</span>).render(<span class="built_in">str</span>(score_int), <span class="literal">True</span>, (<span class="number">66</span>, <span class="number">133</span>, <span class="number">244</span>))</span><br><span class="line">score_w, score_h = scores.get_size()</span><br><span class="line">score_ws = (window_width - score_w) / <span class="number">2</span></span><br><span class="line">score_hs = (<span class="number">75</span> - score_h) / <span class="number">2</span></span><br><span class="line">window.blit(scores, (score_ws, score_hs))</span><br><span class="line">pygame.display.update()</span><br><span class="line"></span><br><span class="line">fonts((<span class="number">0</span>, <span class="number">12</span>), <span class="string">&#x27;SCORE:&#x27;</span>, font_source, <span class="literal">True</span>, (<span class="number">66</span>, <span class="number">133</span>, <span class="number">244</span>), <span class="number">50</span>)</span><br><span class="line"><span class="comment"># fonts((300, 12), str(score_int), font_source, True, (66, 133, 244), 50)</span></span><br><span class="line">draw_bg()</span><br><span class="line"></span><br><span class="line">rw = random.randrange(<span class="number">25</span>, <span class="number">575</span>, <span class="number">25</span>)</span><br><span class="line">rh = random.randrange(<span class="number">100</span>, <span class="number">575</span>, <span class="number">25</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># rwc = random.randrange(25, 575, 25)</span></span><br><span class="line"><span class="comment"># rhc = random.randrange(100, 575, 25)</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">rwhcs = []</span><br><span class="line"><span class="comment"># while candy_num &lt;3:</span></span><br><span class="line"><span class="comment">#     rwc = random.randrange(25, 575, 25)</span></span><br><span class="line"><span class="comment">#     rhc = random.randrange(100, 575, 25)</span></span><br><span class="line"><span class="comment">#     rwhc = (rwc, rhc)</span></span><br><span class="line"><span class="comment">#     rwhcs.append(rwhc)</span></span><br><span class="line"><span class="comment">#     candy_num += 1</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">creat_candy</span>(<span class="params">num</span>):</span><br><span class="line">    candy_num = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> candy_num &lt; num:</span><br><span class="line">        rwc = random.randrange(<span class="number">25</span>, <span class="number">575</span>, <span class="number">25</span>)</span><br><span class="line">        rhc = random.randrange(<span class="number">100</span>, <span class="number">575</span>, <span class="number">25</span>)</span><br><span class="line">        rwhc = (rwc, rhc)</span><br><span class="line">        <span class="keyword">if</span> rwhc <span class="keyword">in</span> rwhcs:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            rwhcs.append(rwhc)</span><br><span class="line">            candy_num += <span class="number">1</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">creat_candy(<span class="number">3</span>)  <span class="comment"># 创建初始糖果</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">print</span>(<span class="string">f&#x27;初始糖果位置<span class="subst">&#123;rwhcs&#125;</span>&#x27;</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment"># snake = pygame.draw.rect(window, (118, 37, 39), (rw, rh, 25, 25))</span></span><br><span class="line"><span class="comment"># candy = pygame.draw.rect(window, (1, 185, 237), (rwc, rhc, 25, 25))</span></span><br><span class="line"><span class="comment"># pygame.display.update()</span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line">active = <span class="number">0</span></span><br><span class="line">active_time = <span class="number">0</span></span><br><span class="line">speed = <span class="number">475</span></span><br><span class="line"><span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line"></span><br><span class="line">    window.fill((<span class="number">229</span>, <span class="number">231</span>, <span class="number">206</span>), (rw, rh, <span class="number">25</span>, <span class="number">25</span>))</span><br><span class="line"></span><br><span class="line">    snake_pos = (rw, rh)</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> snake_pos <span class="keyword">in</span> rwhcs:</span><br><span class="line">        snake_index = rwhcs.index(snake_pos)</span><br><span class="line">        rwc = random.randrange(<span class="number">25</span>, <span class="number">575</span>, <span class="number">25</span>)</span><br><span class="line">        rhc = random.randrange(<span class="number">100</span>, <span class="number">575</span>, <span class="number">25</span>)</span><br><span class="line">        rwhc = (rwc, rhc)</span><br><span class="line">        rwhcs[snake_index] = rwhc</span><br><span class="line"></span><br><span class="line">        pygame.mixer.Sound(<span class="string">&#x27;resources/eat.mp3&#x27;</span>).play(<span class="number">0</span>)</span><br><span class="line"></span><br><span class="line">        pygame.draw.rect(window, (<span class="number">1</span>, <span class="number">185</span>, <span class="number">237</span>), (rw, rh, <span class="number">25</span>, <span class="number">25</span>))</span><br><span class="line">        pygame.display.update()</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> score_int &lt; <span class="number">500</span>:</span><br><span class="line">            score_int += <span class="number">100</span></span><br><span class="line">            speed = <span class="number">425</span></span><br><span class="line">        <span class="keyword">if</span> <span class="number">500</span> &lt;= score_int &lt; <span class="number">1000</span>:</span><br><span class="line">            score_int += <span class="number">100</span></span><br><span class="line">            speed = <span class="number">400</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">len</span>(rwhcs) &lt;= <span class="number">3</span>:</span><br><span class="line">                creat_candy(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="number">1000</span> &lt;= score_int &lt; <span class="number">1500</span>:</span><br><span class="line">            score_int += <span class="number">150</span></span><br><span class="line">            speed = <span class="number">350</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">len</span>(rwhcs) &lt;= <span class="number">4</span>:</span><br><span class="line">                creat_candy(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="number">1500</span> &lt;= score_int &lt; <span class="number">2000</span>:</span><br><span class="line">            score_int += <span class="number">200</span></span><br><span class="line">            speed = <span class="number">325</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">len</span>(rwhcs) &lt;= <span class="number">5</span>:</span><br><span class="line">                creat_candy(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="number">2000</span> &lt;= score_int &lt; <span class="number">2500</span>:</span><br><span class="line">            score_int += <span class="number">250</span></span><br><span class="line">            speed = <span class="number">300</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">len</span>(rwhcs) &lt;= <span class="number">6</span>:</span><br><span class="line">                creat_candy(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="number">2500</span> &lt;= score_int &lt; <span class="number">3000</span>:</span><br><span class="line">            score_int += <span class="number">300</span></span><br><span class="line">            speed = <span class="number">275</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">len</span>(rwhcs) &lt;= <span class="number">7</span>:</span><br><span class="line">                creat_candy(<span class="number">1</span>)</span><br><span class="line">        <span class="keyword">if</span> <span class="number">3000</span> &lt;= score_int:</span><br><span class="line">            score_int += <span class="number">350</span></span><br><span class="line">            speed = <span class="number">250</span></span><br><span class="line">            <span class="keyword">if</span> <span class="built_in">len</span>(rwhcs) &lt;= <span class="number">8</span>:</span><br><span class="line">                creat_candy(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> score_int &lt;= <span class="number">999999</span>:</span><br><span class="line">            window.fill((<span class="number">229</span>, <span class="number">231</span>, <span class="number">206</span>), (<span class="number">180</span>, <span class="number">0</span>, <span class="number">420</span>, <span class="number">75</span>))</span><br><span class="line"></span><br><span class="line">            scores = pygame.font.Font(font_source, <span class="number">50</span>).render(<span class="built_in">str</span>(score_int), <span class="literal">True</span>, (<span class="number">66</span>, <span class="number">133</span>, <span class="number">244</span>))</span><br><span class="line">            score_w, score_h = scores.get_size()</span><br><span class="line">            score_ws = (window_width - score_w) / <span class="number">2</span></span><br><span class="line">            score_hs = (<span class="number">75</span> - score_h) / <span class="number">2</span></span><br><span class="line">            window.blit(scores, (score_ws, score_hs))</span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            window.fill((<span class="number">229</span>, <span class="number">231</span>, <span class="number">206</span>), (<span class="number">180</span>, <span class="number">0</span>, <span class="number">420</span>, <span class="number">75</span>))</span><br><span class="line">            scores = pygame.font.Font(font_source, <span class="number">50</span>).render(<span class="string">&#x27;999999+&#x27;</span>, <span class="literal">True</span>, (<span class="number">66</span>, <span class="number">133</span>, <span class="number">244</span>))</span><br><span class="line">            score_w, score_h = scores.get_size()</span><br><span class="line">            score_ws = (window_width - score_w) / <span class="number">2</span></span><br><span class="line">            score_hs = (<span class="number">75</span> - score_h) / <span class="number">2</span></span><br><span class="line">            window.blit(scores, (score_ws, score_hs))</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">f&#x27;得分<span class="subst">&#123;score_int&#125;</span>&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> rw <span class="keyword">in</span> [<span class="number">0</span>, <span class="number">575</span>] <span class="keyword">or</span> rh <span class="keyword">in</span> [<span class="number">75</span>, <span class="number">575</span>]:</span><br><span class="line">        active = <span class="number">0</span></span><br><span class="line">        pygame.mixer.Sound(<span class="string">&#x27;resources/gameover.mp3&#x27;</span>).play(<span class="number">0</span>)</span><br><span class="line">        draw_bg()</span><br><span class="line">        gameover_img = pygame.image.load(<span class="string">&#x27;resources/gameover.png&#x27;</span>)</span><br><span class="line">        gameover_size = gameover_img.get_size()</span><br><span class="line">        window.blit(gameover_img, ((window_width - gameover_size[<span class="number">0</span>])/<span class="number">2</span>, ((window_height - gameover_size[<span class="number">1</span>])/<span class="number">2</span> - <span class="number">50</span>)))</span><br><span class="line"></span><br><span class="line">        score_all = pygame.font.Font(font_source, <span class="number">50</span>).render(<span class="string">f&#x27;本次得分: <span class="subst">&#123;score_int&#125;</span>&#x27;</span>, <span class="literal">True</span>, (<span class="number">200</span>, <span class="number">62</span>, <span class="number">62</span>))</span><br><span class="line">        score_all_pos = score_all.get_size()</span><br><span class="line">        score_ws = (window_width - score_all_pos[<span class="number">0</span>]) / <span class="number">2</span></span><br><span class="line">        score_hs = (window_height - score_all_pos[<span class="number">1</span>] + <span class="number">75</span>) / <span class="number">2</span></span><br><span class="line">        window.blit(score_all, (score_ws, score_hs))</span><br><span class="line"></span><br><span class="line">        pygame.display.update()</span><br><span class="line">        <span class="keyword">try</span>:</span><br><span class="line">            update_score(userinputs[<span class="number">0</span>],score_int)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&#x27;你的最终分数为：<span class="subst">&#123;score_int&#125;</span>&#x27;</span>)</span><br><span class="line">        <span class="keyword">except</span>:</span><br><span class="line">            <span class="keyword">pass</span></span><br><span class="line">        pygame.time.wait(<span class="number">7500</span>)</span><br><span class="line">        pygame.quit()</span><br><span class="line">        sys.exit()</span><br><span class="line"></span><br><span class="line">    active_time += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> active_time % speed == <span class="number">0</span>:</span><br><span class="line">        <span class="keyword">if</span> active == <span class="number">1</span>:  <span class="comment"># 上</span></span><br><span class="line">            rh -= <span class="number">25</span></span><br><span class="line">        <span class="keyword">if</span> active == <span class="number">2</span>:  <span class="comment"># 下</span></span><br><span class="line">            rh += <span class="number">25</span></span><br><span class="line">        <span class="keyword">if</span> active == <span class="number">3</span>:  <span class="comment"># 左</span></span><br><span class="line">            rw -= <span class="number">25</span></span><br><span class="line">        <span class="keyword">if</span> active == <span class="number">4</span>:  <span class="comment"># 右</span></span><br><span class="line">            rw += <span class="number">25</span></span><br><span class="line"></span><br><span class="line">    pygame.draw.rect(window, (<span class="number">118</span>, <span class="number">37</span>, <span class="number">39</span>), (rw, rh, <span class="number">25</span>, <span class="number">25</span>))  <span class="comment"># 蛇</span></span><br><span class="line">    <span class="comment"># pygame.draw.rect(window, (1, 185, 237), (rwc, rhc, 25, 25))  # 糖果</span></span><br><span class="line">    pygame.display.update()</span><br><span class="line">    draw_bg()</span><br><span class="line">    <span class="keyword">for</span> rwhc_temp <span class="keyword">in</span> rwhcs:</span><br><span class="line">        pygame.draw.rect(window, (<span class="number">1</span>, <span class="number">185</span>, <span class="number">237</span>), (rwhc_temp[<span class="number">0</span>], rwhc_temp[<span class="number">1</span>], <span class="number">25</span>, <span class="number">25</span>))</span><br><span class="line">    pygame.display.update()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">for</span> event <span class="keyword">in</span> pygame.event.get():</span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> event.<span class="built_in">type</span> == pygame.KEYDOWN:</span><br><span class="line">            keys = event.key</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&#x27;按下<span class="subst">&#123;keys&#125;</span>&#x27;</span>)</span><br><span class="line">            <span class="keyword">if</span> keys == <span class="number">1073741906</span> <span class="keyword">or</span> keys == <span class="number">119</span>:  <span class="comment"># 上</span></span><br><span class="line">                active = <span class="number">1</span></span><br><span class="line">            <span class="keyword">if</span> keys == <span class="number">1073741905</span> <span class="keyword">or</span> keys == <span class="number">115</span>:  <span class="comment"># 下</span></span><br><span class="line">                active = <span class="number">2</span></span><br><span class="line">            <span class="keyword">if</span> keys == <span class="number">1073741904</span> <span class="keyword">or</span> keys == <span class="number">97</span>:  <span class="comment"># 左</span></span><br><span class="line">                active = <span class="number">3</span></span><br><span class="line">            <span class="keyword">if</span> keys == <span class="number">1073741903</span> <span class="keyword">or</span> keys == <span class="number">100</span>:  <span class="comment"># 右</span></span><br><span class="line">                active = <span class="number">4</span></span><br><span class="line"></span><br><span class="line">        <span class="keyword">if</span> event.<span class="built_in">type</span> == pygame.QUIT:</span><br><span class="line">            <span class="keyword">try</span>:</span><br><span class="line">                update_score(userinputs[<span class="number">0</span>], score_int)</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">f&#x27;你的最终分数为：<span class="subst">&#123;score_int&#125;</span>&#x27;</span>)</span><br><span class="line">            <span class="keyword">except</span>:</span><br><span class="line">                <span class="keyword">pass</span></span><br><span class="line">            exit()</span><br></pre></td></tr></table></figure><h4 id="第二部分：InputBox-py"><a href="#第二部分：InputBox-py" class="headerlink" title="第二部分：InputBox.py"></a>第二部分：InputBox.py</h4><h3 id="这个部分是用来显示输入框的-用户登录输入-，这里在Tai-Hsuan-Ho编写的基础上进行了修改"><a href="#这个部分是用来显示输入框的-用户登录输入-，这里在Tai-Hsuan-Ho编写的基础上进行了修改" class="headerlink" title="这个部分是用来显示输入框的(用户登录输入)，这里在Tai-Hsuan Ho编写的基础上进行了修改"></a>这个部分是用来显示输入框的(用户登录输入)，这里在Tai-Hsuan Ho编写的基础上进行了修改</h3><pre><code class="python"># Written by Tai-Hsuan Ho# Created on 2016/11/25, and developed on Python 3.4## Call: InputBox(rect, **args) to create a input bar editor in region defined by rect.## Keyword arguments include:# font: font of the input text.# text: text initially in the input bar when it is created.# bk_image: background image of the text input bar.# bk_color: background color of the text input bar, ignored when bk_image is specified.# bd_color: border color, no border if this keyword is missing or None.# text_color: color of the input text. White color if this keyword is missing.## Return value:# Return (input text, flag), where flag can be True, False, or None when pressing ENTER, # ESC, or receiving QUIT event.## Comments: # The input bar is transparent if both bk_image and bk_color are not specified or None.# There is a game loop inside this function, and all events will be ignore except for # the key events and QUIT, which will be put back to the event queue and exit the game # loop. #import pygamefrom pygame.locals import *COLOR_WHITE = (255, 255, 255)TEXT_MARGIN = 16FPS = 20ALPHABETS = &#39;abcdefghijklmnopqrstuvwxyz&#39;OTHER_KEYS = &#39;`1234567890-=[]\;\&#39;,./&#39;SHIFT_KEYS  = &#39;~!@#$%^&amp;*()_+&#123;&#125;|:&quot;&lt;&gt;?&#39;PRINTABLE = &#39; &#39; + ALPHABETS + OTHER_KEYS + SHIFT_KEYSdef _caps_lock(ch):    # Convert the input char when Caps Lock is turned on.    if ch in ALPHABETS:        return chr(ord(ch) - ord(&#39;a&#39;) + ord(&#39;A&#39;))    else:        return chdef _shift_hold(ch, caps_lock):    # Convert the input char when SHIFT key is hold.    if ch in OTHER_KEYS:        return SHIFT_KEYS[OTHER_KEYS.index(ch)]    elif ch in ALPHABETS and not caps_lock:        return chr(ord(ch) - ord(&#39;a&#39;) + ord(&#39;A&#39;))    else:        return chdef _draw_box(surface, bk_image, bk_color, display_backup, bd_color):    # Draw background image or fill color to the base surface, and draw the border.    (width, height) = surface.get_size()    if bk_image:        surface.blit(bk_image, (0, 0))    elif bk_color:        surface.fill(bk_color)    else:        surface.blit(display_backup, (0, 0))    if bd_color:        pygame.draw.rect(surface, bd_color, surface.get_rect(), 1)def _draw_text(surface, input_chars, font, color, start_index, cursor_index, show_cursor):    # Draw the input text, making sure the char at cursor index is shown on the screen.    x = 0    pos = [x]    for i in range(len(input_chars)):        x += font.size(input_chars[i])[0]        pos.append(x)    # Find the start index, so that the cursor can be seen in the input editor.    if start_index &gt;= cursor_index:        start_index = max(0, cursor_index - 5)    while pos[cursor_index] - pos[start_index] &gt; (surface.get_width() - 2 * TEXT_MARGIN):        start_index += 1    # Draw text in the editor.    h = font.size(&#39;a&#39;)[1]    y = (surface.get_height() - h) // 2    for i in range(start_index, len(input_chars)):        img = font.render(input_chars[i], True, color)        x = TEXT_MARGIN + pos[i] - pos[start_index]        surface.blit(img, (x, y))        if x + img.get_width() + TEXT_MARGIN &gt; surface.get_width():            break    # Draw cursor and return the start index.    if show_cursor:        x = TEXT_MARGIN + pos[cursor_index] - pos[start_index]        pygame.draw.line(surface, color, (x, y), (x, y + h))    return start_indexdef InputBox(rect, **args):    # Draw a box for text input. Return (text, True) when ENTER is pressed, (text, False) when ESC pressed, and (text, None) when QUIT    # event is received. When both bk_image and bk_color are not specified, the input editor is transparent.    try: font = args[&#39;font&#39;]    except: font = pygame.font.Font(None, 32)    try: input_chars = list(args[&#39;text&#39;])    except: input_chars = []    try: bk_image = args[&#39;bk_image&#39;]    except: bk_image = None    try:bd_color = args[&#39;bd_color&#39;]    except:bd_color = None    try:bk_color = args[&#39;bk_color&#39;]    except:bk_color = None    try:text_color = args[&#39;text_color&#39;]    except:text_color = COLOR_WHITE    # Set repeat key event.    pygame.key.set_repeat(500, 50)    # Get display background in region of the input box.    display = pygame.display.get_surface()    display_backup = display.subsurface(rect).copy()    # Create surface.    surface = pygame.Surface(rect.size)    # Get input event and update the input box.    n = 0    start_index = 0    cursor_index = len(input_chars)    bNeedUpdate = True    myClock = pygame.time.Clock()    while True:        for event in pygame.event.get():            if event.type == QUIT:                display.blit(display_backup, rect)                pygame.display.update(rect)                pygame.event.post(pygame.event.Event(QUIT, &#123;&#125;))                return (&#39;&#39;.join(input_chars), None)            # For key up event, check if enter or escape keys are pressed.            elif event.type == KEYUP:                if event.key in (K_ESCAPE, K_RETURN):                    display.blit(display_backup, rect)                    pygame.display.update(rect)                    return (&#39;&#39;.join(input_chars), event.key is K_RETURN)            # For key down event, process backspace, delete, left, right, home, end, and normal key inputs.            elif event.type == KEYDOWN:                bNeedUpdate = True                # Backspace key, deleting the previous char and moving the cursor left.                if event.key == K_BACKSPACE:                    if cursor_index &gt;= 1:                        del input_chars[cursor_index - 1]                        cursor_index -= 1                # Delete key, deleting the current char.                elif event.key == K_DELETE:                    if cursor_index &lt; len(input_chars):                        del input_chars[cursor_index]                # Left arrow key, moving cursor index to the previous char.                elif event.key == K_LEFT:                    if cursor_index &gt;= 1:                        cursor_index -= 1                # Right arrow key, moving cursor index to the next char.                elif event.key == K_RIGHT:                    if cursor_index &lt; len(input_chars):                        cursor_index += 1                # HOME key, moving cursor index to start of the input chars.                elif event.key == K_HOME:                    cursor_index = 0                # END key, moving cursor index to end of the input chars.                elif event.key == K_END:                    cursor_index = len(input_chars)                # Otherwise, add printable key to the input char list.                else:                    try:                        ch = chr(event.key)                        if ch in PRINTABLE:                            shift_hold = (event.mod &amp; KMOD_SHIFT)                            caps_lock = (event.mod &amp; KMOD_CAPS)                            if shift_hold:                                ch = _shift_hold(ch, caps_lock)                            elif caps_lock:                                ch = _caps_lock(ch)                            input_chars.insert(cursor_index, ch)                            cursor_index += 1                    except:                        pass        # Update the display for every 0.5 second or when key down event is received.        if bNeedUpdate or (n == 0) or (n == FPS // 2):            _draw_box(surface, bk_image, bk_color, display_backup, bd_color)            show_cursor = bNeedUpdate or (n == 0)            start_index = _draw_text(surface, input_chars, font, text_color, start_index, cursor_index, show_cursor)            display.blit(surface, rect)            pygame.display.update(rect)            bNeedUpdate = False        n = (n + 1) % FPS        myClock.tick(FPS)# Test codes.def main():    rect = pygame.Rect(200, 250, 200, 40)    name_input = InputBox(rect, text = &#39;&#39;, bd_color = (66, 133, 244), bk_color = (229, 231, 206), text_color = (0, 0, 0))    print(f&#39;你好：&#123;name_input&#125;&#39;)    return name_input</code></pre>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h1 id=&quot;SnakeGame&quot;&gt;&lt;a href=&quot;#SnakeGame&quot; class=&quot;headerlink&quot; title=&quot;SnakeGame&quot;&gt;&lt;/a&gt;SnakeGame&lt;/h1&gt;&lt;p&gt;一个Python &amp;amp; Pygame开发的贪吃蛇小游戏&lt;/p&gt;
&lt;p&gt;由&lt;a</summary>
        
      
    
    
    
    <category term="Python" scheme="https://www.wotemo.com/categories/Python/"/>
    
    <category term="游戏" scheme="https://www.wotemo.com/categories/Python/%E6%B8%B8%E6%88%8F/"/>
    
    
    <category term="Python" scheme="https://www.wotemo.com/tags/Python/"/>
    
    <category term="游戏" scheme="https://www.wotemo.com/tags/%E6%B8%B8%E6%88%8F/"/>
    
    <category term="Pygame" scheme="https://www.wotemo.com/tags/Pygame/"/>
    
    <category term="贪吃蛇" scheme="https://www.wotemo.com/tags/%E8%B4%AA%E5%90%83%E8%9B%87/"/>
    
    <category term="SnakeGame" scheme="https://www.wotemo.com/tags/SnakeGame/"/>
    
  </entry>
  
  <entry>
    <title>一款完备的Python用户注册与登录系统（含验证码及哈希加密）</title>
    <link href="https://www.wotemo.com/posts/4547.html"/>
    <id>https://www.wotemo.com/posts/4547.html</id>
    <published>2022-05-01T07:33:45.000Z</published>
    <updated>2023-08-24T07:33:45.000Z</updated>
    
    <content type="html"><![CDATA[<p>滴滴滴，<a href="https://www.wotemo.com/">沃特陌</a>与您汇报情况</p><p>历经几个周末的时间，我终于将这款<strong>用户注册与登录系统</strong>做出来啦~撒花✿✿ヽ(°▽°)ノ✿</p><h3 id="一-特性"><a href="#一-特性" class="headerlink" title="一.特性"></a>一.特性</h3><ol><li>邮箱验证码验证身份的<strong>真实性</strong></li><li>验证码错误次数<strong>限制</strong></li><li>验证码具有<strong>时效性</strong>，超时将会失效（这里设置的是五分钟）</li><li>可以用’<strong>用户名</strong>‘或者’<strong>邮箱</strong>‘登录，登录方式不再单一</li><li>使用<strong>哈希函数</strong>进行加密，用户信息更安全</li><li>多用<strong>if</strong>等简单语句，小白也能看懂</li></ol><h3 id="二-基础要求"><a href="#二-基础要求" class="headerlink" title="二.基础要求"></a>二.基础要求</h3><ol><li>Python的基本语法</li><li>熟练掌握<strong>if</strong>，<strong>else</strong>，<strong>while</strong>，<strong>for</strong>等语句</li><li>会使用<strong>def</strong>函数</li><li>会<strong>调用模块</strong>（import module）</li><li>了解<strong>哈希函数</strong></li><li>了解<strong>列表</strong>，<strong>字典</strong>的格式与用法</li><li>会使用<strong>open</strong>等语句读写txt文件以存储用户数据</li><li>用到的模块有：<strong>json</strong>，<strong>hashlib</strong>，<strong>random</strong>，<strong>string</strong>，<strong>os</strong>，<strong>time</strong>等</li></ol><h3 id="三-代码部分"><a href="#三-代码部分" class="headerlink" title="三.代码部分"></a>三.代码部分</h3><h4 id="本系统由四部分代码组成："><a href="#本系统由四部分代码组成：" class="headerlink" title="本系统由四部分代码组成："></a>本系统由四部分代码组成：</h4><ol><li><strong>users.py</strong>（主代码，程序在此运行）</li><li><strong>login.py</strong>（用户登录时调用）</li><li><strong>register.py</strong>（用户注册时调用）</li><li><strong>check_code.py</strong>（发送验证码时调用）</li></ol><h4 id="第一部分：uers-py"><a href="#第一部分：uers-py" class="headerlink" title="第一部分：uers.py"></a>第一部分：uers.py</h4><figure class="highlight python"><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="string">&quot;&quot;&quot;用户选择登录或注册，这是主代码，在这里运行程序&quot;&quot;&quot;</span></span><br><span class="line"><span class="comment"># 本代码由沃特陌Wotemo自主编写</span></span><br><span class="line"><span class="keyword">import</span> register</span><br><span class="line"><span class="keyword">import</span> login</span><br><span class="line">activekey = <span class="number">0</span></span><br><span class="line"><span class="keyword">while</span> activekey == <span class="number">0</span>:</span><br><span class="line">    choice = <span class="built_in">input</span>(<span class="string">&#x27;你想登录还是注册？\n(键入&quot;注册&quot;或&quot;登录&quot;以继续...)\n(键入&quot;取消&quot;以取消操作...)\n&#x27;</span>)</span><br><span class="line">    <span class="keyword">if</span> choice == <span class="string">&#x27;注册&#x27;</span>:</span><br><span class="line">       register.register()</span><br><span class="line">       activekey = <span class="number">1</span></span><br><span class="line">    <span class="keyword">elif</span> choice == <span class="string">&#x27;登录&#x27;</span>:</span><br><span class="line">        login.login_way()</span><br><span class="line">        activekey = <span class="number">1</span></span><br><span class="line">    <span class="keyword">elif</span> choice == <span class="string">&#x27;取消&#x27;</span>:</span><br><span class="line">        activekey = <span class="number">1</span></span><br><span class="line">    <span class="keyword">else</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;请不要键入除&quot;注册&quot;或&quot;登录&quot;以外的字符！！！\n&#x27;</span>)</span><br></pre></td></tr></table></figure><h4 id="第二部分：login-py"><a href="#第二部分：login-py" class="headerlink" title="第二部分：login.py"></a>第二部分：login.py</h4><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;用户登录&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> check_code</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">login_by_username</span>():</span><br><span class="line">    check_code.checking()</span><br><span class="line">    filename = <span class="string">&#x27;users.txt&#x27;</span></span><br><span class="line">    username = []</span><br><span class="line">    active = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">import</span> os  <span class="comment"># 检测用户信息是否存在</span></span><br><span class="line">    file_exist = os.path.exists(filename)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> file_exist:</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&#x27;w&#x27;</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;暂无用户数据，请先注册！！！&#x27;</span>)</span><br><span class="line">        quit()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&#x27;r&#x27;</span>) <span class="keyword">as</span> file:</span><br><span class="line">        names = file.read().splitlines()</span><br><span class="line">        names = [name <span class="keyword">for</span> name <span class="keyword">in</span> names]</span><br><span class="line">    <span class="keyword">while</span> active == <span class="number">0</span>:</span><br><span class="line">        usernameinput = <span class="built_in">input</span>(<span class="string">&#x27;请输入你的用户名：&#x27;</span>)</span><br><span class="line">        userpasswordinput = <span class="built_in">input</span>(<span class="string">&#x27;请输入你的密码：&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">len</span>(usernameinput.strip()) == <span class="number">0</span>) <span class="keyword">or</span> (<span class="built_in">len</span>(userpasswordinput.strip()) == <span class="number">0</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;用户名或密码不为空,请重试！！！\n&#x27;</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        <span class="keyword">for</span> name_one <span class="keyword">in</span> names:</span><br><span class="line">            name_one = <span class="built_in">eval</span>(name_one)</span><br><span class="line">            username.append(name_one[<span class="string">&#x27;username&#x27;</span>])</span><br><span class="line">        <span class="keyword">if</span> usernameinput <span class="keyword">in</span> username:</span><br><span class="line">            <span class="keyword">for</span> name_two <span class="keyword">in</span> names:</span><br><span class="line">                name_two = <span class="built_in">eval</span>(name_two)</span><br><span class="line">                <span class="keyword">if</span> usernameinput == name_two[<span class="string">&#x27;username&#x27;</span>]:</span><br><span class="line">                    pws = name_two[<span class="string">&#x27;salt&#x27;</span>] + userpasswordinput</span><br><span class="line">                    after_hash_password = hashlib.sha256(pws.encode(<span class="string">&quot;utf-8&quot;</span>)).hexdigest()</span><br><span class="line">                    <span class="keyword">if</span> after_hash_password == name_two[<span class="string">&#x27;password_hash&#x27;</span>]:</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">&#x27;登录成功！！！&#x27;</span>)</span><br><span class="line">                        active = <span class="number">1</span></span><br><span class="line">                        <span class="keyword">break</span></span><br><span class="line">                    <span class="keyword">elif</span> after_hash_password != name_two[<span class="string">&#x27;password_hash&#x27;</span>]:</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">&#x27;密码错误,请重试！！！\n&#x27;</span>)</span><br><span class="line">                        <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;用户名不存在,请重试！！！\n&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">login_by_email</span>():</span><br><span class="line">    check_code.checking()</span><br><span class="line">    filename = <span class="string">&#x27;users.txt&#x27;</span></span><br><span class="line">    email = []</span><br><span class="line">    active = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">import</span> os  <span class="comment"># 检测用户信息是否存在</span></span><br><span class="line">    file_exist = os.path.exists(filename)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> file_exist:</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&#x27;w&#x27;</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;暂无用户数据，请先注册！！！&#x27;</span>)</span><br><span class="line">        quit()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&#x27;r&#x27;</span>) <span class="keyword">as</span> file:</span><br><span class="line">        names = file.read().splitlines()</span><br><span class="line">        names = [name <span class="keyword">for</span> name <span class="keyword">in</span> names]</span><br><span class="line">    <span class="keyword">while</span> active == <span class="number">0</span>:</span><br><span class="line">        emailinput = <span class="built_in">input</span>(<span class="string">&#x27;请输入你的邮箱：&#x27;</span>)</span><br><span class="line">        userpasswordinput = <span class="built_in">input</span>(<span class="string">&#x27;请输入你的密码：&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">len</span>(emailinput.strip()) == <span class="number">0</span>) <span class="keyword">or</span> (<span class="built_in">len</span>(userpasswordinput.strip()) == <span class="number">0</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;邮箱或密码不为空,请重试！！！\n&#x27;</span>)</span><br><span class="line">            <span class="keyword">continue</span></span><br><span class="line">        <span class="keyword">for</span> name_one <span class="keyword">in</span> names:</span><br><span class="line">            name_one = <span class="built_in">eval</span>(name_one)</span><br><span class="line">            email.append(name_one[<span class="string">&#x27;email&#x27;</span>])</span><br><span class="line">        <span class="keyword">if</span> emailinput <span class="keyword">in</span> email:</span><br><span class="line">            <span class="keyword">for</span> name_two <span class="keyword">in</span> names:</span><br><span class="line">                name_two = <span class="built_in">eval</span>(name_two)</span><br><span class="line">                <span class="keyword">if</span> emailinput == name_two[<span class="string">&#x27;email&#x27;</span>]:</span><br><span class="line">                    pws = name_two[<span class="string">&#x27;salt&#x27;</span>] + userpasswordinput</span><br><span class="line">                    after_hash_password = hashlib.sha256(pws.encode(<span class="string">&quot;utf-8&quot;</span>)).hexdigest()</span><br><span class="line">                    <span class="keyword">if</span> after_hash_password == name_two[<span class="string">&#x27;password_hash&#x27;</span>]:</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">&#x27;登录成功！！！&#x27;</span>)</span><br><span class="line">                        active = <span class="number">1</span></span><br><span class="line">                        <span class="keyword">break</span></span><br><span class="line">                    <span class="keyword">elif</span> after_hash_password != name_two[<span class="string">&#x27;password_hash&#x27;</span>]:</span><br><span class="line">                        <span class="built_in">print</span>(<span class="string">&#x27;密码错误,请重试！！！\n&#x27;</span>)</span><br><span class="line">                        <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;邮箱不存在,请重试！！！\n&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">login_way</span>():</span><br><span class="line">    activekey_login = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> activekey_login == <span class="number">0</span>:</span><br><span class="line">        login_way = <span class="built_in">input</span>(<span class="string">&#x27;用户名还是邮箱登录?\n(键入&quot;用户名&quot;或&quot;邮箱&quot;以继续...)\n(键入&quot;取消&quot;以取消操作...)\n&#x27;</span>)</span><br><span class="line">        <span class="keyword">if</span> login_way == <span class="string">&#x27;用户名&#x27;</span>:</span><br><span class="line">            login_by_username()</span><br><span class="line">            activekey_login = <span class="number">1</span></span><br><span class="line">        <span class="keyword">elif</span> login_way == <span class="string">&#x27;邮箱&#x27;</span>:</span><br><span class="line">            login_by_email()</span><br><span class="line">            activekey_login = <span class="number">1</span></span><br><span class="line">        <span class="keyword">elif</span> login_way == <span class="string">&#x27;取消&#x27;</span>:</span><br><span class="line">            activekey_login = <span class="number">1</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;请不要键入除&quot;用户名&quot;或&quot;邮箱&quot;以外的字符！！！\n&#x27;</span>)</span><br></pre></td></tr></table></figure><h4 id="第三部分：register-py"><a href="#第三部分：register-py" class="headerlink" title="第三部分：register.py"></a>第三部分：register.py</h4><figure class="highlight python"><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="string">&quot;&quot;&quot;用户注册&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">import</span> json</span><br><span class="line"><span class="comment"># 加密密码函数</span></span><br><span class="line"><span class="keyword">import</span> hashlib</span><br><span class="line"><span class="keyword">import</span> random</span><br><span class="line"><span class="keyword">import</span> string</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> check_code</span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">addsalt</span>():</span><br><span class="line">    active = <span class="number">0</span></span><br><span class="line">    <span class="comment">#激活while时使用</span></span><br><span class="line">    salts = <span class="string">&#x27;&#x27;</span></span><br><span class="line">    <span class="comment">#先建一个空白字符串</span></span><br><span class="line">    <span class="keyword">while</span> active &lt; <span class="number">10</span>:</span><br><span class="line">        <span class="comment">#用while循环随机生成一个6位数验证码</span></span><br><span class="line">        salt_one = random.choice(string.digits)</span><br><span class="line">        salt_two = random.choice(string.ascii_letters)</span><br><span class="line">        salt = salt_one + salt_two</span><br><span class="line">        <span class="comment">#随机生成一个0~9的数字</span></span><br><span class="line">        salts += salt</span><br><span class="line">        active += <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> salts</span><br><span class="line"></span><br><span class="line">before_salt = addsalt()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">secret</span>(<span class="params">password</span>):</span><br><span class="line">    pws = before_salt + password</span><br><span class="line">    after_password = hashlib.sha256(pws.encode(<span class="string">&quot;utf-8&quot;</span>)).hexdigest()</span><br><span class="line">    <span class="keyword">return</span> after_password</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">register</span>():</span><br><span class="line">    <span class="string">&quot;&quot;&quot;用户注册&quot;&quot;&quot;</span></span><br><span class="line"></span><br><span class="line">    email = check_code.checking()</span><br><span class="line">    <span class="built_in">print</span>(<span class="string">f&#x27;验证邮箱<span class="subst">&#123;email&#125;</span>将成为注册邮箱&#x27;</span>)</span><br><span class="line">    filename = <span class="string">&#x27;users.txt&#x27;</span></span><br><span class="line">    users = &#123;&#125;</span><br><span class="line">    namecheck = []</span><br><span class="line"></span><br><span class="line">    <span class="keyword">import</span> os <span class="comment">#检测用户信息是否存在</span></span><br><span class="line">    file_exist = os.path.exists(filename)</span><br><span class="line">    <span class="keyword">if</span> <span class="keyword">not</span> file_exist:</span><br><span class="line">        <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&#x27;w&#x27;</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;你是第一个注册的用户！！！&#x27;</span>)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">with</span> <span class="built_in">open</span>(filename, <span class="string">&#x27;r+&#x27;</span>) <span class="keyword">as</span> file:</span><br><span class="line">        names = file.read().splitlines()</span><br><span class="line">        <span class="keyword">for</span> name <span class="keyword">in</span> names:</span><br><span class="line">            name = <span class="built_in">eval</span>(name)</span><br><span class="line">            namecheck.append(name[<span class="string">&#x27;username&#x27;</span>])</span><br><span class="line">        namecheck = [name.lower() <span class="keyword">for</span> name <span class="keyword">in</span> namecheck]</span><br><span class="line">        <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">            username = <span class="built_in">input</span>(<span class="string">&#x27;请输入您要注册的用户名：&#x27;</span>)</span><br><span class="line">            <span class="keyword">if</span> username.lower() <span class="keyword">in</span> namecheck:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&#x27;用户名已存在,请重试！！！&#x27;</span>)</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            <span class="keyword">elif</span> <span class="built_in">len</span>(username.strip()) == <span class="number">0</span>:</span><br><span class="line">                <span class="built_in">print</span>(<span class="string">&#x27;用户名不为空！！！&#x27;</span>)</span><br><span class="line">                <span class="keyword">continue</span></span><br><span class="line">            users[<span class="string">&#x27;username&#x27;</span>] = username</span><br><span class="line">            users[<span class="string">&#x27;email&#x27;</span>] = <span class="built_in">str</span>(email)</span><br><span class="line">            password = <span class="built_in">input</span>(<span class="string">&#x27;请输入密码：&#x27;</span>)</span><br><span class="line">            users[<span class="string">&#x27;password&#x27;</span>] = password <span class="comment">#如果不想显示出用户的真实密码可以把这行代码删除</span></span><br><span class="line">            users[<span class="string">&#x27;password_hash&#x27;</span>] = secret(password)</span><br><span class="line">            users[<span class="string">&#x27;salt&#x27;</span>] = before_salt</span><br><span class="line"></span><br><span class="line">            <span class="comment"># 加密密码</span></span><br><span class="line"></span><br><span class="line">            users = json.dumps(users, ensure_ascii=<span class="literal">False</span>)</span><br><span class="line">            users += <span class="string">f&#x27;\n&#x27;</span></span><br><span class="line">            file.write(users)</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">f&#x27;注册成功！！！\n您的邮箱为：<span class="subst">&#123;email&#125;</span>\n您的用户名为：<span class="subst">&#123;username&#125;</span>\n您的密码为：<span class="subst">&#123;password&#125;</span>&#x27;</span>)</span><br><span class="line">            <span class="keyword">break</span></span><br></pre></td></tr></table></figure><h4 id="第四部分：check-code-py"><a href="#第四部分：check-code-py" class="headerlink" title="第四部分：check_code.py"></a>第四部分：check_code.py</h4><figure class="highlight python"><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></pre></td><td class="code"><pre><span class="line"><span class="string">&quot;&quot;&quot;验证码的发送与验证&quot;&quot;&quot;</span></span><br><span class="line"><span class="keyword">def</span> <span class="title function_">checking</span>():</span><br><span class="line">    <span class="keyword">import</span> random</span><br><span class="line">    <span class="comment"># 调用random函数随机生成验证码</span></span><br><span class="line">    to_addr_input = <span class="built_in">input</span>(<span class="string">f&#x27;我们需要验证您身份的真实性\n请输入您的邮箱,以接收验证码：\n&#x27;</span>)</span><br><span class="line">    check_times = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> (to_addr_input.find(<span class="string">&quot;@&quot;</span>) == -<span class="number">1</span> <span class="keyword">or</span> to_addr_input.find(<span class="string">&quot;.&quot;</span>) == -<span class="number">1</span>):</span><br><span class="line">        <span class="keyword">if</span> (check_times &gt;= <span class="number">2</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;您的错误次数过多，请稍后重试...&#x27;</span>)</span><br><span class="line">            quit()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&quot;错误的邮箱格式&quot;</span>)</span><br><span class="line">        to_addr_input = <span class="built_in">input</span>(<span class="string">&quot;请输入您的邮箱以接收验证码：\n&quot;</span>)</span><br><span class="line">        check_times += <span class="number">1</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">check_code</span>():</span><br><span class="line">        active = <span class="number">0</span></span><br><span class="line">        <span class="comment"># 激活while时使用</span></span><br><span class="line">        codes = <span class="string">&#x27;&#x27;</span></span><br><span class="line">        <span class="comment"># 先建一个空白字符串</span></span><br><span class="line">        <span class="keyword">while</span> active &lt; <span class="number">6</span>:</span><br><span class="line">            <span class="comment"># 用while循环随机生成一个6位数验证码</span></span><br><span class="line">            code = <span class="built_in">str</span>(random.randint(<span class="number">0</span>, <span class="number">9</span>))</span><br><span class="line">            <span class="comment"># 随机生成一个0~9的数字</span></span><br><span class="line">            codes += code</span><br><span class="line">            active += <span class="number">1</span></span><br><span class="line">        <span class="keyword">return</span> codes</span><br><span class="line"></span><br><span class="line">    check_codes = check_code()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">def</span> <span class="title function_">send_code</span>():</span><br><span class="line">        <span class="comment"># smtplib 用于邮件的发信动作</span></span><br><span class="line">        <span class="keyword">import</span> smtplib</span><br><span class="line">        <span class="comment"># email 用于构建邮件内容</span></span><br><span class="line">        <span class="keyword">from</span> email.mime.text <span class="keyword">import</span> MIMEText</span><br><span class="line">        <span class="comment"># 构建邮件头</span></span><br><span class="line">        <span class="keyword">from</span> email.header <span class="keyword">import</span> Header</span><br><span class="line">        <span class="comment"># 发信方的信息：发信邮箱，QQ 邮箱授权码</span></span><br><span class="line">        from_addr = <span class="string">&#x27;此处填写你的发件邮箱，用作发送验证码，例：wotemo@qq.com&#x27;</span></span><br><span class="line">        password = <span class="string">&#x27;此处填写你的smtp密钥，例：edghkejyddhjsoid (在QQ邮箱网站中获取)&#x27;</span></span><br><span class="line">        <span class="comment"># 收信方邮箱</span></span><br><span class="line">        to_addr = to_addr_input</span><br><span class="line">        <span class="comment"># 发信服务器</span></span><br><span class="line">        smtp_server = <span class="string">&#x27;smtp.qq.com&#x27;</span></span><br><span class="line">        <span class="comment"># 邮箱正文内容，第一个参数为内容，第二个参数为格式(plain 为纯文本)，第三个参数为编码</span></span><br><span class="line">        msg = MIMEText(<span class="string">f&#x27;【沃特陌】您正在进行登录/注册操作，您的验证码为：<span class="subst">&#123;check_codes&#125;</span>，请在10分钟内按页面提示完成验证，切勿将验证码泄露给他人。&#x27;</span>, <span class="string">&#x27;plain&#x27;</span>, <span class="string">&#x27;utf-8&#x27;</span>)</span><br><span class="line">        msg[<span class="string">&#x27;From&#x27;</span>] = Header(<span class="string">&#x27;沃特陌&#x27;</span>)</span><br><span class="line">        msg[<span class="string">&#x27;To&#x27;</span>] = Header(<span class="string">&#x27;用户&#x27;</span>)</span><br><span class="line">        msg[<span class="string">&#x27;Subject&#x27;</span>] = Header(<span class="string">&#x27;沃特陌邮箱验证码&#x27;</span>)</span><br><span class="line">        server = smtplib.SMTP_SSL(smtp_server)</span><br><span class="line">        server.connect(smtp_server, <span class="number">465</span>)</span><br><span class="line">        server.login(from_addr, password)</span><br><span class="line">        server.sendmail(from_addr, to_addr, msg.as_string())</span><br><span class="line">        <span class="comment"># 关闭服务器</span></span><br><span class="line">        server.quit()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">try</span>:</span><br><span class="line">        send_code()</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;验证码已发送到您的邮箱中，请注意查收...&#x27;</span>)</span><br><span class="line">        <span class="keyword">import</span> time <span class="comment">#开启计时器，验证码过时失效</span></span><br><span class="line">        time_start = time.time()</span><br><span class="line"></span><br><span class="line">    <span class="keyword">except</span>:</span><br><span class="line">        <span class="built_in">print</span>(<span class="string">&#x27;验证码发送失败，请稍后重试，或反馈客服iswotemo@gmail.com&#x27;</span>)</span><br><span class="line">        quit()</span><br><span class="line"></span><br><span class="line">    check_code_input = <span class="built_in">input</span>(<span class="string">&#x27;请输入您的验证码：&#x27;</span>)</span><br><span class="line">    time_end = time.time()</span><br><span class="line">    time_continue = time_end - time_start</span><br><span class="line">    check_times = <span class="number">0</span></span><br><span class="line">    <span class="keyword">while</span> <span class="literal">True</span>:</span><br><span class="line">        <span class="keyword">if</span> (time_continue &gt; <span class="number">300</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;验证码超时，请稍后重试...&#x27;</span>)</span><br><span class="line">            quit()</span><br><span class="line">        <span class="keyword">if</span> (check_times &gt;= <span class="number">2</span>):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;您的错误次数过多，请稍后重试...&#x27;</span>)</span><br><span class="line">            quit()</span><br><span class="line">        <span class="keyword">if</span> (<span class="built_in">str</span>(check_code_input) == <span class="built_in">str</span>(check_codes)):</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;验证通过，请继续您的操作...&#x27;</span>)</span><br><span class="line">            <span class="keyword">break</span></span><br><span class="line">        <span class="keyword">else</span>:</span><br><span class="line">            <span class="built_in">print</span>(<span class="string">&#x27;验证码错误，请重试...&#x27;</span>)</span><br><span class="line">            check_code_input = <span class="built_in">input</span>(<span class="string">&#x27;请输入您的验证码：&#x27;</span>)</span><br><span class="line">            check_times += <span class="number">1</span></span><br><span class="line">    <span class="keyword">return</span> to_addr_input</span><br></pre></td></tr></table></figure><h3 id="四-系统使用"><a href="#四-系统使用" class="headerlink" title="四.系统使用"></a>四.系统使用</h3><ol><li>将<strong>check_code.py</strong>中的第<strong>38</strong>,<strong>39</strong>行代码改为你自己的邮箱和密钥<img src="https://img.wotemo.com/post/python-users/python-user1.png" alt="check_code部分代码"></li><li>在<strong>users.py</strong>中运行程序，按照提示进行操作即可</li><li>如果不想显示明文密码，可将register.py中的第67行代码注释掉<img src="https://img.wotemo.com/post/python-users/python-user2.png" alt="register部分代码"></li></ol><h3 id="五-进程展示"><a href="#五-进程展示" class="headerlink" title="五.进程展示"></a>五.进程展示</h3><p> 1.<strong>用户注册</strong>展示<br><img src="https://img.wotemo.com/post/python-users/python-user3.png" alt="进程展示--注册"><br> 2.<strong>用户登录</strong>展示<br><img src="https://img.wotemo.com/post/python-users/python-user4.png" alt="进程展示--登录"><br> 3.<strong>邮箱验证码</strong>展示<br> <img src="https://img.wotemo.com/post/python-users/python-user5.png" alt="进程展示--验证码"></p><h3 id="六-代码获取"><a href="#六-代码获取" class="headerlink" title="六.代码获取"></a>六.代码获取</h3><ol><li><a href="https://github.com/wtmxxx/users">Github</a>：<a href="https://github.com/wtmxxx/users">https://github.com/wtmxxx/users</a></li></ol><h3 id="七-联系方式"><a href="#七-联系方式" class="headerlink" title="七.联系方式"></a>七.联系方式</h3><ol><li><a href="https://www.wotemo.com/">我的博客</a>：<a href="https://www.wotemo.com/">https://www.wotemo.com/</a></li><li>邮箱：<a href="mailto:&#x69;&#x73;&#119;&#x6f;&#116;&#101;&#x6d;&#x6f;&#64;&#103;&#x6d;&#97;&#x69;&#x6c;&#46;&#99;&#111;&#x6d;">&#x69;&#x73;&#119;&#x6f;&#116;&#101;&#x6d;&#x6f;&#64;&#103;&#x6d;&#97;&#x69;&#x6c;&#46;&#99;&#111;&#x6d;</a></li><li><a href="https://github.com/wotemo/">Github</a>：<a href="https://github.com/wtmxxx/">https://github.com/wtmxxx/</a></li></ol><p>本代码由<strong>沃特陌</strong>（<a href="https://www.wotemo.com/">Wotemo</a>）自主编写，如有瑕疵可以和我反映哦~<br>如果有不懂的地方也可以和我交流哟(＾Ｕ＾)ノ~ＹＯ</p>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;p&gt;滴滴滴，&lt;a href=&quot;https://www.wotemo.com/&quot;&gt;沃特陌&lt;/a&gt;与您汇报情况&lt;/p&gt;
&lt;p&gt;历经几个周末的时间，我终于将这款&lt;strong&gt;用户注册与登录系统&lt;/strong&gt;做出来啦~撒花✿✿ヽ(°▽°)ノ✿&lt;/p&gt;
&lt;h3</summary>
        
      
    
    
    
    <category term="Python" scheme="https://www.wotemo.com/categories/Python/"/>
    
    <category term="哈希" scheme="https://www.wotemo.com/categories/Python/%E5%93%88%E5%B8%8C/"/>
    
    
    <category term="Python" scheme="https://www.wotemo.com/tags/Python/"/>
    
    <category term="注册" scheme="https://www.wotemo.com/tags/%E6%B3%A8%E5%86%8C/"/>
    
    <category term="登录" scheme="https://www.wotemo.com/tags/%E7%99%BB%E5%BD%95/"/>
    
    <category term="系统" scheme="https://www.wotemo.com/tags/%E7%B3%BB%E7%BB%9F/"/>
    
    <category term="哈希" scheme="https://www.wotemo.com/tags/%E5%93%88%E5%B8%8C/"/>
    
  </entry>
  
  <entry>
    <title>Hello World</title>
    <link href="https://www.wotemo.com/posts/16107.html"/>
    <id>https://www.wotemo.com/posts/16107.html</id>
    <published>2022-05-01T00:33:45.000Z</published>
    <updated>2024-03-23T10:31:28.350Z</updated>
    
    <content type="html"><![CDATA[<h3 id="这是我的第一篇博客"><a href="#这是我的第一篇博客" class="headerlink" title="这是我的第一篇博客"></a>这是我的第一篇博客</h3><figure class="highlight python"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">print</span>(<span class="string">&#x27;Hello World!&#x27;</span>)</span><br></pre></td></tr></table></figure>]]></content>
    
    
      
      
        
        
    <summary type="html">&lt;h3 id=&quot;这是我的第一篇博客&quot;&gt;&lt;a href=&quot;#这是我的第一篇博客&quot; class=&quot;headerlink&quot; title=&quot;这是我的第一篇博客&quot;&gt;&lt;/a&gt;这是我的第一篇博客&lt;/h3&gt;&lt;figure class=&quot;highlight</summary>
        
      
    
    
    
    
  </entry>
  
</feed>
