<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-CN"><generator uri="https://jekyllrb.com/" version="3.10.0">Jekyll</generator><link href="https://xxxcrel.github.io/feed.xml" rel="self" type="application/atom+xml" /><link href="https://xxxcrel.github.io/" rel="alternate" type="text/html" hreflang="zh-CN" /><updated>2026-08-17T13:57:38+08:00</updated><id>https://xxxcrel.github.io/feed.xml</id><title type="html">克里尔肖</title><subtitle>克里尔的个人博客</subtitle><author><name>Xuecheng Wu</name></author><entry><title type="html">Spring initialization mechanism</title><link href="https://xxxcrel.github.io/2021/04/11/spring-init/" rel="alternate" type="text/html" title="Spring initialization mechanism" /><published>2021-04-11T00:00:00+08:00</published><updated>2021-04-11T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2021/04/11/spring-init</id><content type="html" xml:base="https://xxxcrel.github.io/2021/04/11/spring-init/"><![CDATA[<h2 id="spring-bean装载">Spring Bean装载</h2>

<ul>
  <li>屏蔽BeanFactory的后置加载等其他容器相关扩展, 单独看Bean的装载</li>
</ul>

<p>Spring IOC会先调用所有注册进来的BeanPostProcessor</p>
<ul>
  <li>实例化Bean(AbstractAutowireCapableBeanFactory.populateBean)</li>
  <li>初始化bean(AbstractAutowireCapableBeanFactory.initializeBean)
    <ul>
      <li>
        <p>调用Aware接口(BeanNameAware, BeanClassLoaderAware, BeanFactoryAware)</p>
      </li>
      <li>
        <p>调用初始化方法(实现了InitializingBean接口的Bean)</p>
      </li>
    </ul>
  </li>
</ul>

<p>在所有Bean实例化后调用实现了SmartInitializingSingleton的Bean
以上为简要概括, 每个过程中有许多Spring内部使用的高级PostProcessor没有概括</p>

<ul>
  <li>Aware 扩展机制</li>
</ul>

<p>当我们在定义自己的Bean时,可能会想要获得一个Spring IOC容器的引用(ApplicationContext)</p>

<p>Spring提供了一个接口(ApplicationContextAware)满足这个需求, 它会在bean装载的时候调用这个接口的方法</p>

<p>将ApplicationContext注入到你自己的bean中.</p>

<ul>
  <li>深入源码分析:</li>
</ul>

<p>定位到AbstractApplicationContext.refresh()核心方法</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">AbstractApplicationContext</span><span class="o">.</span><span class="na">refresh</span><span class="o">()</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">refresh</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">BeansException</span><span class="o">,</span> <span class="nc">IllegalStateException</span> <span class="o">{</span>
    <span class="kd">synchronized</span> <span class="o">(</span><span class="k">this</span><span class="o">.</span><span class="na">startupShutdownMonitor</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">//....</span>
 
        <span class="c1">//...</span>
     
        <span class="n">prepareBeanFactory</span><span class="o">(</span><span class="n">beanFactory</span><span class="o">);</span>
 
        <span class="k">try</span> <span class="o">{</span>
            <span class="c1">//....BeanFactory的后置处理等相关操作</span>
 
            <span class="c1">// Instantiate all remaining (non-lazy-init) singletons.</span>
            <span class="n">finishBeanFactoryInitialization</span><span class="o">(</span><span class="n">beanFactory</span><span class="o">);</span>
 
            <span class="c1">//....</span>
        <span class="o">}</span>
 
        <span class="k">catch</span> <span class="o">(</span><span class="nc">BeansException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
            <span class="c1">//....</span>
        <span class="o">}</span>
 
        <span class="k">finally</span> <span class="o">{</span>
            <span class="c1">//....</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="o">}</span>
</code></pre></div></div>

<p>首先在prepareBeanFactory() 中会先注册一个ApplicationContextAwareProcessor这是一个 BeanPostProcessor</p>

<p>然后将那些Bean中ApplicationContext、Environment等与Aware相关的字段属性设为忽略,这样Spring的依赖注入机制就不会自动给这个字段注入相关引用</p>

<p>而是由Spring自己控制注入时机,即手动调用setApplicationContext等相关接口方法</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">prepareBeanFactory</span><span class="o">()</span>
<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">prepareBeanFactory</span><span class="o">(</span><span class="nc">ConfigurableListableBeanFactory</span> <span class="n">beanFactory</span><span class="o">)</span> <span class="o">{</span>
        <span class="c1">//....</span>
 
        <span class="c1">// Configure the bean factory with context callbacks.</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">addBeanPostProcessor</span><span class="o">(</span><span class="k">new</span> <span class="nc">ApplicationContextAwareProcessor</span><span class="o">(</span><span class="k">this</span><span class="o">));</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">ignoreDependencyInterface</span><span class="o">(</span><span class="nc">EnvironmentAware</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">ignoreDependencyInterface</span><span class="o">(</span><span class="nc">EmbeddedValueResolverAware</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">ignoreDependencyInterface</span><span class="o">(</span><span class="nc">ResourceLoaderAware</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">ignoreDependencyInterface</span><span class="o">(</span><span class="nc">ApplicationEventPublisherAware</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">ignoreDependencyInterface</span><span class="o">(</span><span class="nc">MessageSourceAware</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">ignoreDependencyInterface</span><span class="o">(</span><span class="nc">ApplicationContextAware</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
        <span class="n">beanFactory</span><span class="o">.</span><span class="na">ignoreDependencyInterface</span><span class="o">(</span><span class="nc">ApplicationStartup</span><span class="o">.</span><span class="na">class</span><span class="o">);</span>
 
        <span class="c1">//....</span>
<span class="o">}</span>
</code></pre></div></div>
<p>在上诉准备完成后,ApplicationContext中的Bean还只是一堆Bean配置信息,并没有真正实例化.</p>

<p>而refresh()方法中的finishBeanFactoryInitizalization(BeanFactory)才会触发Bean的最终实例化流程(首次执行)</p>

<p>这个过程的调用过程:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nc">ApplicationContext</span><span class="o">.</span><span class="na">prepareBeanFactory</span><span class="o">()</span> 
<span class="err">→</span> <span class="nc">DefaultListableBeanFactory</span><span class="o">.</span><span class="na">preInstantiateSingletons</span><span class="o">()</span>
<span class="err">→</span> <span class="nc">AbstractBeanFactory</span><span class="o">.</span><span class="na">getBean</span><span class="o">()</span> 
<span class="err">→</span> <span class="nc">AbstractBeanFactory</span><span class="o">.</span><span class="na">doGetBean</span><span class="o">()</span> 
<span class="err">→</span> <span class="nc">AbstractAutowiredCapableBeanFactory</span><span class="o">.</span><span class="na">createBean</span><span class="o">()</span> <span class="o">*</span> <span class="mi">2</span> 
<span class="err">→</span> <span class="nc">AbstractAutowiredCapableBeanFactory</span><span class="o">.</span><span class="na">resolveBeforeInstantiation</span><span class="o">()</span>
</code></pre></div></div>
<p>而resolveBeforeInstantiztion()中就是触发BeanPostProcessor处理Bean</p>

<p>所以我们自己的Bean中的ApplicaitonContext等字段就是在这个时候通过ApplicationContextAwareProcessor(BeanPostProcessor)注入进去的</p>

<p>注意:</p>

<p>ApplicationContextAwareProcessor只触发下面7个Aware</p>

<ul>
  <li>EnvironmentAware</li>
  <li>EmbeddedValueResolverAware</li>
  <li>ResourceLoaderAware</li>
  <li>ApplicationEnventPublisherAware</li>
  <li>MessageSourceAware</li>
  <li>ApplicationContextAware</li>
  <li>ApplicationStarupAware</li>
</ul>

<p>而</p>
<ul>
  <li>BeanNameAware</li>
  <li>BeanClassLoaderAware</li>
  <li>BeanFactoryAware</li>
</ul>

<p>他们的触发时机在createBean中的populateBean后的initiializeBean方法中调用</p>]]></content><author><name>Xuecheng Wu</name></author><category term="Spring" /><summary type="html"><![CDATA[Spring 初始化机制]]></summary></entry><entry><title type="html">Timing Wheel</title><link href="https://xxxcrel.github.io/2021/03/19/timing-wheel/" rel="alternate" type="text/html" title="Timing Wheel" /><published>2021-03-19T00:00:00+08:00</published><updated>2021-03-19T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2021/03/19/timing-wheel</id><content type="html" xml:base="https://xxxcrel.github.io/2021/03/19/timing-wheel/"><![CDATA[<h2 id="时间轮算法powerjob实现">时间轮算法(Powerjob实现)</h2>

<h3 id="powerjob时间轮算法实现">powerjob时间轮算法实现:</h3>

<p>概览图:</p>

<p><img src="/images/posts/powerjob/timing-wheel-overview.png" alt="fail" /></p>

<p>一个时间轮有ticksPerWheel个槽</p>

<p>每个槽是一个桶, 这个桶其实就是一个链表</p>

<p>每走一个时间间隔(槽的时间间隔)则指针向前走一格,然后执行当前桶内的所有任务</p>

<h3 id="powerjob实现概览">powerjob实现概览:</h3>

<p><img src="/images/posts/powerjob/powerjob-timing-wheel-overview.png" alt="fail" /></p>

<p>HashedWheelBucket就是代表每个槽的桶,里面装了当前槽内需要执行的任务、</p>

<p>为了以O(1)时间复杂度获取当前槽(Bucket), 内部使用一个Bucket数组来表示</p>

<p>private final HashedWheelBucket[] wheel;
HashedWheelTimerFuture里面封装了我们需要执行的的任务</p>

<p><em>Indicator就是控制整个时间轮转动和执行桶内任务的主要线程</em></p>

<h3 id="构造方法">构造方法:</h3>

<p>tickDuration确定时间轮的转动频率, 类比手表指针转动频率</p>

<p>ticksPerWheel确定时间轮槽数量, 类比手表指针数量</p>

<p>processThreadNum未执行当前槽的线程数量,根据业务进行优化</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="nf">HashedWheelTimer</span><span class="o">(</span><span class="kt">long</span> <span class="n">tickDuration</span><span class="o">,</span> <span class="kt">int</span> <span class="n">ticksPerWheel</span><span class="o">,</span> <span class="kt">int</span> <span class="n">processThreadNum</span><span class="o">)</span> <span class="o">{</span>
 
        <span class="k">this</span><span class="o">.</span><span class="na">tickDuration</span> <span class="o">=</span> <span class="n">tickDuration</span><span class="o">;</span>
 
        <span class="c1">// 初始化轮盘，大小格式化为2的N次</span>
        <span class="kt">int</span> <span class="n">ticksNum</span> <span class="o">=</span> <span class="nc">CommonUtils</span><span class="o">.</span><span class="na">formatSize</span><span class="o">(</span><span class="n">ticksPerWheel</span><span class="o">);</span>
        <span class="n">wheel</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashedWheelBucket</span><span class="o">[</span><span class="n">ticksNum</span><span class="o">];</span>
        <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">i</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">i</span> <span class="o">&lt;</span> <span class="n">ticksNum</span><span class="o">;</span> <span class="n">i</span><span class="o">++)</span> <span class="o">{</span>
            <span class="n">wheel</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashedWheelBucket</span><span class="o">();</span>
        <span class="o">}</span>
        <span class="c1">//&amp; mask等价于(%取余操作)</span>
        <span class="n">mask</span> <span class="o">=</span> <span class="n">wheel</span><span class="o">.</span><span class="na">length</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
 
        <span class="c1">// 初始化执行线程池</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">processThreadNum</span> <span class="o">&lt;=</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">taskProcessPool</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
        <span class="o">}</span><span class="k">else</span> <span class="o">{</span>
            <span class="nc">ThreadFactory</span> <span class="n">threadFactory</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadFactoryBuilder</span><span class="o">().</span><span class="na">setNameFormat</span><span class="o">(</span><span class="s">"HashedWheelTimer-Executor-%d"</span><span class="o">).</span><span class="na">build</span><span class="o">();</span>
            <span class="nc">BlockingQueue</span><span class="o">&lt;</span><span class="nc">Runnable</span><span class="o">&gt;</span> <span class="n">queue</span> <span class="o">=</span> <span class="nc">Queues</span><span class="o">.</span><span class="na">newLinkedBlockingQueue</span><span class="o">(</span><span class="mi">16</span><span class="o">);</span>
            <span class="kt">int</span> <span class="n">core</span> <span class="o">=</span> <span class="nc">Math</span><span class="o">.</span><span class="na">max</span><span class="o">(</span><span class="nc">Runtime</span><span class="o">.</span><span class="na">getRuntime</span><span class="o">().</span><span class="na">availableProcessors</span><span class="o">(),</span> <span class="n">processThreadNum</span><span class="o">);</span>
            <span class="n">taskProcessPool</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ThreadPoolExecutor</span><span class="o">(</span><span class="n">core</span><span class="o">,</span> <span class="mi">4</span> <span class="o">*</span> <span class="n">core</span><span class="o">,</span>
                    <span class="mi">60</span><span class="o">,</span> <span class="nc">TimeUnit</span><span class="o">.</span><span class="na">SECONDS</span><span class="o">,</span>
                    <span class="n">queue</span><span class="o">,</span> <span class="n">threadFactory</span><span class="o">,</span> <span class="nc">RejectedExecutionHandlerFactory</span><span class="o">.</span><span class="na">newCallerRun</span><span class="o">(</span><span class="s">"PowerJobTimeWheelPool"</span><span class="o">));</span>
        <span class="o">}</span>
 
        <span class="n">startTime</span> <span class="o">=</span> <span class="nc">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span>
 
        <span class="c1">// 启动后台线程</span>
        <span class="n">indicator</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Indicator</span><span class="o">();</span>
        <span class="k">new</span> <span class="nf">Thread</span><span class="o">(</span><span class="n">indicator</span><span class="o">,</span> <span class="s">"HashedWheelTimer-Indicator"</span><span class="o">).</span><span class="na">start</span><span class="o">();</span>
    <span class="o">}</span>
</code></pre></div></div>
<h3 id="indicatorrun解析">Indicator.run()解析:</h3>

<p><img src="/images/posts/powerjob/powerjob-indicator-run.png" alt="fail" />
该部分代码是整个时间轮的核心运行逻辑:</p>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>
 
            <span class="k">while</span> <span class="o">(!</span><span class="n">stop</span><span class="o">.</span><span class="na">get</span><span class="o">())</span> <span class="o">{</span>
 
                <span class="c1">// 1. 将任务从队列推入时间轮</span>
                <span class="n">pushTaskToBucket</span><span class="o">();</span>
                <span class="c1">// 2. 处理取消的任务</span>
                <span class="n">processCanceledTasks</span><span class="o">();</span>
                <span class="c1">// 3. 等待指针跳向下一刻</span>
                <span class="n">tickTack</span><span class="o">();</span>
                <span class="c1">// 4. 执行定时任务</span>
                <span class="kt">int</span> <span class="n">currentIndex</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="o">(</span><span class="n">tick</span> <span class="o">&amp;</span> <span class="n">mask</span><span class="o">);</span>
                <span class="nc">HashedWheelBucket</span> <span class="n">bucket</span> <span class="o">=</span> <span class="n">wheel</span><span class="o">[</span><span class="n">currentIndex</span><span class="o">];</span>
                <span class="n">bucket</span><span class="o">.</span><span class="na">expireTimerTasks</span><span class="o">(</span><span class="n">tick</span><span class="o">);</span>
 
                <span class="n">tick</span> <span class="o">++;</span>
            <span class="o">}</span>
            <span class="n">latch</span><span class="o">.</span><span class="na">countDown</span><span class="o">();</span>
        <span class="o">}</span>
</code></pre></div></div>

<ol>
  <li>
    <p>pushTaskToBucket(): 将任务队列的超时任务经过计算推入相应槽的Bucket中</p>
  </li>
  <li>
    <p>根据初始化传入的tickDuration进行睡眠,指针进入下一个槽</p>
  </li>
  <li>
    <p>取得当前槽并运行槽中任务</p>
  </li>
</ol>

<p>实现细节:</p>

<ol>
  <li>根据传入的延时时间构造HashedWheelTimerFuture</li>
</ol>

<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//targetTime为任务运行时间点</span>
<span class="kt">long</span> <span class="n">targetTime</span> <span class="o">=</span> <span class="nc">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">()</span> <span class="o">+</span> <span class="n">unit</span><span class="o">.</span><span class="na">toMillis</span><span class="o">(</span><span class="n">delay</span><span class="o">);</span>
<span class="nc">HashedWheelTimerFuture</span> <span class="n">timerFuture</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">HashedWheelTimerFuture</span><span class="o">(</span><span class="n">task</span><span class="o">,</span> <span class="n">targetTime</span><span class="o">);</span>
</code></pre></div></div>

<p>然后在pushTaskToBucket中计算该任务所在槽:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 总共的偏移量</span>
<span class="kt">long</span> <span class="n">offset</span> <span class="o">=</span> <span class="n">timerTask</span><span class="o">.</span><span class="na">targetTime</span> <span class="o">-</span> <span class="n">startTime</span><span class="o">;</span>
<span class="c1">// 总共需要走的指针步数</span>
<span class="n">timerTask</span><span class="o">.</span><span class="na">totalTicks</span> <span class="o">=</span> <span class="n">offset</span> <span class="o">/</span> <span class="n">tickDuration</span><span class="o">;</span>
<span class="c1">// 取余计算 bucket index</span>
<span class="kt">int</span> <span class="n">index</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="o">(</span><span class="n">timerTask</span><span class="o">.</span><span class="na">totalTicks</span> <span class="o">&amp;</span> <span class="n">mask</span><span class="o">);</span>
</code></pre></div></div>

<p>假设时间轮开始运行时间startTime=10000ms, tickDuration=50ms, ticksPerWheel=10</p>

<p>一个新超时任务需求为每5000ms运行一次:</p>

<p>在时间轮运行20000ms后放入时间轮中, targetTime为35000ms</p>

<p>offset = 35000 - 10000 = 25000</p>

<p>totalTicks = 500</p>

<p>index = 500 &amp; 9 = 0</p>

<p>所以这个任务被放在0号槽</p>

<ol>
  <li>长超时任务, 一轮时间轮运行时间不足够运行该任务</li>
</ol>

<p>如上的超时任务, 他虽然在0号槽, 但是却不在这一圈总执行,那么在expireTimerTasks需要判断机制决定该任务是否在该轮执行:</p>

<p>currentTick为走过的tick数, 该值随着时间增加(每调用一次tickTack()则加1)</p>

<p>最红根据totalTicks和currentTick的比较确认是否本轮调度, 如果totalTicks大于当前的指针步数, 则说明该任务不在本轮调度, 直接pass</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 本轮直接调度</span>
 <span class="k">if</span> <span class="o">(</span><span class="n">timerFuture</span><span class="o">.</span><span class="na">totalTicks</span> <span class="o">&lt;=</span> <span class="n">currentTick</span><span class="o">)</span> <span class="o">{</span>
    <span class="k">if</span> <span class="o">(</span><span class="n">timerFuture</span><span class="o">.</span><span class="na">totalTicks</span> <span class="o">&lt;</span> <span class="n">currentTick</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"[HashedWheelTimer] timerFuture.totalTicks &lt; currentTick, please fix the bug"</span><span class="o">);</span>
       <span class="o">}</span>
 
       <span class="k">try</span> <span class="o">{</span>
          <span class="c1">// 提交执行</span>
          <span class="n">runTask</span><span class="o">(</span><span class="n">timerFuture</span><span class="o">);</span>
       <span class="o">}</span><span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">ignore</span><span class="o">)</span> <span class="o">{</span>
       <span class="o">}</span> <span class="k">finally</span> <span class="o">{</span>
          <span class="n">timerFuture</span><span class="o">.</span><span class="na">status</span> <span class="o">=</span> <span class="nc">HashedWheelTimerFuture</span><span class="o">.</span><span class="na">FINISHED</span><span class="o">;</span>
       <span class="o">}</span>
       <span class="k">return</span> <span class="kc">true</span><span class="o">;</span>
 <span class="o">}</span>
</code></pre></div></div>]]></content><author><name>Xuecheng Wu</name></author><category term="powerjob" /><summary type="html"><![CDATA[时间轮算法笔记(powerjob实现)]]></summary></entry><entry><title type="html">Powerjob note</title><link href="https://xxxcrel.github.io/2021/03/18/powerjob-note/" rel="alternate" type="text/html" title="Powerjob note" /><published>2021-03-18T00:00:00+08:00</published><updated>2021-03-18T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2021/03/18/powerjob-note</id><content type="html" xml:base="https://xxxcrel.github.io/2021/03/18/powerjob-note/"><![CDATA[<h2 id="powerjob-笔记">Powerjob 笔记</h2>

<h3 id="1powerjob容器部署">1.powerjob容器部署</h3>
<p><img src="/images/posts/powerjob/powerjob-deploy-container.png" alt="fail" /></p>

<p>powerjob容器部署方案:</p>

<p>用户通过下载模版工程,创建最小化任务执行单元:</p>

<p>MyHandler内部仅需要实现BasicProcessor则可成为一个任务:</p>

<p>由于powerjob基于应用分组的思想,用户通过应用名称注册登入后,通过调度中心部署至该应用的的worker节点</p>

<p>容器上传至worker节点后,其相当于一个单独的执行单元,只需要在任务调度时指定Java Container模式(即上传的jar包)即可和普通任务调度方式一致</p>

<p>其具体部署细节:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//在worker节点接到调度中心请求后, 进行容器部署(即加载jar包)</span>
<span class="kd">public</span> <span class="kd">static</span> <span class="kd">synchronized</span> <span class="kt">void</span> <span class="nf">deployContainer</span><span class="o">(</span><span class="nc">ServerDeployContainerRequest</span> <span class="n">request</span><span class="o">)</span> <span class="o">{</span>

       	<span class="c1">//...</span>
        <span class="nc">OmsContainer</span> <span class="n">oldContainer</span> <span class="o">=</span> <span class="no">CARGO</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">containerId</span><span class="o">);</span>
      	<span class="c1">//...</span>

        <span class="k">try</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(!</span><span class="n">jarFile</span><span class="o">.</span><span class="na">exists</span><span class="o">())</span> <span class="o">{</span>
                <span class="nc">FileUtils</span><span class="o">.</span><span class="na">forceMkdirParent</span><span class="o">(</span><span class="n">jarFile</span><span class="o">);</span>
				<span class="c1">//从URL下载至本地</span>
                <span class="nc">FileUtils</span><span class="o">.</span><span class="na">copyURLToFile</span><span class="o">(</span><span class="k">new</span> <span class="no">URL</span><span class="o">(</span><span class="n">request</span><span class="o">.</span><span class="na">getDownloadURL</span><span class="o">()),</span> <span class="n">jarFile</span><span class="o">,</span> <span class="mi">5000</span><span class="o">,</span> <span class="mi">300000</span><span class="o">);</span>
                <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[OmsContainer-{}] download jar successfully, path={}"</span><span class="o">,</span> <span class="n">containerId</span><span class="o">,</span> <span class="n">jarFile</span><span class="o">.</span><span class="na">getPath</span><span class="o">());</span>
            <span class="o">}</span>

            <span class="c1">// 创建新容器</span>
            <span class="nc">OmsContainer</span> <span class="n">newContainer</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">OmsJarContainer</span><span class="o">(</span><span class="n">containerId</span><span class="o">,</span> <span class="n">containerName</span><span class="o">,</span> <span class="n">version</span><span class="o">,</span> <span class="n">jarFile</span><span class="o">);</span>
			<span class="c1">//具体容器加载机制,如内部使用Spring的ApplicationContext加载我们写的MyHandler</span>
            <span class="n">newContainer</span><span class="o">.</span><span class="na">init</span><span class="o">();</span>

            <span class="c1">// 替换容器 这里很重要, 此处相当于一个将该容器放入一个工厂中,之后调用改容器仅需通过CARGO获得即可</span>
            <span class="no">CARGO</span><span class="o">.</span><span class="na">put</span><span class="o">(</span><span class="n">containerId</span><span class="o">,</span> <span class="n">newContainer</span><span class="o">);</span>
            <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[OmsContainer-{}] deployed new version:{} successfully!"</span><span class="o">,</span> <span class="n">containerId</span><span class="o">,</span> <span class="n">version</span><span class="o">);</span>

            <span class="k">if</span> <span class="o">(</span><span class="n">oldContainer</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                <span class="c1">// 销毁旧容器</span>
                <span class="n">oldContainer</span><span class="o">.</span><span class="na">destroy</span><span class="o">();</span>
            <span class="o">}</span>

        <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"[OmsContainer-{}] deployContainer(name={},version={}) failed."</span><span class="o">,</span> <span class="n">containerId</span><span class="o">,</span> <span class="n">containerName</span><span class="o">,</span> <span class="n">version</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
            <span class="c1">// 如果部署失败，则删除该 jar（本次失败可能是下载jar出错导致，不删除会导致这个版本永久无法重新部署）</span>
            <span class="nc">CommonUtils</span><span class="o">.</span><span class="na">executeIgnoreException</span><span class="o">(()</span> <span class="o">-&gt;</span> <span class="nc">FileUtils</span><span class="o">.</span><span class="na">forceDelete</span><span class="o">(</span><span class="n">jarFile</span><span class="o">));</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="c1">//OmsJarContainer具体加载jar方式</span>
<span class="kd">public</span> <span class="kt">void</span> <span class="nf">init</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>

        <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[OmsJarContainer-{}] start to init container(name={},jarPath={})"</span><span class="o">,</span> <span class="n">containerId</span><span class="o">,</span> <span class="n">name</span><span class="o">,</span> <span class="n">localJarFile</span><span class="o">.</span><span class="na">getPath</span><span class="o">());</span>

        <span class="no">URL</span> <span class="n">jarURL</span> <span class="o">=</span> <span class="n">localJarFile</span><span class="o">.</span><span class="na">toURI</span><span class="o">().</span><span class="na">toURL</span><span class="o">();</span>

        <span class="c1">// 创建类加载器（父类加载为 Worker 的类加载）</span>
        <span class="k">this</span><span class="o">.</span><span class="na">containerClassLoader</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">OhMyClassLoader</span><span class="o">(</span><span class="k">new</span> <span class="no">URL</span><span class="o">[]{</span><span class="n">jarURL</span><span class="o">},</span> <span class="k">this</span><span class="o">.</span><span class="na">getClass</span><span class="o">().</span><span class="na">getClassLoader</span><span class="o">());</span>

        <span class="c1">// 解析 Properties, 该properteis为模版工程里的oms-worker-container.properties, 目的是为了获得MyHandler的包名</span>
        <span class="nc">Properties</span> <span class="n">properties</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Properties</span><span class="o">();</span>
        <span class="k">try</span> <span class="o">(</span><span class="nc">InputStream</span> <span class="n">propertiesURLStream</span> <span class="o">=</span> <span class="n">containerClassLoader</span><span class="o">.</span><span class="na">getResourceAsStream</span><span class="o">(</span><span class="nc">ContainerConstant</span><span class="o">.</span><span class="na">CONTAINER_PROPERTIES_FILE_NAME</span><span class="o">))</span> <span class="o">{</span>

            <span class="k">if</span> <span class="o">(</span><span class="n">propertiesURLStream</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"[OmsJarContainer-{}] can't find {} in jar {}."</span><span class="o">,</span> <span class="n">containerId</span><span class="o">,</span> <span class="nc">ContainerConstant</span><span class="o">.</span><span class="na">CONTAINER_PROPERTIES_FILE_NAME</span><span class="o">,</span> <span class="n">localJarFile</span><span class="o">.</span><span class="na">getPath</span><span class="o">());</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">PowerJobException</span><span class="o">(</span><span class="s">"invalid jar file because of no "</span> <span class="o">+</span> <span class="nc">ContainerConstant</span><span class="o">.</span><span class="na">CONTAINER_PROPERTIES_FILE_NAME</span><span class="o">);</span>
            <span class="o">}</span>

            <span class="n">properties</span><span class="o">.</span><span class="na">load</span><span class="o">(</span><span class="n">propertiesURLStream</span><span class="o">);</span>
            <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[OmsJarContainer-{}] load container properties successfully: {}"</span><span class="o">,</span> <span class="n">containerId</span><span class="o">,</span> <span class="n">properties</span><span class="o">);</span>
        <span class="o">}</span>
		<span class="c1">//取得MyHandler的包名</span>
        <span class="nc">String</span> <span class="n">packageName</span> <span class="o">=</span> <span class="n">properties</span><span class="o">.</span><span class="na">getProperty</span><span class="o">(</span><span class="nc">ContainerConstant</span><span class="o">.</span><span class="na">CONTAINER_PACKAGE_NAME_KEY</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="nc">StringUtils</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">(</span><span class="n">packageName</span><span class="o">))</span> <span class="o">{</span>
            <span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"[OmsJarContainer-{}] get package name failed, developer should't modify the properties file!"</span><span class="o">,</span> <span class="n">containerId</span><span class="o">);</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">PowerJobException</span><span class="o">(</span><span class="s">"invalid jar file"</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="c1">// 加载用户类</span>
        <span class="n">containerClassLoader</span><span class="o">.</span><span class="na">load</span><span class="o">(</span><span class="n">packageName</span><span class="o">);</span>

        <span class="c1">// 创建 Spring IOC 容器（Spring配置文件需要填相对路径）</span>
        <span class="c1">// 需要切换线程上下文类加载器以加载 JDBC 类驱动（SPI）</span>
        <span class="nc">ClassLoader</span> <span class="n">oldCL</span> <span class="o">=</span> <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">getContextClassLoader</span><span class="o">();</span>
        <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">setContextClassLoader</span><span class="o">(</span><span class="n">containerClassLoader</span><span class="o">);</span>
        <span class="k">try</span> <span class="o">{</span>
            <span class="k">this</span><span class="o">.</span><span class="na">container</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ClassPathXmlApplicationContext</span><span class="o">(</span><span class="k">new</span> <span class="nc">String</span><span class="o">[]{</span><span class="nc">ContainerConstant</span><span class="o">.</span><span class="na">SPRING_CONTEXT_FILE_NAME</span><span class="o">},</span> <span class="kc">false</span><span class="o">);</span>
            <span class="k">this</span><span class="o">.</span><span class="na">container</span><span class="o">.</span><span class="na">setClassLoader</span><span class="o">(</span><span class="n">containerClassLoader</span><span class="o">);</span>
			<span class="c1">//到此处MyHandler已经被注册为IOC容器中的一个bean,只需在任务执行时通过判断执行类型为Java Container是通过上面的工厂里获取MyHandler即可执行相应逻辑</span>
            <span class="k">this</span><span class="o">.</span><span class="na">container</span><span class="o">.</span><span class="na">refresh</span><span class="o">();</span>
        <span class="o">}</span><span class="k">finally</span> <span class="o">{</span>
            <span class="nc">Thread</span><span class="o">.</span><span class="na">currentThread</span><span class="o">().</span><span class="na">setContextClassLoader</span><span class="o">(</span><span class="n">oldCL</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[OmsJarContainer-{}] init container(name={},jarPath={}) successfully"</span><span class="o">,</span> <span class="n">containerId</span><span class="o">,</span> <span class="n">name</span><span class="o">,</span> <span class="n">localJarFile</span><span class="o">.</span><span class="na">getPath</span><span class="o">());</span>
    <span class="o">}</span>
</code></pre></div></div>

<h3 id="2powerjob任务传参实现方式">2.powerjob任务传参实现方式</h3>
<p><img src="/images/posts/powerjob/powerjob-send-param.png" alt="fail" />
具体到最终我们实现的BasicProcessor有两处重要的地方,</p>

<p>一是根据调度中心发起调度请求时指定的ProcessorType构建真实运行的processor,具体代码逻辑在初始化ProcessorTracker时:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">private</span> <span class="kt">void</span> <span class="nf">initProcessor</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">Exception</span> <span class="o">{</span>

        <span class="nc">ProcessorType</span> <span class="n">processorType</span> <span class="o">=</span> <span class="nc">ProcessorType</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">instanceInfo</span><span class="o">.</span><span class="na">getProcessorType</span><span class="o">());</span>
        <span class="nc">String</span> <span class="n">processorInfo</span> <span class="o">=</span> <span class="n">instanceInfo</span><span class="o">.</span><span class="na">getProcessorInfo</span><span class="o">();</span>

        <span class="k">switch</span> <span class="o">(</span><span class="n">processorType</span><span class="o">)</span> <span class="o">{</span>
			<span class="c1">//此处为我们直接在项目中实现的BasicProcessor</span>
            <span class="k">case</span> <span class="nl">EMBEDDED_JAVA:</span>
                <span class="c1">// 先使用 Spring 加载</span>
                <span class="k">if</span> <span class="o">(</span><span class="nc">SpringUtils</span><span class="o">.</span><span class="na">supportSpringBean</span><span class="o">())</span> <span class="o">{</span>
                    <span class="k">try</span> <span class="o">{</span>
                        <span class="n">processor</span> <span class="o">=</span> <span class="nc">SpringUtils</span><span class="o">.</span><span class="na">getBean</span><span class="o">(</span><span class="n">processorInfo</span><span class="o">);</span>
                    <span class="o">}</span><span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                        <span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"[ProcessorTracker-{}] no spring bean of processor(className={}), reason is {}."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">processorInfo</span><span class="o">,</span> <span class="nc">ExceptionUtils</span><span class="o">.</span><span class="na">getMessage</span><span class="o">(</span><span class="n">e</span><span class="o">));</span>
                    <span class="o">}</span>
                <span class="o">}</span>
                <span class="c1">// 反射加载</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">processor</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">processor</span> <span class="o">=</span> <span class="nc">ProcessorBeanFactory</span><span class="o">.</span><span class="na">getInstance</span><span class="o">().</span><span class="na">getLocalProcessor</span><span class="o">(</span><span class="n">processorInfo</span><span class="o">);</span>
                <span class="o">}</span>
                <span class="k">break</span><span class="o">;</span>
			<span class="c1">//执行Shell脚本使用的内置Processor</span>
            <span class="k">case</span> <span class="nl">SHELL:</span>
                <span class="n">processor</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ShellProcessor</span><span class="o">(</span><span class="n">instanceId</span><span class="o">,</span> <span class="n">processorInfo</span><span class="o">,</span> <span class="n">instanceInfo</span><span class="o">.</span><span class="na">getInstanceTimeoutMS</span><span class="o">());</span>
                <span class="k">break</span><span class="o">;</span>
			<span class="c1">//执行Python脚本使用的内置Processor</span>
            <span class="k">case</span> <span class="nl">PYTHON:</span>
                <span class="n">processor</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">PythonProcessor</span><span class="o">(</span><span class="n">instanceId</span><span class="o">,</span> <span class="n">processorInfo</span><span class="o">,</span> <span class="n">instanceInfo</span><span class="o">.</span><span class="na">getInstanceTimeoutMS</span><span class="o">());</span>
                <span class="k">break</span><span class="o">;</span>
			<span class="c1">//我们通过调度中心部署至该worker的Processor(jar包部署)</span>
            <span class="k">case</span> <span class="nl">JAVA_CONTAINER:</span>
                <span class="nc">String</span><span class="o">[]</span> <span class="n">split</span> <span class="o">=</span> <span class="n">processorInfo</span><span class="o">.</span><span class="na">split</span><span class="o">(</span><span class="s">"#"</span><span class="o">);</span>
                <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[ProcessorTracker-{}] try to load processor({}) in container({})"</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">split</span><span class="o">[</span><span class="mi">1</span><span class="o">],</span> <span class="n">split</span><span class="o">[</span><span class="mi">0</span><span class="o">]);</span>

                <span class="n">omsContainer</span> <span class="o">=</span> <span class="nc">OmsContainerFactory</span><span class="o">.</span><span class="na">fetchContainer</span><span class="o">(</span><span class="nc">Long</span><span class="o">.</span><span class="na">valueOf</span><span class="o">(</span><span class="n">split</span><span class="o">[</span><span class="mi">0</span><span class="o">]),</span> <span class="kc">true</span><span class="o">);</span>
                <span class="k">if</span> <span class="o">(</span><span class="n">omsContainer</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                    <span class="n">processor</span> <span class="o">=</span> <span class="n">omsContainer</span><span class="o">.</span><span class="na">getProcessor</span><span class="o">(</span><span class="n">split</span><span class="o">[</span><span class="mi">1</span><span class="o">]);</span>
                <span class="o">}</span><span class="k">else</span> <span class="o">{</span>
                    <span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"[ProcessorTracker-{}] load container failed."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">);</span>
                <span class="o">}</span>
                <span class="k">break</span><span class="o">;</span>
            <span class="k">default</span><span class="o">:</span>
                <span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"[ProcessorTracker-{}] unknown processor type: {}."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">processorType</span><span class="o">);</span>
                <span class="k">throw</span> <span class="k">new</span> <span class="nf">PowerJobException</span><span class="o">(</span><span class="s">"unknown processor type of "</span> <span class="o">+</span> <span class="n">processorType</span><span class="o">);</span>
        <span class="o">}</span>

        <span class="k">if</span> <span class="o">(</span><span class="n">processor</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"[ProcessorTracker-{}] fetch Processor(type={},info={}) failed."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">processorType</span><span class="o">,</span> <span class="n">processorInfo</span><span class="o">);</span>
            <span class="k">throw</span> <span class="k">new</span> <span class="nf">PowerJobException</span><span class="o">(</span><span class="s">"fetch Processor failed, please check your processorType and processorInfo config"</span><span class="o">);</span>
        <span class="o">}</span>
    <span class="o">}</span>
<span class="n">二是在ProcessorRunnable中通过InstancesInfo构建TaskContext</span><span class="o">,</span><span class="n">作为Processor</span><span class="o">.</span><span class="na">process</span><span class="o">(</span><span class="nc">TaskContext</span> <span class="n">context</span><span class="o">)</span><span class="nl">的方法入参:</span>

<span class="kd">public</span> <span class="kt">void</span> <span class="nf">innerRun</span><span class="o">()</span> <span class="kd">throws</span> <span class="nc">InterruptedException</span> <span class="o">{</span>

       	<span class="c1">//...</span>
        <span class="c1">// 0. 完成执行上下文准备 &amp; 上报执行信息</span>
        <span class="nc">TaskContext</span> <span class="n">taskContext</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TaskContext</span><span class="o">();</span>
        <span class="nc">BeanUtils</span><span class="o">.</span><span class="na">copyProperties</span><span class="o">(</span><span class="n">task</span><span class="o">,</span> <span class="n">taskContext</span><span class="o">);</span>
        <span class="n">taskContext</span><span class="o">.</span><span class="na">setJobId</span><span class="o">(</span><span class="n">instanceInfo</span><span class="o">.</span><span class="na">getJobId</span><span class="o">());</span>
        <span class="n">taskContext</span><span class="o">.</span><span class="na">setMaxRetryTimes</span><span class="o">(</span><span class="n">instanceInfo</span><span class="o">.</span><span class="na">getTaskRetryNum</span><span class="o">());</span>
        <span class="n">taskContext</span><span class="o">.</span><span class="na">setCurrentRetryTimes</span><span class="o">(</span><span class="n">task</span><span class="o">.</span><span class="na">getFailedCnt</span><span class="o">());</span>
        <span class="n">taskContext</span><span class="o">.</span><span class="na">setJobParams</span><span class="o">(</span><span class="n">instanceInfo</span><span class="o">.</span><span class="na">getJobParams</span><span class="o">());</span>
        <span class="n">taskContext</span><span class="o">.</span><span class="na">setInstanceParams</span><span class="o">(</span><span class="n">instanceInfo</span><span class="o">.</span><span class="na">getInstanceParams</span><span class="o">());</span>
        <span class="n">taskContext</span><span class="o">.</span><span class="na">setOmsLogger</span><span class="o">(</span><span class="n">omsLogger</span><span class="o">);</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">task</span><span class="o">.</span><span class="na">getTaskContent</span><span class="o">()</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">task</span><span class="o">.</span><span class="na">getTaskContent</span><span class="o">().</span><span class="na">length</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">taskContext</span><span class="o">.</span><span class="na">setSubTask</span><span class="o">(</span><span class="nc">SerializerUtils</span><span class="o">.</span><span class="na">deSerialized</span><span class="o">(</span><span class="n">task</span><span class="o">.</span><span class="na">getTaskContent</span><span class="o">()));</span>
        <span class="o">}</span>
        <span class="n">taskContext</span><span class="o">.</span><span class="na">setUserContext</span><span class="o">(</span><span class="nc">OhMyWorker</span><span class="o">.</span><span class="na">getConfig</span><span class="o">().</span><span class="na">getUserContext</span><span class="o">());</span>
        <span class="nc">ThreadLocalStore</span><span class="o">.</span><span class="na">setTask</span><span class="o">(</span><span class="n">task</span><span class="o">);</span>

        <span class="n">reportStatus</span><span class="o">(</span><span class="nc">TaskStatus</span><span class="o">.</span><span class="na">WORKER_PROCESSING</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>

        <span class="c1">//....广播任务相关....</span>

        <span class="k">try</span> <span class="o">{</span>
			<span class="c1">// 正式提交运行, 此处是最终执行我们想要执行的任务方法</span>
            <span class="n">processResult</span> <span class="o">=</span> <span class="n">processor</span><span class="o">.</span><span class="na">process</span><span class="o">(</span><span class="n">taskContext</span><span class="o">);</span>
        <span class="o">}</span><span class="k">catch</span> <span class="o">(</span><span class="nc">Throwable</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
            <span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"[ProcessorRunnable-{}] task(id={},name={}) process failed."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">taskContext</span><span class="o">.</span><span class="na">getTaskId</span><span class="o">(),</span> <span class="n">taskContext</span><span class="o">.</span><span class="na">getTaskName</span><span class="o">(),</span> <span class="n">e</span><span class="o">);</span>
            <span class="n">processResult</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ProcessResult</span><span class="o">(</span><span class="kc">false</span><span class="o">,</span> <span class="n">e</span><span class="o">.</span><span class="na">toString</span><span class="o">());</span>
        <span class="o">}</span>
		<span class="c1">//上报调度中心执行结果</span>
        <span class="n">reportStatus</span><span class="o">(</span><span class="n">processResult</span><span class="o">.</span><span class="na">isSuccess</span><span class="o">()</span> <span class="o">?</span> <span class="nc">TaskStatus</span><span class="o">.</span><span class="na">WORKER_PROCESS_SUCCESS</span> <span class="o">:</span> <span class="nc">TaskStatus</span><span class="o">.</span><span class="na">WORKER_PROCESS_FAILED</span><span class="o">,</span> <span class="n">suit</span><span class="o">(</span><span class="n">processResult</span><span class="o">.</span><span class="na">getMsg</span><span class="o">()),</span> <span class="kc">null</span><span class="o">);</span>
    <span class="o">}</span>
</code></pre></div></div>

<h3 id="3powerjob秒级任务fix_rate-fix_delay通用任务cron调度方式">3.Powerjob秒级任务(FIX_RATE, FIX_DELAY)、通用任务(CRON)调度方式</h3>
<p><img src="/images/posts/powerjob/powerjob-schedule-overview.png" alt="fail" />
区分秒级任务和通用任务的区别, 秒级任务调度频率高, 如果有一个任务每1s执行, 那如果从调度中心统一调度, 那光是从调度中心向worker节点发送请求的时间都已经花费超过1秒了
所以powerjob在处理秒级任务时直接将定时任务下发给worker节点由worker节点自行处理, 只需按时上报任务状态即可, 
而通用任务由于执行频率不高, 有些可能是每天执行一次, 每周执行一次, 而这些低频率的任务则由调度中心统一调度, 减轻worker端压力, 达到分布式任务调度的效果
powerjob中处理秒级任务(FIX_RATE)、通用任务(CRON)具体实现类: com.github.kfcfans.powerjob.server.service.timing.schedule.OmsScheduleService:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">//通用任务处理</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">scheduleCronJob</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Long</span><span class="o">&gt;</span> <span class="n">appIds</span><span class="o">)</span> <span class="o">{</span>

        <span class="kt">long</span> <span class="n">nowTime</span> <span class="o">=</span> <span class="nc">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">();</span>
        <span class="kt">long</span> <span class="n">timeThreshold</span> <span class="o">=</span> <span class="n">nowTime</span> <span class="o">+</span> <span class="mi">2</span> <span class="o">*</span> <span class="no">SCHEDULE_RATE</span><span class="o">;</span>
        <span class="nc">Lists</span><span class="o">.</span><span class="na">partition</span><span class="o">(</span><span class="n">appIds</span><span class="o">,</span> <span class="no">MAX_APP_NUM</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">partAppIds</span> <span class="o">-&gt;</span> <span class="o">{</span>

            <span class="k">try</span> <span class="o">{</span>

                <span class="c1">// 查询条件：任务开启 + 使用CRON表达调度时间 + 指定appId + 即将需要调度执行</span>
                <span class="nc">List</span><span class="o">&lt;</span><span class="nc">JobInfoDO</span><span class="o">&gt;</span> <span class="n">jobInfos</span> <span class="o">=</span> <span class="n">jobInfoRepository</span><span class="o">.</span><span class="na">findByAppIdInAndStatusAndTimeExpressionTypeAndNextTriggerTimeLessThanEqual</span><span class="o">(</span><span class="n">partAppIds</span><span class="o">,</span> <span class="nc">SwitchableStatus</span><span class="o">.</span><span class="na">ENABLE</span><span class="o">.</span><span class="na">getV</span><span class="o">(),</span> <span class="nc">TimeExpressionType</span><span class="o">.</span><span class="na">CRON</span><span class="o">.</span><span class="na">getV</span><span class="o">(),</span> <span class="n">timeThreshold</span><span class="o">);</span>
				<span class="c1">//...</span>

                <span class="c1">// 1. 批量写日志表</span>
               	<span class="c1">//...</span>

                <span class="c1">// 2. 推入时间轮中等待调度执行</span>
                <span class="n">jobInfos</span><span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="n">jobInfoDO</span> <span class="o">-&gt;</span>  <span class="o">{</span>

                    <span class="nc">Long</span> <span class="n">instanceId</span> <span class="o">=</span> <span class="n">jobId2InstanceId</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">jobInfoDO</span><span class="o">.</span><span class="na">getId</span><span class="o">());</span>

                    <span class="kt">long</span> <span class="n">targetTriggerTime</span> <span class="o">=</span> <span class="n">jobInfoDO</span><span class="o">.</span><span class="na">getNextTriggerTime</span><span class="o">();</span>
                    <span class="kt">long</span> <span class="n">delay</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
                    <span class="k">if</span> <span class="o">(</span><span class="n">targetTriggerTime</span> <span class="o">&lt;</span> <span class="n">nowTime</span><span class="o">)</span> <span class="o">{</span>
                        <span class="n">log</span><span class="o">.</span><span class="na">warn</span><span class="o">(</span><span class="s">"[Job-{}] schedule delay, expect: {}, current: {}"</span><span class="o">,</span> <span class="n">jobInfoDO</span><span class="o">.</span><span class="na">getId</span><span class="o">(),</span> <span class="n">targetTriggerTime</span><span class="o">,</span> <span class="nc">System</span><span class="o">.</span><span class="na">currentTimeMillis</span><span class="o">());</span>
                    <span class="o">}</span><span class="k">else</span> <span class="o">{</span>
                        <span class="n">delay</span> <span class="o">=</span> <span class="n">targetTriggerTime</span> <span class="o">-</span> <span class="n">nowTime</span><span class="o">;</span>
                    <span class="o">}</span>
                    <span class="nc">InstanceTimeWheelService</span><span class="o">.</span><span class="na">schedule</span><span class="o">(</span><span class="n">instanceId</span><span class="o">,</span> <span class="n">delay</span><span class="o">,</span> <span class="o">()</span> <span class="o">-&gt;</span> <span class="o">{</span>
                        <span class="n">dispatchService</span><span class="o">.</span><span class="na">dispatch</span><span class="o">(</span><span class="n">jobInfoDO</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="mi">0</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
                    <span class="o">});</span>
                <span class="o">});</span>

                <span class="c1">// 3. 计算下一次调度时间（忽略5S内的重复执行，即CRON模式下最小的连续执行间隔为 SCHEDULE_RATE ms）</span>
                <span class="n">jobInfos</span><span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="n">jobInfoDO</span> <span class="o">-&gt;</span> <span class="o">{</span>
                    <span class="k">try</span> <span class="o">{</span>
                        <span class="n">refreshJob</span><span class="o">(</span><span class="n">jobInfoDO</span><span class="o">);</span>
                    <span class="o">}</span> <span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                        <span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"[Job-{}] refresh job failed."</span><span class="o">,</span> <span class="n">jobInfoDO</span><span class="o">.</span><span class="na">getId</span><span class="o">(),</span> <span class="n">e</span><span class="o">);</span>
                    <span class="o">}</span>
                <span class="o">});</span>
                <span class="n">jobInfoRepository</span><span class="o">.</span><span class="na">flush</span><span class="o">();</span>


            <span class="o">}</span><span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"[CronScheduler] schedule cron job failed."</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">});</span>
    <span class="o">}</span>
<span class="c1">//处理秒级任务</span>
<span class="kd">private</span> <span class="kt">void</span> <span class="nf">scheduleFrequentJob</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;</span><span class="nc">Long</span><span class="o">&gt;</span> <span class="n">appIds</span><span class="o">)</span> <span class="o">{</span>

        <span class="nc">Lists</span><span class="o">.</span><span class="na">partition</span><span class="o">(</span><span class="n">appIds</span><span class="o">,</span> <span class="no">MAX_APP_NUM</span><span class="o">).</span><span class="na">forEach</span><span class="o">(</span><span class="n">partAppIds</span> <span class="o">-&gt;</span> <span class="o">{</span>
            <span class="k">try</span> <span class="o">{</span>
                <span class="c1">// 查询所有的秒级任务（只包含ID）</span>
                <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Long</span><span class="o">&gt;</span> <span class="n">jobIds</span> <span class="o">=</span> <span class="n">jobInfoRepository</span><span class="o">.</span><span class="na">findByAppIdInAndStatusAndTimeExpressionTypeIn</span><span class="o">(</span><span class="n">partAppIds</span><span class="o">,</span> <span class="nc">SwitchableStatus</span><span class="o">.</span><span class="na">ENABLE</span><span class="o">.</span><span class="na">getV</span><span class="o">(),</span> <span class="nc">TimeExpressionType</span><span class="o">.</span><span class="na">frequentTypes</span><span class="o">);</span>
               	<span class="c1">//...</span>
                <span class="c1">// 查询日志记录表中是否存在相关的任务</span>
               	<span class="c1">//...</span>

                <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Long</span><span class="o">&gt;</span> <span class="n">notRunningJobIds</span> <span class="o">=</span> <span class="nc">Lists</span><span class="o">.</span><span class="na">newLinkedList</span><span class="o">();</span>
                <span class="n">jobIds</span><span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="n">jobId</span> <span class="o">-&gt;</span> <span class="o">{</span>
                    <span class="k">if</span> <span class="o">(!</span><span class="n">runningJobIdSet</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">jobId</span><span class="o">))</span> <span class="o">{</span>
                        <span class="n">notRunningJobIds</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="n">jobId</span><span class="o">);</span>
                    <span class="o">}</span>
                <span class="o">});</span>

              	<span class="c1">//...</span>

                <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[FrequentScheduler] These frequent jobs will be scheduled： {}."</span><span class="o">,</span> <span class="n">notRunningJobIds</span><span class="o">);</span>
                <span class="n">notRunningJobIds</span><span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="n">jobId</span> <span class="o">-&gt;</span> <span class="o">{</span>
                    <span class="nc">Optional</span><span class="o">&lt;</span><span class="nc">JobInfoDO</span><span class="o">&gt;</span> <span class="n">jobInfoOpt</span> <span class="o">=</span> <span class="n">jobInfoRepository</span><span class="o">.</span><span class="na">findById</span><span class="o">(</span><span class="n">jobId</span><span class="o">);</span>
					<span class="c1">//此处直接调用jobService.runJob, 相当于web端点击执行一次按钮, 调度中心无需调度, 执行压力交给worker节点</span>
                    <span class="n">jobInfoOpt</span><span class="o">.</span><span class="na">ifPresent</span><span class="o">(</span><span class="n">jobInfoDO</span> <span class="o">-&gt;</span> <span class="n">jobService</span><span class="o">.</span><span class="na">runJob</span><span class="o">(</span><span class="n">jobInfoDO</span><span class="o">.</span><span class="na">getAppId</span><span class="o">(),</span> <span class="n">jobId</span><span class="o">,</span> <span class="kc">null</span><span class="o">,</span> <span class="mi">0L</span><span class="o">));</span>
                <span class="o">});</span>
            <span class="o">}</span><span class="k">catch</span> <span class="o">(</span><span class="nc">Exception</span> <span class="n">e</span><span class="o">)</span> <span class="o">{</span>
                <span class="n">log</span><span class="o">.</span><span class="na">error</span><span class="o">(</span><span class="s">"[FrequentScheduler] schedule frequent job failed."</span><span class="o">,</span> <span class="n">e</span><span class="o">);</span>
            <span class="o">}</span>
        <span class="o">});</span>
    <span class="o">}</span>
</code></pre></div></div>

<h3 id="4powerjob-worker端任务分发原理">4.powerjob-worker端任务分发原理</h3>
<p><img src="/images/posts/powerjob/powerjob-worker-dispatch.png" alt="fail" />
powerjob使用AppName分组, 那么worker端就可以通过配置相同的appName来部署worker集群, 而在调度中心分发任务时, 则需要一个机制来确定如何分发任务到集群中</p>

<p>首先当我们指定该app集群中某一个worker时, 即新建任务时写入某个worker IP,此时则用户自己明确该任务由当前worker(ip指定的worker)去执行.
<img src="/images/powerjob/powerjob-workerip.png" alt="fail" />
当该地址为空时,默认为该app下全部worker均需要接受这个调度任务, 那么此时就需要一种手段去分发这些任务:而powerjob将分发能力交给worker处理</p>

<p>powerjob使用第一个可使用的worker为master, 最终由master(worker)去分发任务至该集群中所有worker:</p>

<p>powerjob选主源码:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">dispatch</span><span class="o">(</span><span class="nc">JobInfoDO</span> <span class="n">jobInfo</span><span class="o">,</span> <span class="kt">long</span> <span class="n">instanceId</span><span class="o">,</span> <span class="kt">long</span> <span class="n">currentRunningTimes</span><span class="o">,</span> <span class="nc">String</span> <span class="n">instanceParams</span><span class="o">,</span> <span class="nc">Long</span> <span class="n">wfInstanceId</span><span class="o">)</span> <span class="o">{</span>
		
		<span class="c1">//....一些前置检测</span>

        <span class="c1">// 获取当前所有可用的Worker</span>
        <span class="nc">List</span><span class="o">&lt;</span><span class="nc">WorkerInfo</span><span class="o">&gt;</span> <span class="n">allAvailableWorker</span> <span class="o">=</span> <span class="nc">WorkerClusterManagerService</span><span class="o">.</span><span class="na">getSortedAvailableWorkers</span><span class="o">(</span><span class="n">jobInfo</span><span class="o">.</span><span class="na">getAppId</span><span class="o">(),</span> <span class="n">jobInfo</span><span class="o">.</span><span class="na">getMinCpuCores</span><span class="o">(),</span> <span class="n">jobInfo</span><span class="o">.</span><span class="na">getMinMemorySpace</span><span class="o">(),</span> <span class="n">jobInfo</span><span class="o">.</span><span class="na">getMinDiskSpace</span><span class="o">());</span>

		<span class="c1">//过滤空worker和不属于该appName下的集群worker</span>
        <span class="n">allAvailableWorker</span><span class="o">.</span><span class="na">removeIf</span><span class="o">(</span><span class="n">worker</span> <span class="o">-&gt;</span> <span class="o">{</span>
            <span class="c1">// 空，则全部不过滤</span>
            <span class="k">if</span> <span class="o">(</span><span class="nc">StringUtils</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">(</span><span class="n">jobInfo</span><span class="o">.</span><span class="na">getDesignatedWorkers</span><span class="o">()))</span> <span class="o">{</span>
                <span class="k">return</span> <span class="kc">false</span><span class="o">;</span>
            <span class="o">}</span>
            <span class="c1">// 非空，只有匹配上的 worker 才不被过滤</span>
            <span class="nc">Set</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">designatedWorkers</span> <span class="o">=</span> <span class="nc">Sets</span><span class="o">.</span><span class="na">newHashSet</span><span class="o">(</span><span class="n">commaSplitter</span><span class="o">.</span><span class="na">splitToList</span><span class="o">(</span><span class="n">jobInfo</span><span class="o">.</span><span class="na">getDesignatedWorkers</span><span class="o">()));</span>
            <span class="k">return</span> <span class="o">!</span><span class="n">designatedWorkers</span><span class="o">.</span><span class="na">contains</span><span class="o">(</span><span class="n">worker</span><span class="o">.</span><span class="na">getAddress</span><span class="o">());</span>
        <span class="o">});</span>

     	<span class="c1">//...</span>

        <span class="c1">// 限定集群大小（0代表不限制）</span>
        <span class="k">if</span> <span class="o">(</span><span class="n">jobInfo</span><span class="o">.</span><span class="na">getMaxWorkerCount</span><span class="o">()</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
            <span class="k">if</span> <span class="o">(</span><span class="n">allAvailableWorker</span><span class="o">.</span><span class="na">size</span><span class="o">()</span> <span class="o">&gt;</span> <span class="n">jobInfo</span><span class="o">.</span><span class="na">getMaxWorkerCount</span><span class="o">())</span> <span class="o">{</span>
                <span class="n">allAvailableWorker</span> <span class="o">=</span> <span class="n">allAvailableWorker</span><span class="o">.</span><span class="na">subList</span><span class="o">(</span><span class="mi">0</span><span class="o">,</span> <span class="n">jobInfo</span><span class="o">.</span><span class="na">getMaxWorkerCount</span><span class="o">());</span>
            <span class="o">}</span>
        <span class="o">}</span>
        <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">workerIpList</span> <span class="o">=</span> <span class="n">allAvailableWorker</span><span class="o">.</span><span class="na">stream</span><span class="o">().</span><span class="na">map</span><span class="o">(</span><span class="nl">WorkerInfo:</span><span class="o">:</span><span class="n">getAddress</span><span class="o">).</span><span class="na">collect</span><span class="o">(</span><span class="nc">Collectors</span><span class="o">.</span><span class="na">toList</span><span class="o">());</span>

        <span class="c1">// 构造请求</span>
        <span class="nc">ServerScheduleJobReq</span> <span class="n">req</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ServerScheduleJobReq</span><span class="o">();</span>
       	<span class="c1">//...填充req</span>

        <span class="c1">// 发送请求（不可靠，需要一个后台线程定期轮询状态）</span>
		<span class="c1">// 此处设置第一个可用的worker为master worker</span>
        <span class="nc">WorkerInfo</span> <span class="n">taskTracker</span> <span class="o">=</span> <span class="n">allAvailableWorker</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="mi">0</span><span class="o">);</span>
        <span class="nc">String</span> <span class="n">taskTrackerAddress</span> <span class="o">=</span> <span class="n">taskTracker</span><span class="o">.</span><span class="na">getAddress</span><span class="o">();</span>
		
		<span class="c1">//向master worker发起任务请求</span>
        <span class="n">transportService</span><span class="o">.</span><span class="na">tell</span><span class="o">(</span><span class="nc">Protocol</span><span class="o">.</span><span class="na">of</span><span class="o">(</span><span class="n">taskTracker</span><span class="o">.</span><span class="na">getProtocol</span><span class="o">()),</span> <span class="n">taskTrackerAddress</span><span class="o">,</span> <span class="n">req</span><span class="o">);</span>
        <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[Dispatcher-{}|{}] send schedule request to TaskTracker[protocol:{},address:{}] successfully: {}."</span><span class="o">,</span> <span class="n">jobId</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">taskTracker</span><span class="o">.</span><span class="na">getProtocol</span><span class="o">(),</span> <span class="n">taskTrackerAddress</span><span class="o">,</span> <span class="n">req</span><span class="o">);</span>

        <span class="c1">// 修改状态</span>
        <span class="n">instanceInfoRepository</span><span class="o">.</span><span class="na">update4TriggerSucceed</span><span class="o">(</span><span class="n">instanceId</span><span class="o">,</span> <span class="no">WAITING_WORKER_RECEIVE</span><span class="o">.</span><span class="na">getV</span><span class="o">(),</span> <span class="n">currentRunningTimes</span> <span class="o">+</span> <span class="mi">1</span><span class="o">,</span> <span class="n">current</span><span class="o">,</span> <span class="n">taskTrackerAddress</span><span class="o">,</span> <span class="n">dbInstanceParams</span><span class="o">,</span> <span class="n">now</span><span class="o">);</span>

        <span class="c1">// 装载缓存</span>
        <span class="n">instanceMetadataService</span><span class="o">.</span><span class="na">loadJobInfo</span><span class="o">(</span><span class="n">instanceId</span><span class="o">,</span> <span class="n">jobInfo</span><span class="o">);</span>
    <span class="o">}</span>
</code></pre></div></div>
<p>当请求到达master worker时, worker根据调度类型创建具体的TaskTracker(CommonTaskTracker, FrequentTaskTracker)</p>

<p>TaskTracker初始化时根据ServerScheduleJobReq中携带的所有worker地址创建ProcessorTracker:</p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">protected</span> <span class="nf">TaskTracker</span><span class="o">(</span><span class="nc">ServerScheduleJobReq</span> <span class="n">req</span><span class="o">)</span> <span class="o">{</span>

        <span class="c1">// 初始化成员变量</span>
       	<span class="c1">//...</span>
        <span class="c1">// 特殊处理超时时间</span>
        <span class="c1">//...</span>
        <span class="c1">// 赋予时间表达式类型</span>
        <span class="c1">//...</span>
        <span class="c1">// 保护性操作</span>
      	<span class="c1">//...</span>

		<span class="c1">//此处初始化所有ProcessorTracker</span>
        <span class="k">this</span><span class="o">.</span><span class="na">ptStatusHolder</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ProcessorTrackerStatusHolder</span><span class="o">(</span><span class="n">req</span><span class="o">.</span><span class="na">getAllWorkerAddress</span><span class="o">());</span>
       
        <span class="c1">// 构建缓存</span>
       	<span class="c1">//...</span>

        <span class="c1">// 构建分段锁</span>
        <span class="c1">// ...</span>

        <span class="c1">// 子类自定义初始化操作</span>
        <span class="c1">//...</span>

        <span class="n">log</span><span class="o">.</span><span class="na">info</span><span class="o">(</span><span class="s">"[TaskTracker-{}] create TaskTracker successfully."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">);</span>
    <span class="o">}</span>
</code></pre></div></div>
<p>之后TaskTracker会开启一个Dispatcher线程处理分发任务,通过初始化时保存的集群中的ProcessorTracker进行任务分发:
<em>com.github.kfcfans.powerjob.worker.core.tracker.task.TaskTracker.Dispatcher</em></p>
<div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kt">void</span> <span class="nf">run</span><span class="o">()</span> <span class="o">{</span>

           	<span class="c1">//...</span>

            <span class="c1">// 1. 获取可以派发任务的 ProcessorTracker</span>
            <span class="nc">List</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">availablePtIps</span> <span class="o">=</span> <span class="n">ptStatusHolder</span><span class="o">.</span><span class="na">getAvailableProcessorTrackers</span><span class="o">();</span>

            <span class="c1">// 2. 没有可用 ProcessorTracker，本次不派发</span>
            <span class="c1">//...</span>

            <span class="c1">// 3. 避免大查询，分批派发任务</span>
            <span class="c1">//...</span>

            <span class="c1">// 4. 循环查询数据库，获取需要派发的任务</span>
            <span class="k">while</span> <span class="o">(</span><span class="n">maxDispatchNum</span> <span class="o">&gt;</span> <span class="n">currentDispatchNum</span><span class="o">)</span> <span class="o">{</span>

                <span class="kt">int</span> <span class="n">dbQueryLimit</span> <span class="o">=</span> <span class="nc">Math</span><span class="o">.</span><span class="na">min</span><span class="o">(</span><span class="no">DB_QUERY_LIMIT</span><span class="o">,</span> <span class="o">(</span><span class="kt">int</span><span class="o">)</span> <span class="n">maxDispatchNum</span><span class="o">);</span>
                <span class="nc">List</span><span class="o">&lt;</span><span class="nc">TaskDO</span><span class="o">&gt;</span> <span class="n">needDispatchTasks</span> <span class="o">=</span> <span class="n">taskPersistenceService</span><span class="o">.</span><span class="na">getTaskByStatus</span><span class="o">(</span><span class="n">instanceId</span><span class="o">,</span> <span class="nc">TaskStatus</span><span class="o">.</span><span class="na">WAITING_DISPATCH</span><span class="o">,</span> <span class="n">dbQueryLimit</span><span class="o">);</span>
                <span class="n">currentDispatchNum</span> <span class="o">+=</span> <span class="n">needDispatchTasks</span><span class="o">.</span><span class="na">size</span><span class="o">();</span>
				
				<span class="c1">//此处通过迭代分发任务至可获得的worker中</span>
                <span class="n">needDispatchTasks</span><span class="o">.</span><span class="na">forEach</span><span class="o">(</span><span class="n">task</span> <span class="o">-&gt;</span> <span class="o">{</span>
                    <span class="c1">// 获取 ProcessorTracker 地址，如果 Task 中自带了 Address，则使用该 Address</span>
                    <span class="nc">String</span> <span class="n">ptAddress</span> <span class="o">=</span> <span class="n">task</span><span class="o">.</span><span class="na">getAddress</span><span class="o">();</span>
                    <span class="k">if</span> <span class="o">(</span><span class="nc">StringUtils</span><span class="o">.</span><span class="na">isEmpty</span><span class="o">(</span><span class="n">ptAddress</span><span class="o">)</span> <span class="o">||</span> <span class="nc">RemoteConstant</span><span class="o">.</span><span class="na">EMPTY_ADDRESS</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">ptAddress</span><span class="o">))</span> <span class="o">{</span>
                        <span class="n">ptAddress</span> <span class="o">=</span> <span class="n">availablePtIps</span><span class="o">.</span><span class="na">get</span><span class="o">(</span><span class="n">index</span><span class="o">.</span><span class="na">getAndIncrement</span><span class="o">()</span> <span class="o">%</span> <span class="n">availablePtIps</span><span class="o">.</span><span class="na">size</span><span class="o">());</span>
                    <span class="o">}</span>
                    <span class="n">dispatchTask</span><span class="o">(</span><span class="n">task</span><span class="o">,</span> <span class="n">ptAddress</span><span class="o">);</span>
                <span class="o">});</span>

                <span class="c1">// 数量不足 或 查询失败，则终止循环</span>
                <span class="c1">//...</span>
            <span class="o">}</span>

            <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"[TaskTracker-{}] dispatched {} tasks,using time {}."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">currentDispatchNum</span><span class="o">,</span> <span class="n">stopwatch</span><span class="o">.</span><span class="na">stop</span><span class="o">());</span>
        <span class="o">}</span>
    <span class="o">}</span>

	<span class="kd">protected</span> <span class="kt">void</span> <span class="nf">dispatchTask</span><span class="o">(</span><span class="nc">TaskDO</span> <span class="n">task</span><span class="o">,</span> <span class="nc">String</span> <span class="n">processorTrackerAddress</span><span class="o">)</span> <span class="o">{</span>

        <span class="c1">// 1. 持久化，更新数据库（如果更新数据库失败，可能导致重复执行，先不处理）</span>
       	<span class="c1">//...</span>
        <span class="c1">// 写入处理该任务的 ProcessorTracker</span>
       	<span class="c1">//...</span>

        <span class="c1">// 2. 更新 ProcessorTrackerStatus 状态</span>
      	<span class="c1">//...</span>
        <span class="c1">// 3. 初始化缓存</span>
      	<span class="c1">//...</span>

        <span class="c1">// 4. 任务派发</span>
        <span class="nc">TaskTrackerStartTaskReq</span> <span class="n">startTaskReq</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">TaskTrackerStartTaskReq</span><span class="o">(</span><span class="n">instanceInfo</span><span class="o">,</span> <span class="n">task</span><span class="o">);</span>
        <span class="nc">String</span> <span class="n">ptActorPath</span> <span class="o">=</span> <span class="nc">AkkaUtils</span><span class="o">.</span><span class="na">getAkkaWorkerPath</span><span class="o">(</span><span class="n">processorTrackerAddress</span><span class="o">,</span> <span class="nc">RemoteConstant</span><span class="o">.</span><span class="na">PROCESSOR_TRACKER_ACTOR_NAME</span><span class="o">);</span>
        <span class="nc">ActorSelection</span> <span class="n">ptActor</span> <span class="o">=</span> <span class="nc">OhMyWorker</span><span class="o">.</span><span class="na">actorSystem</span><span class="o">.</span><span class="na">actorSelection</span><span class="o">(</span><span class="n">ptActorPath</span><span class="o">);</span>
        <span class="n">ptActor</span><span class="o">.</span><span class="na">tell</span><span class="o">(</span><span class="n">startTaskReq</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>

        <span class="n">log</span><span class="o">.</span><span class="na">debug</span><span class="o">(</span><span class="s">"[TaskTracker-{}] dispatch task(taskId={},taskName={}) successfully."</span><span class="o">,</span> <span class="n">instanceId</span><span class="o">,</span> <span class="n">task</span><span class="o">.</span><span class="na">getTaskId</span><span class="o">(),</span> <span class="n">task</span><span class="o">.</span><span class="na">getTaskName</span><span class="o">());</span>
    <span class="o">}</span>
</code></pre></div></div>
<p>其他非master worker则直接通过akka接收TaskTrackerStartTaskReq请求并执行相应任务, 只有master worker会接收ServerScheduleJobReq请求并分发任务到其他worker节点中</p>]]></content><author><name>Xuecheng Wu</name></author><category term="powerjob" /><summary type="html"><![CDATA[Powerjob 笔记]]></summary></entry><entry><title type="html">Java HashMap</title><link href="https://xxxcrel.github.io/2020/11/24/java-hashmap-two/" rel="alternate" type="text/html" title="Java HashMap" /><published>2020-11-24T00:00:00+08:00</published><updated>2020-11-24T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2020/11/24/java-hashmap-two</id><content type="html" xml:base="https://xxxcrel.github.io/2020/11/24/java-hashmap-two/"><![CDATA[<h2 id="java-hashmap笔记二">Java HashMap笔记二</h2>

<ul>
  <li>
    <p>HashMap的resize(阿成直呼妙啊)</p>

    <p><strong>上源码</strong></p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;[]</span> <span class="nf">resize</span><span class="o">()</span> <span class="o">{</span>
      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;[]</span> <span class="n">oldTab</span> <span class="o">=</span> <span class="n">table</span><span class="o">;</span>
      <span class="kt">int</span> <span class="n">oldCap</span> <span class="o">=</span> <span class="o">(</span><span class="n">oldTab</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="n">oldTab</span><span class="o">.</span><span class="na">length</span><span class="o">;</span>
      <span class="kt">int</span> <span class="n">oldThr</span> <span class="o">=</span> <span class="n">threshold</span><span class="o">;</span>
      <span class="kt">int</span> <span class="n">newCap</span><span class="o">,</span> <span class="n">newThr</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
      <span class="c1">//分析三</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">oldCap</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
          <span class="k">if</span> <span class="o">(</span><span class="n">oldCap</span> <span class="o">&gt;=</span> <span class="no">MAXIMUM_CAPACITY</span><span class="o">)</span> <span class="o">{</span>
              <span class="n">threshold</span> <span class="o">=</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">;</span>
              <span class="k">return</span> <span class="n">oldTab</span><span class="o">;</span>
          <span class="o">}</span>
          <span class="k">else</span> <span class="nf">if</span> <span class="o">((</span><span class="n">newCap</span> <span class="o">=</span> <span class="n">oldCap</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="o">)</span> <span class="o">&lt;</span> <span class="no">MAXIMUM_CAPACITY</span> <span class="o">&amp;&amp;</span>
                   <span class="n">oldCap</span> <span class="o">&gt;=</span> <span class="no">DEFAULT_INITIAL_CAPACITY</span><span class="o">)</span>
              <span class="n">newThr</span> <span class="o">=</span> <span class="n">oldThr</span> <span class="o">&lt;&lt;</span> <span class="mi">1</span><span class="o">;</span> <span class="c1">// double threshold</span>
      <span class="o">}</span>
      <span class="c1">//分析二</span>
      <span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">oldThr</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="o">)</span> <span class="c1">// initial capacity was placed in threshold</span>
          <span class="n">newCap</span> <span class="o">=</span> <span class="n">oldThr</span><span class="o">;</span>
      <span class="c1">//分析一</span>
      <span class="k">else</span> <span class="o">{</span>               <span class="c1">// zero initial threshold signifies using defaults</span>
          <span class="n">newCap</span> <span class="o">=</span> <span class="no">DEFAULT_INITIAL_CAPACITY</span><span class="o">;</span>
          <span class="n">newThr</span> <span class="o">=</span> <span class="o">(</span><span class="kt">int</span><span class="o">)(</span><span class="no">DEFAULT_LOAD_FACTOR</span> <span class="o">*</span> <span class="no">DEFAULT_INITIAL_CAPACITY</span><span class="o">);</span>
      <span class="o">}</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">newThr</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
          <span class="kt">float</span> <span class="n">ft</span> <span class="o">=</span> <span class="o">(</span><span class="kt">float</span><span class="o">)</span><span class="n">newCap</span> <span class="o">*</span> <span class="n">loadFactor</span><span class="o">;</span>
          <span class="n">newThr</span> <span class="o">=</span> <span class="o">(</span><span class="n">newCap</span> <span class="o">&lt;</span> <span class="no">MAXIMUM_CAPACITY</span> <span class="o">&amp;&amp;</span> <span class="n">ft</span> <span class="o">&lt;</span> <span class="o">(</span><span class="kt">float</span><span class="o">)</span><span class="no">MAXIMUM_CAPACITY</span> <span class="o">?</span>
                    <span class="o">(</span><span class="kt">int</span><span class="o">)</span><span class="n">ft</span> <span class="o">:</span> <span class="nc">Integer</span><span class="o">.</span><span class="na">MAX_VALUE</span><span class="o">);</span>
      <span class="o">}</span>
      <span class="n">threshold</span> <span class="o">=</span> <span class="n">newThr</span><span class="o">;</span>
      <span class="nd">@SuppressWarnings</span><span class="o">({</span><span class="s">"rawtypes"</span><span class="o">,</span><span class="s">"unchecked"</span><span class="o">})</span>
      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;[]</span> <span class="n">newTab</span> <span class="o">=</span> <span class="o">(</span><span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;[])</span><span class="k">new</span> <span class="nc">Node</span><span class="o">[</span><span class="n">newCap</span><span class="o">];</span>
      <span class="n">table</span> <span class="o">=</span> <span class="n">newTab</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">oldTab</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
          <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">j</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">j</span> <span class="o">&lt;</span> <span class="n">oldCap</span><span class="o">;</span> <span class="o">++</span><span class="n">j</span><span class="o">)</span> <span class="o">{</span>
              <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="n">e</span><span class="o">;</span>
              <span class="k">if</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">oldTab</span><span class="o">[</span><span class="n">j</span><span class="o">])</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                  <span class="n">oldTab</span><span class="o">[</span><span class="n">j</span><span class="o">]</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
                  <span class="k">if</span> <span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">next</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
                      <span class="n">newTab</span><span class="o">[</span><span class="n">e</span><span class="o">.</span><span class="na">hash</span> <span class="o">&amp;</span> <span class="o">(</span><span class="n">newCap</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)]</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
                  <span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">e</span> <span class="k">instanceof</span> <span class="nc">TreeNode</span><span class="o">)</span>
                      <span class="o">((</span><span class="nc">TreeNode</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;)</span><span class="n">e</span><span class="o">).</span><span class="na">split</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">newTab</span><span class="o">,</span> <span class="n">j</span><span class="o">,</span> <span class="n">oldCap</span><span class="o">);</span>
                  <span class="k">else</span> <span class="o">{</span> <span class="c1">// preserve order</span>
                      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="n">loHead</span> <span class="o">=</span> <span class="kc">null</span><span class="o">,</span> <span class="n">loTail</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
                      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="n">hiHead</span> <span class="o">=</span> <span class="kc">null</span><span class="o">,</span> <span class="n">hiTail</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
                      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="n">next</span><span class="o">;</span>
                      <span class="c1">//分析四</span>
                      <span class="k">do</span> <span class="o">{</span>
                          <span class="n">next</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
                          <span class="k">if</span> <span class="o">((</span><span class="n">e</span><span class="o">.</span><span class="na">hash</span> <span class="o">&amp;</span> <span class="n">oldCap</span><span class="o">)</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span> <span class="o">{</span>
                              <span class="k">if</span> <span class="o">(</span><span class="n">loTail</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
                                  <span class="n">loHead</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
                              <span class="k">else</span>
                                  <span class="n">loTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
                              <span class="n">loTail</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
                          <span class="o">}</span>
                          <span class="k">else</span> <span class="o">{</span>
                              <span class="k">if</span> <span class="o">(</span><span class="n">hiTail</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
                                  <span class="n">hiHead</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
                              <span class="k">else</span>
                                  <span class="n">hiTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
                              <span class="n">hiTail</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
                          <span class="o">}</span>
                      <span class="o">}</span> <span class="k">while</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">next</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">);</span>
                      <span class="k">if</span> <span class="o">(</span><span class="n">loTail</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                          <span class="n">loTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
                          <span class="n">newTab</span><span class="o">[</span><span class="n">j</span><span class="o">]</span> <span class="o">=</span> <span class="n">loHead</span><span class="o">;</span>
                      <span class="o">}</span>
                      <span class="k">if</span> <span class="o">(</span><span class="n">hiTail</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                          <span class="n">hiTail</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
                          <span class="n">newTab</span><span class="o">[</span><span class="n">j</span> <span class="o">+</span> <span class="n">oldCap</span><span class="o">]</span> <span class="o">=</span> <span class="n">hiHead</span><span class="o">;</span>
                      <span class="o">}</span>
                  <span class="o">}</span>
              <span class="o">}</span>
          <span class="o">}</span>
      <span class="o">}</span>
      <span class="k">return</span> <span class="n">newTab</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div>    </div>

    <p><strong>分析一:直接new HashMap()时, 第一次调用put后会resize并初始化table = 16, threshold = 12(16 * 0.75)</strong></p>

    <p><strong>分析二:创建HashMap时传入了初始大小, 第一次调用put后resize并初始化table为传入的初始大小的下一个最大2次幂,并重置threshold, 一和二都是属于懒加载</strong></p>

    <p><strong>分析三:当table不为空,且在规定范围内,则double table和double threshold</strong></p>

    <p><strong>分析四:这里非常精妙,这段循环的作用即将在oldCap时发生hash碰撞的链表归化为newCap大小时的链表,由于table的大小每次resize只增大了两倍, 所以每个Node的hash &amp; (newCap -1)后只有两种情况.(具体分析请看<a href="https://tech.meituan.com/2016/06/24/java-hashmap.html">HashMap</a>), 所以这段代码用过遍历链表, 将可以放到newTab中的Node组成新的链表并放到[oldCap + j]的位置</strong></p>

    <p><img src="../assets/images/hashmap-resize.png" alt="加载失败" /></p>

    <p><strong>最后: 与笔记一一样没有分析红黑树的节点如何resize</strong></p>
  </li>
</ul>]]></content><author><name>Xuecheng Wu</name></author><category term="Java" /><summary type="html"><![CDATA[Java HashMap笔记二]]></summary></entry><entry><title type="html">Java HashMap</title><link href="https://xxxcrel.github.io/2020/11/09/java-hashmap-one/" rel="alternate" type="text/html" title="Java HashMap" /><published>2020-11-09T00:00:00+08:00</published><updated>2020-11-09T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2020/11/09/java-hashmap-one</id><content type="html" xml:base="https://xxxcrel.github.io/2020/11/09/java-hashmap-one/"><![CDATA[<h2 id="java-hashmap笔记一">Java HashMap笔记一</h2>

<ul>
  <li>
    <p>Java中的HashMap采用分离链表法(拉链法)实现</p>

    <p><strong>各大博客文章有图</strong></p>
  </li>
  <li>
    <p>准备容器大小和hash函数(保证容器的大小一定是2的幂, 16, 32, 64…)</p>

    <p><strong>取得当前数的下一个2的最小幂, 如63-&gt;64, 129-&gt;256, 256-&gt;256</strong></p>

    <p><strong>技巧在于通过<code class="language-plaintext highlighter-rouge">与</code>操作和无符号右移31位(满足int类型极值情况)取得下一次2的最小幂方</strong></p>

    <p><strong>如0000-1101-0111-0101 -&gt; 0000-1101-0111-0100 -&gt; 0000-1111-1111-1111.</strong></p>

    <p><strong>极限值:0001-0000-0000-0000 -&gt; 0000-1111-1111-1111 -&gt; 0000-1111-1111-1111.</strong></p>

    <p><strong>最后加1获得0001-0000-0000-0000.</strong></p>

    <p><strong>第一行的-1用来处理恰好情形如64, 256</strong></p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="no">MAXIMUM_CAPACITY</span> <span class="o">=</span> <span class="mi">1</span> <span class="o">&lt;&lt;</span> <span class="mi">30</span><span class="o">;</span>

  <span class="kt">int</span> <span class="nf">tableSizeFor</span><span class="o">(</span><span class="kt">int</span> <span class="n">cap</span><span class="o">){</span>
      <span class="kt">int</span> <span class="n">n</span> <span class="o">=</span> <span class="n">cap</span> <span class="o">-</span> <span class="mi">1</span><span class="o">;</span>
      <span class="n">n</span> <span class="o">|=</span> <span class="n">n</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">1</span><span class="o">;</span>
      <span class="n">n</span> <span class="o">|=</span> <span class="n">n</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">2</span><span class="o">;</span>
      <span class="n">n</span> <span class="o">|=</span> <span class="n">n</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">4</span><span class="o">;</span>
      <span class="n">n</span> <span class="o">|=</span> <span class="n">n</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">8</span><span class="o">;</span>
      <span class="n">n</span> <span class="o">|=</span> <span class="n">n</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">16</span><span class="o">;</span>
      <span class="k">return</span> <span class="o">(</span><span class="n">n</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="o">)</span> <span class="o">?</span> <span class="mi">1</span> <span class="o">:</span> <span class="o">(</span><span class="n">n</span> <span class="o">&gt;=</span> <span class="no">MAXIMUM_CAPACITY</span><span class="o">)</span> <span class="o">?</span> <span class="no">MAXIMUM_CAPACITY</span> <span class="o">:</span> <span class="n">n</span> <span class="o">+</span> <span class="mi">1</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">static</span> <span class="kd">final</span> <span class="kt">int</span> <span class="nf">hash</span><span class="o">(</span><span class="nc">Object</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span>
      <span class="kt">int</span> <span class="n">h</span><span class="o">;</span>
      <span class="k">return</span> <span class="o">(</span><span class="n">key</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">?</span> <span class="mi">0</span> <span class="o">:</span> <span class="o">(</span><span class="n">h</span> <span class="o">=</span> <span class="n">key</span><span class="o">.</span><span class="na">hashCode</span><span class="o">())</span> <span class="o">^</span> <span class="o">(</span><span class="n">h</span> <span class="o">&gt;&gt;&gt;</span> <span class="mi">16</span><span class="o">);</span>
  <span class="o">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>put(K key, V value), 内部调用putVal</p>

    <p><strong><code class="language-plaintext highlighter-rouge">重点1</code>:i = (n - 1) &amp; hash, n为容器大小且为2的幂方, 所以这段代码通过<code class="language-plaintext highlighter-rouge">&amp;</code>取hash值的最后符合容器大小的位数,所以即使两个hash值不同, 也有可能发生hash碰撞</strong>
  eg:hashCode返回0x00-00-00-01和0x00-03-00-02;
  通过上面的hash(key)变成0x00-00-00-01和0x00-03-00-01;
  设n = 16, 那么n-1:0x00-00-00-7F,那么(n - 1) &amp; 上面的两个hash值获得结果一致发生hash碰撞</p>

    <p><strong><code class="language-plaintext highlighter-rouge">重点2</code>:当发生hash碰撞时通过遍历Node的next指针至末尾,如hash和key相同则更新value, 不同则最后插入, 但是当binCount(node节点的数量)达到一个阈值时(默认是8)则将其转换成红黑数提高查询效率</strong></p>

    <p><strong><code class="language-plaintext highlighter-rouge">重点3</code>:key相同有两种情况,他们是用一个对象引用, 或者非空且equals返回true</strong></p>

    <p><strong><code class="language-plaintext highlighter-rouge">重点4</code>:从hash函数中可以得知put方法的Key可以为null, 而null值也就相当于0</strong></p>

    <p><strong>这就是当需要把对象作为hash容器的key时需要覆写<code class="language-plaintext highlighter-rouge">hashCode</code>和<code class="language-plaintext highlighter-rouge">equals</code>的原因</strong></p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">final</span> <span class="no">V</span> <span class="nf">putVal</span><span class="o">(</span><span class="kt">int</span> <span class="n">hash</span><span class="o">,</span> <span class="no">K</span> <span class="n">key</span><span class="o">,</span> <span class="no">V</span> <span class="n">value</span><span class="o">,</span> <span class="kt">boolean</span> <span class="n">onlyIfAbsent</span><span class="o">,</span>
                 <span class="kt">boolean</span> <span class="n">evict</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;[]</span> <span class="n">tab</span><span class="o">;</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="n">p</span><span class="o">;</span> <span class="kt">int</span> <span class="n">n</span><span class="o">,</span> <span class="n">i</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">((</span><span class="n">tab</span> <span class="o">=</span> <span class="n">table</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span> <span class="o">||</span> <span class="o">(</span><span class="n">n</span> <span class="o">=</span> <span class="n">tab</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">==</span> <span class="mi">0</span><span class="o">)</span>
          <span class="n">n</span> <span class="o">=</span> <span class="o">(</span><span class="n">tab</span> <span class="o">=</span> <span class="n">resize</span><span class="o">()).</span><span class="na">length</span><span class="o">;</span>
      <span class="c1">//无hash碰撞,直接插入</span>
      <span class="k">if</span> <span class="o">((</span><span class="n">p</span> <span class="o">=</span> <span class="n">tab</span><span class="o">[</span><span class="n">i</span> <span class="o">=</span> <span class="o">(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="o">&amp;</span> <span class="n">hash</span><span class="o">])</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
          <span class="n">tab</span><span class="o">[</span><span class="n">i</span><span class="o">]</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">(</span><span class="n">hash</span><span class="o">,</span> <span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
      <span class="k">else</span> <span class="o">{</span><span class="c1">//有hash碰撞</span>
          <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="n">e</span><span class="o">;</span> <span class="no">K</span> <span class="n">k</span><span class="o">;</span>
          <span class="c1">//hash值和key相同直接更新value</span>
          <span class="k">if</span> <span class="o">(</span><span class="n">p</span><span class="o">.</span><span class="na">hash</span> <span class="o">==</span> <span class="n">hash</span> <span class="o">&amp;&amp;</span>
              <span class="o">((</span><span class="n">k</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">key</span><span class="o">)</span> <span class="o">==</span> <span class="n">key</span> <span class="o">||</span> <span class="o">(</span><span class="n">key</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">key</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">k</span><span class="o">))))</span>
              <span class="n">e</span> <span class="o">=</span> <span class="n">p</span><span class="o">;</span>
          <span class="c1">//插入至红黑树</span>
          <span class="k">else</span> <span class="nf">if</span> <span class="o">(</span><span class="n">p</span> <span class="k">instanceof</span> <span class="nc">TreeNode</span><span class="o">)</span>
              <span class="n">e</span> <span class="o">=</span> <span class="o">((</span><span class="nc">TreeNode</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;)</span><span class="n">p</span><span class="o">).</span><span class="na">putTreeVal</span><span class="o">(</span><span class="k">this</span><span class="o">,</span> <span class="n">tab</span><span class="o">,</span> <span class="n">hash</span><span class="o">,</span> <span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">);</span>
          <span class="k">else</span> <span class="o">{</span><span class="c1">//通过遍历链表插入到尾部,如hash和key相同则退出并更新value, 否则放入尾部</span>
              <span class="k">for</span> <span class="o">(</span><span class="kt">int</span> <span class="n">binCount</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="o">;</span> <span class="o">++</span><span class="n">binCount</span><span class="o">)</span> <span class="o">{</span>
                  <span class="k">if</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">p</span><span class="o">.</span><span class="na">next</span><span class="o">)</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
                      <span class="n">p</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">newNode</span><span class="o">(</span><span class="n">hash</span><span class="o">,</span> <span class="n">key</span><span class="o">,</span> <span class="n">value</span><span class="o">,</span> <span class="kc">null</span><span class="o">);</span>
                      <span class="c1">//达到树化的阈值,进行红黑树的转化</span>
                      <span class="k">if</span> <span class="o">(</span><span class="n">binCount</span> <span class="o">&gt;=</span> <span class="no">TREEIFY_THRESHOLD</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="c1">// -1 for 1st</span>
                          <span class="n">treeifyBin</span><span class="o">(</span><span class="n">tab</span><span class="o">,</span> <span class="n">hash</span><span class="o">);</span>
                      <span class="k">break</span><span class="o">;</span>
                  <span class="o">}</span>
                  <span class="k">if</span> <span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">hash</span> <span class="o">==</span> <span class="n">hash</span> <span class="o">&amp;&amp;</span>
                      <span class="o">((</span><span class="n">k</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">key</span><span class="o">)</span> <span class="o">==</span> <span class="n">key</span> <span class="o">||</span> <span class="o">(</span><span class="n">key</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">key</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">k</span><span class="o">))))</span>
                      <span class="k">break</span><span class="o">;</span>
                  <span class="n">p</span> <span class="o">=</span> <span class="n">e</span><span class="o">;</span>
              <span class="o">}</span>
          <span class="o">}</span>
          <span class="k">if</span> <span class="o">(</span><span class="n">e</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span> <span class="c1">// existing mapping for key</span>
              <span class="no">V</span> <span class="n">oldValue</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">value</span><span class="o">;</span>
              <span class="k">if</span> <span class="o">(!</span><span class="n">onlyIfAbsent</span> <span class="o">||</span> <span class="n">oldValue</span> <span class="o">==</span> <span class="kc">null</span><span class="o">)</span>
                  <span class="n">e</span><span class="o">.</span><span class="na">value</span> <span class="o">=</span> <span class="n">value</span><span class="o">;</span>
              <span class="n">afterNodeAccess</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
              <span class="k">return</span> <span class="n">oldValue</span><span class="o">;</span>
          <span class="o">}</span>
      <span class="o">}</span>
      <span class="o">++</span><span class="n">modCount</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">(++</span><span class="n">size</span> <span class="o">&gt;</span> <span class="n">threshold</span><span class="o">)</span>
          <span class="n">resize</span><span class="o">();</span>
      <span class="n">afterNodeInsertion</span><span class="o">(</span><span class="n">evict</span><span class="o">);</span>
      <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>get(K key)内部调用getNode</p>

    <p><strong>同put一样通过(n - 1) &amp; hash获得内部数组的索引,如果找到,则通过遍历当前桶中的Node查找</strong></p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">final</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="nf">getNode</span><span class="o">(</span><span class="kt">int</span> <span class="n">hash</span><span class="o">,</span> <span class="nc">Object</span> <span class="n">key</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;[]</span> <span class="n">tab</span><span class="o">;</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;</span> <span class="n">first</span><span class="o">,</span> <span class="n">e</span><span class="o">;</span> <span class="kt">int</span> <span class="n">n</span><span class="o">;</span> <span class="no">K</span> <span class="n">k</span><span class="o">;</span>
      <span class="k">if</span> <span class="o">((</span><span class="n">tab</span> <span class="o">=</span> <span class="n">table</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="o">(</span><span class="n">n</span> <span class="o">=</span> <span class="n">tab</span><span class="o">.</span><span class="na">length</span><span class="o">)</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span>
          <span class="o">(</span><span class="n">first</span> <span class="o">=</span> <span class="n">tab</span><span class="o">[(</span><span class="n">n</span> <span class="o">-</span> <span class="mi">1</span><span class="o">)</span> <span class="o">&amp;</span> <span class="n">hash</span><span class="o">])</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
          <span class="c1">//</span>
          <span class="k">if</span> <span class="o">(</span><span class="n">first</span><span class="o">.</span><span class="na">hash</span> <span class="o">==</span> <span class="n">hash</span> <span class="o">&amp;&amp;</span> <span class="c1">// always check first node</span>
              <span class="o">((</span><span class="n">k</span> <span class="o">=</span> <span class="n">first</span><span class="o">.</span><span class="na">key</span><span class="o">)</span> <span class="o">==</span> <span class="n">key</span> <span class="o">||</span> <span class="o">(</span><span class="n">key</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">key</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">k</span><span class="o">))))</span>
              <span class="k">return</span> <span class="n">first</span><span class="o">;</span>
          <span class="k">if</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">first</span><span class="o">.</span><span class="na">next</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
              <span class="k">if</span> <span class="o">(</span><span class="n">first</span> <span class="k">instanceof</span> <span class="nc">TreeNode</span><span class="o">)</span>
                  <span class="k">return</span> <span class="o">((</span><span class="nc">TreeNode</span><span class="o">&lt;</span><span class="no">K</span><span class="o">,</span><span class="no">V</span><span class="o">&gt;)</span><span class="n">first</span><span class="o">).</span><span class="na">getTreeNode</span><span class="o">(</span><span class="n">hash</span><span class="o">,</span> <span class="n">key</span><span class="o">);</span>
              <span class="k">do</span> <span class="o">{</span>
                  <span class="k">if</span> <span class="o">(</span><span class="n">e</span><span class="o">.</span><span class="na">hash</span> <span class="o">==</span> <span class="n">hash</span> <span class="o">&amp;&amp;</span>
                      <span class="o">((</span><span class="n">k</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">key</span><span class="o">)</span> <span class="o">==</span> <span class="n">key</span> <span class="o">||</span> <span class="o">(</span><span class="n">key</span> <span class="o">!=</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="n">key</span><span class="o">.</span><span class="na">equals</span><span class="o">(</span><span class="n">k</span><span class="o">))))</span>
                      <span class="k">return</span> <span class="n">e</span><span class="o">;</span>
              <span class="o">}</span> <span class="k">while</span> <span class="o">((</span><span class="n">e</span> <span class="o">=</span> <span class="n">e</span><span class="o">.</span><span class="na">next</span><span class="o">)</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">);</span>
          <span class="o">}</span>
      <span class="o">}</span>
      <span class="k">return</span> <span class="kc">null</span><span class="o">;</span>
  <span class="o">}</span>
</code></pre></div>    </div>
  </li>
</ul>]]></content><author><name>Xuecheng Wu</name></author><category term="Java" /><summary type="html"><![CDATA[Java HashMap笔记一]]></summary></entry><entry><title type="html">java泛型笔记</title><link href="https://xxxcrel.github.io/2020/10/21/java-generic/" rel="alternate" type="text/html" title="java泛型笔记" /><published>2020-10-21T00:00:00+08:00</published><updated>2020-10-21T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2020/10/21/java-generic</id><content type="html" xml:base="https://xxxcrel.github.io/2020/10/21/java-generic/"><![CDATA[<h2 id="java泛型笔记">Java泛型笔记</h2>

<ul>
  <li>
    <h3 id="为什么使用泛型">为什么使用泛型</h3>

    <ol>
      <li>增强代码复用性</li>
      <li>有更强大的语义去写容器库</li>
    </ol>
  </li>
  <li>
    <h3 id="简单泛型栗子">简单泛型栗子</h3>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">public</span> <span class="kd">class</span> <span class="nc">LinkedStack</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="o">{</span>
  <span class="kd">private</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">top</span><span class="o">;</span>
  <span class="kd">private</span> <span class="kt">int</span> <span class="n">size</span><span class="o">;</span>

  <span class="kd">private</span> <span class="kd">static</span> <span class="kd">class</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="o">{</span>
      <span class="kd">private</span> <span class="no">T</span> <span class="n">item</span><span class="o">;</span>
      <span class="kd">private</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">next</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="nf">LinkedStack</span><span class="o">()</span> <span class="o">{</span>
      <span class="n">top</span> <span class="o">=</span> <span class="kc">null</span><span class="o">;</span>
      <span class="kt">int</span> <span class="n">size</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="kt">void</span> <span class="nf">push</span><span class="o">(</span><span class="no">T</span> <span class="n">item</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">Node</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="n">oldTop</span> <span class="o">=</span> <span class="n">top</span><span class="o">;</span>
      <span class="n">top</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Node</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;();</span>
      <span class="n">top</span><span class="o">.</span><span class="na">item</span> <span class="o">=</span> <span class="n">item</span><span class="o">;</span>
      <span class="n">top</span><span class="o">.</span><span class="na">next</span> <span class="o">=</span> <span class="n">oldTop</span><span class="o">;</span>
      <span class="n">size</span><span class="o">++;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="kt">boolean</span> <span class="nf">isEmpty</span><span class="o">()</span> <span class="o">{</span>
      <span class="k">return</span> <span class="n">top</span> <span class="o">==</span> <span class="kc">null</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="kt">int</span> <span class="nf">size</span><span class="o">(){</span>
      <span class="k">return</span> <span class="n">size</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="no">T</span> <span class="nf">pop</span><span class="o">()</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">isEmpty</span><span class="o">())</span> <span class="o">{</span>
          <span class="k">throw</span> <span class="k">new</span> <span class="nf">NoSuchElementException</span><span class="o">(</span><span class="s">"Stack underflow"</span><span class="o">);</span>
      <span class="o">}</span>
      <span class="no">T</span> <span class="n">item</span> <span class="o">=</span> <span class="n">top</span><span class="o">.</span><span class="na">item</span><span class="o">;</span>
      <span class="n">top</span> <span class="o">=</span> <span class="n">top</span><span class="o">.</span><span class="na">next</span><span class="o">;</span>
      <span class="n">size</span><span class="o">--;</span>
      <span class="k">return</span> <span class="n">item</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="no">T</span> <span class="nf">peek</span><span class="o">()</span> <span class="o">{</span>
      <span class="k">if</span> <span class="o">(</span><span class="n">isEmpty</span><span class="o">())</span> <span class="o">{</span>
          <span class="k">throw</span> <span class="k">new</span> <span class="nf">NoSuchElementException</span><span class="o">(</span><span class="s">"Stack underflow"</span><span class="o">);</span>
      <span class="o">}</span>
      <span class="k">return</span> <span class="n">top</span><span class="o">.</span><span class="na">item</span><span class="o">;</span>
  <span class="o">}</span>

  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">LinkedStack</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">stack</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LinkedStack</span><span class="o">&lt;&gt;();</span>
      <span class="nc">LinkedStack</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;</span> <span class="n">stack1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">LinkedStack</span><span class="o">&lt;&gt;();</span>
      <span class="n">stack</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="s">"hello"</span><span class="o">);</span>
      <span class="n">stack</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="s">"world"</span><span class="o">);</span>
      <span class="n">stack1</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="mi">1</span><span class="o">);</span>
      <span class="n">stack1</span><span class="o">.</span><span class="na">push</span><span class="o">(</span><span class="mi">2</span><span class="o">);</span>
  <span class="o">}</span>
</code></pre></div>    </div>
    <p>似乎泛型按照我们设想的那样,将T占位符换成的我们声明的参数<code class="language-plaintext highlighter-rouge">String</code>, <code class="language-plaintext highlighter-rouge">Integer</code>并生成了两个不同的Stack.但是结果并不是, 它们仍然是相同的类型</p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">Class</span> <span class="n">c1</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;().</span><span class="na">getClass</span><span class="o">();</span>
      <span class="nc">Class</span> <span class="n">c2</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">Integer</span><span class="o">&gt;().</span><span class="na">getClass</span><span class="o">();</span>
      <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">c1</span> <span class="o">==</span> <span class="n">c2</span><span class="o">);</span>
  <span class="o">}</span>
  <span class="c1">//output: true</span>
</code></pre></div>    </div>
    <p>Java并不是从1.0就开始有泛型的,这是在5.0后才增加的新特性,而如果像上面的就已经存在的Stack类库(item类型那个为Obejct),并且已经很多公司在用,如果这时候贸然升级成我们想象中的泛型, 那这些类库使用者将会是一个巨大的灾难,所以Java采取了一个折中(也可以说妥协)的方式
  <code class="language-plaintext highlighter-rouge">泛型擦除</code>.</p>

    <p>简单例子</p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Holder</span><span class="o">&lt;</span><span class="no">T</span><span class="o">&gt;</span> <span class="o">{</span>
      <span class="kd">private</span> <span class="no">T</span> <span class="n">item</span><span class="o">;</span>
      <span class="kd">public</span> <span class="nf">Holder</span><span class="o">(</span><span class="no">T</span> <span class="n">item</span><span class="o">){</span>
          <span class="k">this</span><span class="o">.</span><span class="na">item</span> <span class="o">=</span> <span class="n">item</span><span class="o">;</span>
      <span class="o">}</span>

      <span class="kd">public</span> <span class="no">T</span> <span class="nf">getItem</span><span class="o">()</span> <span class="o">{</span>
          <span class="k">return</span> <span class="n">item</span><span class="o">;</span>
      <span class="o">}</span>

      <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="nc">String</span><span class="o">[]</span> <span class="n">args</span><span class="o">)</span> <span class="o">{</span>
          <span class="nc">Holder</span><span class="o">&lt;</span><span class="nc">String</span><span class="o">&gt;</span> <span class="n">holder</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">Holder</span><span class="o">&lt;&gt;(</span><span class="s">"hello"</span><span class="o">);</span>
          <span class="nc">String</span> <span class="n">hello</span> <span class="o">=</span> <span class="n">holder</span><span class="o">.</span><span class="na">getItem</span><span class="o">();</span>
      <span class="o">}</span>
  <span class="o">}</span>
</code></pre></div>    </div>
    <p>运行<code class="language-plaintext highlighter-rouge">javap -v Holder</code></p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nc">Constant</span> <span class="nl">pool:</span>
  <span class="err">#</span><span class="mi">1</span> <span class="o">=</span> <span class="nc">Methodref</span>          <span class="err">#</span><span class="mi">8</span><span class="o">.</span><span class="err">#</span><span class="mi">26</span>         <span class="c1">// java/lang/Object."&lt;init&gt;":()V</span>
  <span class="err">#</span><span class="mi">2</span> <span class="o">=</span> <span class="nc">Fieldref</span>           <span class="err">#</span><span class="mi">3</span><span class="o">.</span><span class="err">#</span><span class="mi">27</span>         <span class="c1">// Holder.item:Ljava/lang/Object;</span>

  <span class="kd">public</span> <span class="no">T</span> <span class="nf">getItem</span><span class="o">();</span>
  <span class="nl">descriptor:</span> <span class="o">()</span><span class="nc">Ljava</span><span class="o">/</span><span class="n">lang</span><span class="o">/</span><span class="nc">Object</span><span class="o">;</span>
  <span class="nl">Code:</span>
       <span class="mi">0</span><span class="o">:</span> <span class="n">aload_0</span>
       <span class="mi">1</span><span class="o">:</span> <span class="n">getfield</span>      <span class="err">#</span><span class="mi">2</span>                  <span class="c1">// Field item:Ljava/lang/Object;</span>
       <span class="mi">4</span><span class="o">:</span> <span class="n">areturn</span>

  <span class="kd">public</span> <span class="kd">static</span> <span class="kt">void</span> <span class="nf">main</span><span class="o">(</span><span class="n">java</span><span class="o">.</span><span class="na">lang</span><span class="o">.</span><span class="na">String</span><span class="o">[]);</span>
  <span class="nl">descriptor:</span> <span class="o">([</span><span class="nc">Ljava</span><span class="o">/</span><span class="n">lang</span><span class="o">/</span><span class="nc">String</span><span class="o">;)</span><span class="no">V</span>
  <span class="nl">Code:</span>
      <span class="mi">11</span><span class="o">:</span> <span class="n">invokevirtual</span> <span class="err">#</span><span class="mi">6</span>                  <span class="c1">// Method getItem:()Ljava/lang/Object;</span>
      <span class="mi">14</span><span class="o">:</span> <span class="n">checkcast</span>     <span class="err">#</span><span class="mi">7</span>                  <span class="c1">// class java/lang/String</span>
      <span class="mi">17</span><span class="o">:</span> <span class="n">astore_2</span>
      <span class="mi">18</span><span class="o">:</span> <span class="k">return</span>
</code></pre></div>    </div>
    <p>从上面的Fieldref为Object可知, T被擦除到它的上边界,编译器自动加了<code class="language-plaintext highlighter-rouge">&lt;T extends Object&gt;</code>,也就是说T其实就是Object.</p>

    <p>继续验证,现在设置T的边界为Bird</p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">class</span> <span class="nc">Bird</span><span class="o">{</span>

  <span class="o">}</span>

  <span class="kd">class</span> <span class="nc">Crow</span> <span class="kd">extends</span> <span class="nc">Bird</span><span class="o">{</span>

  <span class="o">}</span>

  <span class="kd">public</span> <span class="kd">class</span> <span class="nc">Holder</span><span class="o">&lt;</span><span class="no">T</span> <span class="kd">extends</span> <span class="nc">Bird</span><span class="o">&gt;{</span>
      <span class="o">...</span>
  <span class="o">}</span>
</code></pre></div>    </div>

    <p>由javap可得如下</p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nc">Constant</span> <span class="nl">pool:</span>
  <span class="err">#</span><span class="mi">1</span> <span class="o">=</span> <span class="nc">Methodref</span>          <span class="err">#</span><span class="mi">8</span><span class="o">.</span><span class="err">#</span><span class="mi">26</span>         <span class="c1">// java/lang/Object."&lt;init&gt;":()V</span>
  <span class="err">#</span><span class="mi">2</span> <span class="o">=</span> <span class="nc">Fieldref</span>           <span class="err">#</span><span class="mi">3</span><span class="o">.</span><span class="err">#</span><span class="mi">27</span>         <span class="c1">// Holder.item:LBird;</span>

  <span class="kd">public</span> <span class="no">T</span> <span class="nf">getItem</span><span class="o">();</span>
  <span class="nl">descriptor:</span> <span class="o">()</span><span class="nc">LBird</span><span class="o">;</span>
  <span class="nl">Code:</span>
    <span class="n">stack</span><span class="o">=</span><span class="mi">1</span><span class="o">,</span> <span class="n">locals</span><span class="o">=</span><span class="mi">1</span><span class="o">,</span> <span class="n">args_size</span><span class="o">=</span><span class="mi">1</span>
       <span class="mi">0</span><span class="o">:</span> <span class="n">aload_0</span>
       <span class="mi">1</span><span class="o">:</span> <span class="n">getfield</span>      <span class="err">#</span><span class="mi">2</span>                  <span class="c1">// Field item:LBird;</span>
       <span class="mi">4</span><span class="o">:</span> <span class="n">areturn</span>
</code></pre></div>    </div>

    <p>这里T被擦除到Bird,相当于在Holder中定义<code class="language-plaintext highlighter-rouge">Bird item;</code>, 也就是说我们想象的美好生活都被java编译器神秘的替换了,那main方法中的代码是如何正确运行的呢?</p>

    <p>上面main中holder.getItem()生成的字节码如下</p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="mi">11</span><span class="o">:</span> <span class="n">invokevirtual</span> <span class="err">#</span><span class="mi">6</span>                  <span class="c1">// Method getItem:()Ljava/lang/Object;</span>
  <span class="mi">14</span><span class="o">:</span> <span class="n">checkcast</span>     <span class="err">#</span><span class="mi">7</span>                  <span class="c1">// class java/lang/String</span>
</code></pre></div>    </div>
    <p>在调用相应的getItem()后会有一条checkcast指令,官方定义为检查是否能转型成给定的类型, 即getItem()的类型是否能转型为String. 所以java泛型采用类型擦除.即Java泛型是编译器的行为,虚拟机根本不知道泛型的存在.</p>
  </li>
  <li>
    <h3 id="更抽象">更抽象</h3>

    <p>现在假设我有一个方法用来打印一个集合(Collection),如下</p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">//出自官方</span>
  <span class="c1">//5.0之前</span>
  <span class="kt">void</span> <span class="nf">printCollection</span><span class="o">(</span><span class="nc">Collection</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
      <span class="nc">Iterator</span> <span class="n">i</span> <span class="o">=</span> <span class="n">c</span><span class="o">.</span><span class="na">iterator</span><span class="o">();</span>
      <span class="k">for</span> <span class="o">(</span><span class="n">k</span> <span class="o">=</span> <span class="mi">0</span><span class="o">;</span> <span class="n">k</span> <span class="o">&lt;</span> <span class="n">c</span><span class="o">.</span><span class="na">size</span><span class="o">();</span> <span class="n">k</span><span class="o">++)</span> <span class="o">{</span>
          <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">i</span><span class="o">.</span><span class="na">next</span><span class="o">());</span>
      <span class="o">}</span>
  <span class="o">}</span>
  <span class="c1">//尝试使用泛型</span>
  <span class="c1">//但是只能传入Collection&lt;Object&gt;</span>
  <span class="kt">void</span> <span class="nf">printCollection</span><span class="o">(</span><span class="nc">Collection</span><span class="o">&lt;</span><span class="nc">Object</span><span class="o">&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">for</span> <span class="o">(</span><span class="nc">Object</span> <span class="n">e</span> <span class="o">:</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
          <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
      <span class="o">}</span>
  <span class="o">}</span>
</code></pre></div>    </div>
    <p>然而没啥用,并没有带来任何便利和语义上的清晰</p>

    <p>#### 使用通配符<code class="language-plaintext highlighter-rouge">?</code></p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kt">void</span> <span class="nf">printCollection</span><span class="o">(</span><span class="nc">Collection</span><span class="o">&lt;?&gt;</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
      <span class="k">for</span> <span class="o">(</span><span class="nc">Object</span> <span class="n">e</span> <span class="o">:</span> <span class="n">c</span><span class="o">)</span> <span class="o">{</span>
          <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="n">e</span><span class="o">);</span>
      <span class="o">}</span>
  <span class="o">}</span>
</code></pre></div>    </div>
    <p>现在似乎可以了,但现在有个新需求,我希望这个Collection是某个限定类型的子类.</p>

    <p>#### 更具体的<code class="language-plaintext highlighter-rouge">extends</code></p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kd">abstract</span> <span class="kd">class</span> <span class="nc">Bird</span><span class="o">{</span>
      <span class="kd">abstract</span> <span class="kt">void</span> <span class="nf">speak</span><span class="o">();</span>
  <span class="o">}</span>

  <span class="kd">class</span> <span class="nc">Crow</span> <span class="kd">extends</span> <span class="nc">Bird</span><span class="o">{</span>
      <span class="nd">@Override</span>
      <span class="kt">void</span> <span class="nf">speak</span><span class="o">()</span> <span class="o">{</span>
          <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"gaga gaga"</span><span class="o">);</span>
      <span class="o">}</span>
  <span class="o">}</span>

  <span class="kd">class</span> <span class="nc">Cuckoo</span> <span class="kd">extends</span> <span class="nc">Bird</span><span class="o">{</span>
      <span class="nd">@Override</span>
      <span class="kt">void</span> <span class="nf">speak</span><span class="o">()</span> <span class="o">{</span>
          <span class="nc">System</span><span class="o">.</span><span class="na">out</span><span class="o">.</span><span class="na">println</span><span class="o">(</span><span class="s">"bugu bugu"</span><span class="o">);</span>
      <span class="o">}</span>
  <span class="o">}</span>
    
  <span class="kt">void</span> <span class="nf">speak</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="nc">Bird</span><span class="o">&gt;</span> <span class="n">birds</span><span class="o">){</span>
      <span class="k">for</span><span class="o">(</span><span class="nc">Bird</span> <span class="n">bird</span> <span class="o">:</span> <span class="n">birds</span><span class="o">)</span>
          <span class="n">bird</span><span class="o">.</span><span class="na">speak</span><span class="o">();</span>
  <span class="o">}</span>

  <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Crow</span><span class="o">&gt;</span> <span class="n">crows</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="k">new</span> <span class="nc">Crow</span><span class="o">());</span>
  <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Cuckoo</span><span class="o">&gt;</span> <span class="n">cuckoos</span> <span class="o">=</span> <span class="nc">Arrays</span><span class="o">.</span><span class="na">asList</span><span class="o">(</span><span class="k">new</span> <span class="nc">Cuckoo</span><span class="o">());</span>
  <span class="n">speak</span><span class="o">(</span><span class="n">crows</span><span class="o">);</span>
  <span class="n">speak</span><span class="o">(</span><span class="n">cuckoos</span><span class="o">);</span>
</code></pre></div>    </div>
    <p>speak方法表达的是:我接受任何Bird或Bird的子类的列表,
  并且在其上调用<code class="language-plaintext highlighter-rouge">bird.speak()</code>是语义安全的.</p>

    <p><strong>为什么要这样写?</strong></p>

    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">//编译错误, 编译器不会去知道Crow是Bird的子类</span>
  <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Bird</span><span class="o">&gt;</span> <span class="n">birds</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">Crow</span><span class="o">&gt;();</span>
</code></pre></div>    </div>
    <p>所以List&lt;? extends Bird&gt; birds表达的意思是, birds:我能保证我里面放的是Bird或者他的子类,你可以取出来当Bird使用.</p>

    <p>但是,这时候如果想要往里面添加一些东西, 比如:</p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="kt">void</span> <span class="nf">speak</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;?</span> <span class="kd">extends</span> <span class="nc">Bird</span><span class="o">&gt;</span> <span class="n">birds</span><span class="o">){</span>
      <span class="n">birds</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">Cuckoo</span><span class="o">());</span><span class="c1">//编译错误</span>
      <span class="n">birds</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">Crow</span><span class="o">());</span><span class="c1">//同上</span>
  <span class="o">}</span>
  <span class="c1">//当我们调用speak方法的时候,speak是不知道传入的是List&lt;Cuckoo&gt; 还是List&lt;Crow&gt;, </span>
  <span class="c1">//如果往里面添加了不正确的类型, 如传入的是List&lt;Cuckoo&gt;但是却add(new Crow()), 那么对传入的birds是属于破坏的行为, 你不能保证之后对birds的使用都是在调用Bird里面的相同的方法.</span>
</code></pre></div>    </div>

    <p><strong>春天的到来:<code class="language-plaintext highlighter-rouge">super</code></strong></p>

    <p>有时候就有这么一个需求, 有一个方法接收一个泛化的列表,并且向里面添加一些似乎合道理的东西.毕竟下面是合法且合理的:</p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="nc">List</span><span class="o">&lt;</span><span class="nc">Bird</span><span class="o">&gt;</span> <span class="n">birds</span> <span class="o">=</span> <span class="k">new</span> <span class="nc">ArrayList</span><span class="o">&lt;</span><span class="nc">Bird</span><span class="o">&gt;();</span>
  <span class="n">birds</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">Cuckoo</span><span class="o">());</span>
</code></pre></div>    </div>
    <p>但是这个birds只能接受<code class="language-plaintext highlighter-rouge">List&lt;Bird&gt;</code>列表,
  使用<code class="language-plaintext highlighter-rouge">super</code></p>
    <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">//可加工的</span>
  <span class="kd">interface</span> <span class="nc">Processable</span><span class="o">{</span>

  <span class="o">}</span>
  <span class="c1">//可出售的</span>
  <span class="kd">interface</span> <span class="nc">Salable</span><span class="o">{</span>
        
  <span class="o">}</span>
  <span class="kd">class</span> <span class="nc">Fruit</span><span class="o">{</span>

  <span class="o">}</span>
  <span class="kd">class</span> <span class="nc">Apple</span> <span class="kd">extends</span> <span class="nc">Fruit</span> <span class="kd">implements</span> <span class="nc">Processable</span><span class="o">,</span> <span class="nc">Salable</span><span class="o">{</span>
  <span class="o">}</span>

  <span class="kd">class</span> <span class="nc">RedApple</span> <span class="kd">extends</span> <span class="nc">Apple</span><span class="o">{</span>

  <span class="o">}</span>

  <span class="kd">class</span> <span class="nc">YellowApple</span> <span class="kd">extends</span> <span class="nc">Apple</span><span class="o">{</span>

  <span class="o">}</span>

  <span class="kd">class</span> <span class="nc">Banana</span> <span class="kd">extends</span> <span class="nc">Fruit</span><span class="o">{</span>

  <span class="o">}</span>
  <span class="kt">void</span> <span class="nf">addToAppleStore</span><span class="o">(</span><span class="nc">List</span><span class="o">&lt;?</span> <span class="kd">super</span> <span class="nc">Apple</span><span class="o">&gt;</span> <span class="n">apples</span><span class="o">){</span>
      <span class="n">apples</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">RedApple</span><span class="o">());</span>
      <span class="n">apples</span><span class="o">.</span><span class="na">add</span><span class="o">(</span><span class="k">new</span> <span class="nc">YellowApple</span><span class="o">());</span>
  <span class="o">}</span>
</code></pre></div>    </div>
    <p><code class="language-plaintext highlighter-rouge">addToAppleStore</code>表达的意思是:我接受一个列表(仓库),并且这个仓库可能是Fruit, 也可能是可加工的(Processable), 也可能是可出售的(Salable),所以向里面添加任何Apple或者Apple子类是安全且正确的.但是这时候我们如果想要上面<code class="language-plaintext highlighter-rouge">extends</code>语法的便利性去调用Apple中的方法时,那么将会获得一个编译错误.
  <strong>为什么?</strong> 
  因为List的边界是lower boundes,所以传入的List中的元素不一定拥有Apple中的方法, 比如传入List<Processable> 那么Processable中没有Apple中的方法的.</Processable></p>
  </li>
</ul>]]></content><author><name>Xuecheng Wu</name></author><category term="java" /><category term="generic" /><summary type="html"><![CDATA[Java泛型笔记]]></summary></entry><entry><title type="html">proxy config</title><link href="https://xxxcrel.github.io/2020/08/27/linux-proxy/" rel="alternate" type="text/html" title="proxy config" /><published>2020-08-27T00:00:00+08:00</published><updated>2020-08-27T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2020/08/27/linux-proxy</id><content type="html" xml:base="https://xxxcrel.github.io/2020/08/27/linux-proxy/"><![CDATA[<h2 id="linux系统下的各种代理配置踩坑">linux系统下的各种代理配置踩坑</h2>

<ul>
  <li>
    <p>wget, curl, git(https方式)等命令行使用.bashrc, .zshrc中的代理环境变量</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">export </span><span class="nv">http_proxy</span><span class="o">=</span><span class="s2">"http://127.0.0.1:12333"</span>
<span class="nb">export </span><span class="nv">https_proxy</span><span class="o">=</span><span class="s2">"http://127.0.0.1:12333"</span>
<span class="nb">export </span><span class="nv">no_proxy</span><span class="o">=</span><span class="s2">"localhost, 127.0.0.1, 192.168.1.*"</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>snap代理通过命令行配置</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>snap <span class="nb">set </span>system proxy.http<span class="o">=</span><span class="s2">"http://127.0.0.1:12333"</span>
<span class="nb">sudo </span>snap <span class="nb">set </span>system proxy.https<span class="o">=</span><span class="s2">"http://127.0.0.1:12333"</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>apt, apt-get 代理配置</p>

    <div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">sudo </span>vim /etc/apt/apt.conf
Acquire::http::proxy <span class="s2">"http://127.0.0.1:12333"</span><span class="p">;</span>
Acquire::ftp::proxy <span class="s2">"ftp://127.0.0.1:12333"</span><span class="p">;</span>
Acquire::https::proxy <span class="s2">"https://127.0.0.1:12333"</span><span class="p">;</span>
</code></pre></div>    </div>
  </li>
  <li>
    <p>其他类似浏览器走全局代理或使用插件走代理</p>
  </li>
</ul>]]></content><author><name>Xuecheng Wu</name></author><category term="linux" /><category term="proxy" /><summary type="html"><![CDATA[linux系统下的各种代理配置踩坑]]></summary></entry><entry><title type="html">vsftpd config</title><link href="https://xxxcrel.github.io/2020/08/23/vsftpd/" rel="alternate" type="text/html" title="vsftpd config" /><published>2020-08-23T00:00:00+08:00</published><updated>2020-08-23T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2020/08/23/vsftpd</id><content type="html" xml:base="https://xxxcrel.github.io/2020/08/23/vsftpd/"><![CDATA[<h2 id="vsftpd">vsftpd</h2>

<blockquote>
  <p>linux下的vsftpd配置, 用来满足我允许指定ip匿名操控服务器资源</p>
</blockquote>

<ul>
  <li>
    <p>/etc/vsftpd.conf</p>

    <p><code class="language-plaintext highlighter-rouge">shell
//禁止本地用户登入
local_enable=NO
//开启可写权限
write_enable=YES
//允许匿名用户登入
anonymous_enable=YES
//匿名用户根目录
//单纯一个/home下的目录,非用户目录
anon_root=/home/ftp
//匿名用户是否有删除和重名名文件的权限
anon_other_write_enable=YES
//开启用来指定hosts.allow和hosts.deny里面ip权限
//standlone模式依赖libwrap.so库 可用ldd /usr/sbin/vsftpd查看系统是否有依赖
//hosts查找算法 先查找https.allow, 否则查看https.deny, 如无匹配, 默认允许访问
//所以如果想达到只允许某一ip和本地ftp客户端访问可设置:
//https.allow: vsftpd: 192.168.1.7
//https.allow: vsftpd: LOCAl
//https.deny: vsftpd: ALL
tcp_wrappers=YES
//允许i匿名用户上传
anon_upload_enable=YES
//允许匿名用户创建目录
anon_mkdir_wirte_enabel=YES
</code>conf</p>
  </li>
  <li>
    <p>匿名用户上传权限问题<br />
  vsftpd现在对anon_root目录的权限更加严格, 不能有写权限, 只有当开启allow_writeable_enable=YES才允许写权限
  如需让匿名用户上传修改等操作, 在根目录下创建一个新文件夹/pub, 并chown ftp:ftp /home/ftp/pub</p>
  </li>
</ul>]]></content><author><name>Xuecheng Wu</name></author><category term="FTP" /><category term="Server" /><summary type="html"><![CDATA[vsftpd]]></summary></entry><entry><title type="html">Solution for slow SSH connection</title><link href="https://xxxcrel.github.io/2020/06/10/ssh-note/" rel="alternate" type="text/html" title="Solution for slow SSH connection" /><published>2020-06-10T00:00:00+08:00</published><updated>2020-06-10T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2020/06/10/ssh-note</id><content type="html" xml:base="https://xxxcrel.github.io/2020/06/10/ssh-note/"><![CDATA[<h2 id="ssh反应迟钝解决办法">SSH反应迟钝解决办法</h2>

<h3 id="客户端">客户端</h3>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="nb">sudo </span>vim /etc/ssh/ssh_config
</code></pre></div></div>

<p>将GSSAPIAuthentication yes改成no（或者注释掉）</p>

<h3 id="服务器端">服务器端</h3>

<div class="language-shell highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="o">&gt;</span> <span class="nb">sudo </span>vim /etc/ssh/sshd_config
</code></pre></div></div>

<p>同将GSSAPIAuthentication yes改成no
UseDns yes改成no</p>]]></content><author><name>Xuecheng Wu</name></author><category term="Linux" /><summary type="html"><![CDATA[SSH反应迟钝解决办法]]></summary></entry><entry><title type="html">Spring Security Big Picture</title><link href="https://xxxcrel.github.io/2020/05/31/spring-security-overview/" rel="alternate" type="text/html" title="Spring Security Big Picture" /><published>2020-05-31T00:00:00+08:00</published><updated>2020-05-31T00:00:00+08:00</updated><id>https://xxxcrel.github.io/2020/05/31/spring-security-overview</id><content type="html" xml:base="https://xxxcrel.github.io/2020/05/31/spring-security-overview/"><![CDATA[<h1 id="spring-security-架构">Spring Security 架构</h1>

<h3 id="如何给系统增加安全">如何给系统增加安全</h3>

<ol>
  <li>Spring Security把安全认证分为两个步骤
    <ul>
      <li>
        <p>鉴定(authentication)
  <code class="language-plaintext highlighter-rouge">Authentication</code>: 此类相当于令牌,访问者通过携带相关认证信息封装进此类,如UsernamePasswordAuthentication
  封装了用户名和密码.</p>

        <p><code class="language-plaintext highlighter-rouge">AuthenticationProvider</code>: 该接口就两个方法</p>

        <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">//传入的参数是用户传入的认证信息, 此时的Authencation是未经认证的,</span>
  <span class="c1">//根据具体实现对其进行认证后返回, 该方法最终由ProviderManageer调用</span>
  <span class="nc">Authentication</span> <span class="nf">authenticate</span><span class="o">(</span><span class="nc">Authentication</span> <span class="n">authentication</span><span class="o">)</span> <span class="kd">throws</span> <span class="nc">AuthenticationException</span>
  <span class="c1">//根据Filter构造的具体Authentication判断当前鉴定器是否支持</span>
  <span class="kt">boolean</span> <span class="nf">supports</span><span class="o">(</span><span class="nc">Class</span><span class="o">&lt;?&gt;</span> <span class="n">authentication</span><span class="o">)</span>
</code></pre></div>        </div>

        <p><code class="language-plaintext highlighter-rouge">ProviderManger</code>: 该类核心即通过循环调用配置的AuthenticationProvider对用户进行鉴权</p>

        <div class="language-java highlighter-rouge"><div class="highlight"><pre class="highlight"><code>  <span class="c1">//循环调用配置的AuthenticationProvider</span>
  <span class="k">for</span> <span class="o">(</span><span class="nc">AuthenticationProvider</span> <span class="n">provider</span> <span class="o">:</span> <span class="n">getProviders</span><span class="o">())</span> <span class="o">{</span>
      <span class="c1">//该AuthenticationProvider是否支持</span>
      <span class="k">if</span> <span class="o">(!</span><span class="n">provider</span><span class="o">.</span><span class="na">supports</span><span class="o">(</span><span class="n">toTest</span><span class="o">))</span> <span class="o">{</span>
          <span class="k">continue</span><span class="o">;</span>
      <span class="o">}</span>
      <span class="o">...</span>
      <span class="k">try</span> <span class="o">{</span>
          <span class="c1">//进行签订</span>
          <span class="n">result</span> <span class="o">=</span> <span class="n">provider</span><span class="o">.</span><span class="na">authenticate</span><span class="o">(</span><span class="n">authentication</span><span class="o">);</span>
          <span class="k">if</span> <span class="o">(</span><span class="n">result</span> <span class="o">!=</span> <span class="kc">null</span><span class="o">)</span> <span class="o">{</span>
              <span class="n">copyDetails</span><span class="o">(</span><span class="n">authentication</span><span class="o">,</span> <span class="n">result</span><span class="o">);</span>
              <span class="k">break</span><span class="o">;</span>
          <span class="o">}</span>
      <span class="o">}</span>
      <span class="k">catch</span> <span class="o">(</span><span class="nc">AccountStatusException</span> <span class="o">|</span> <span class="nc">InternalAuthenticationServiceException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">prepareException</span><span class="o">(</span><span class="n">ex</span><span class="o">,</span> <span class="n">authentication</span><span class="o">);</span>
          <span class="c1">// SEC-546: Avoid polling additional providers if auth failure is due to</span>
          <span class="c1">// invalid account status</span>
          <span class="k">throw</span> <span class="n">ex</span><span class="o">;</span>
      <span class="o">}</span>
      <span class="k">catch</span> <span class="o">(</span><span class="nc">AuthenticationException</span> <span class="n">ex</span><span class="o">)</span> <span class="o">{</span>
          <span class="n">lastException</span> <span class="o">=</span> <span class="n">ex</span><span class="o">;</span>
      <span class="o">}</span>
  <span class="o">}</span>
</code></pre></div>        </div>

        <p><strong>上面的鉴定方式及相关类是Web无关的, 可以应用到任何需要认证的地方, 而Spring security web中通过自定义filter来应用这一认证机制,
  一般用法为authenticationManager.authenticate(authentication);</strong></p>
      </li>
      <li>
        <p>授权(authorization)
  当用户鉴定完成后, 即确定用户携带的身份信息真实且可靠, 一般会从持久化仓库(数据库, 缓存)获得相关信息,包括详细信息、拥有的权限等, 而授权的这一步骤发生在当用户鉴权完成后, 想要访问(Access)某一被保护资源(Secure Object)时对权限的鉴别.</p>

        <p><img src="/images/posts/spring/spring-security-big-picture.png" alt="load failed" /></p>

        <p>三种实现:
  1.Affirmative(肯定的): 一票决定权, 只要有一个Voter通过则视为投票通过
  2.Consensus(一致的): 民主选票, 根据granted和deny数量决定是否通过
  3.Unanimous(全体一致的): 挨个询问, 当出现一个不同意的则视为不通过</p>
      </li>
    </ul>
  </li>
</ol>]]></content><author><name>Xuecheng Wu</name></author><category term="Spring" /><category term="Security" /><summary type="html"><![CDATA[Spring Security 架构]]></summary></entry></feed>