<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Francis的博客</title>
  
  
  <link href="/atom.xml" rel="self"/>
  
  <link href="https://francisqiang.github.io/"/>
  <updated>2019-09-17T14:34:51.908Z</updated>
  <id>https://francisqiang.github.io/</id>
  
  <author>
    <name>Francis Qiang</name>
    
  </author>
  
  <generator uri="http://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Java核心技术I第九章集合笔记</title>
    <link href="https://francisqiang.github.io/2019/09/17/Java%E6%A0%B8%E5%BF%83%E6%8A%80%E6%9C%AFI%E7%AC%AC%E4%B9%9D%E7%AB%A0%E9%9B%86%E5%90%88%E7%AC%94%E8%AE%B0/"/>
    <id>https://francisqiang.github.io/2019/09/17/Java核心技术I第九章集合笔记/</id>
    <published>2019-09-17T14:05:16.000Z</published>
    <updated>2019-09-17T14:34:51.908Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一些乱七八糟的总结"><a href="#一些乱七八糟的总结" class="headerlink" title="一些乱七八糟的总结"></a>一些乱七八糟的总结</h2><ul><li><p>队列通常的两种实现方式：循环数组和链表。</p></li><li><p>在Java 8 中迭代可迭代对象可以直接使用</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">iterator.forEachRemaining(element -&gt; <span class="keyword">do</span> something with element);</span><br></pre></td></tr></table></figure><ul><li><p>Iterator 接口的 remove 方法将会删除上次调用 next 方法时返回的元素，如果调用 remove 方法前没有调用 next 方法会抛出 IllegalStateException。</p></li><li><p>hashCode 方法的定义是要保证包含相同严肃的两个集会得到相同的散列码。</p></li><li><p>| 集合类型 | 描述|<br>| :-: | :-: |<br>| ArrayList | 一种可以动态增长和缩减的索引序列 |<br>| LinkedList | 一种可以在任何位置进行高效地插入和删除操作的有序序列 |<br>| ArrayDeque | 一种用循环数组实现的双端队列 |<br>| HashSet| 一种没有重复元素的无序集合 |<br>| TreeSet | 一种有序集合 |<br>| EnumSet | 一种包含枚举类型的集合 |<br>| LinkedHashSet | 一种可以记住元素插入次序的集合 |<br>| PriorityQueue | 优先队列 |<br>| HashMap | 键值对映射 |<br>| TreeMap | 一种键值有序排列的映射表 |<br>| EnumMap | 一种属于枚举类型的映射表 |<br>| LinkedHashMap | 一种可以记住键值项添加次序的映射表 |<br>| WeakHashMap | 一种其值无用武之地之后可以被垃圾回收器回收的映射表 |<br>| IdentityHashMap | 一种用 == 而不是 equals 比较键值的映射表</p></li><li><p>ListIterator 继承了 Iterator 其中定义了一些反向遍历等方法。并且通过get(n)方法可以返回链表指定位置元素(其中做了一些优化 如果n大于size/2 那么从尾到头遍历)。</p></li><li><p>散列表通过链表数组实现，如果其中的桶被占满会出现 散列冲突现象。</p></li><li><p>TreeSet 在 Java 8 之后使用 红黑树 进行实现。TreeSet 性能会比 HashSet慢一些。如果需要自定义比较器 需要传入Comparator。</p></li><li><p>:: 为 Java 8之后的静态方法引用，注意 是引用不是调用。</p></li><li><p>Collections 工具类中提供了很多方法 比如排序，对集合的各种操作，返回不可更改集合， 返回受查视图等。</p></li><li><p>toArray() 返回的是一个 Object[] 数组。</p></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;一些乱七八糟的总结&quot;&gt;&lt;a href=&quot;#一些乱七八糟的总结&quot; class=&quot;headerlink&quot; title=&quot;一些乱七八糟的总结&quot;&gt;&lt;/a&gt;一些乱七八糟的总结&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;队列通常的两种实现方式：循环数组和链表。&lt;/p&gt;
&lt;/li&gt;
&lt;l
      
    
    </summary>
    
      <category term="Java SE" scheme="https://francisqiang.github.io/categories/Java-SE/"/>
    
    
  </entry>
  
  <entry>
    <title>Java编程思想读书笔记三</title>
    <link href="https://francisqiang.github.io/2019/09/16/Java%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B8%89/"/>
    <id>https://francisqiang.github.io/2019/09/16/Java编程思想读书笔记三/</id>
    <published>2019-09-16T02:01:17.000Z</published>
    <updated>2019-09-16T02:41:28.425Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一些乱七八糟的总结"><a href="#一些乱七八糟的总结" class="headerlink" title="一些乱七八糟的总结"></a>一些乱七八糟的总结</h2><ul><li><p>当 String 类型的对象进行修改操作的时候，编译器会自动引入 StringBuilder 类型， 编译器会自动构造一个 StringBuilder 对象，用以构造最终的 String 。但是别指望编译器能有多智能，当String类型的对象进行循环更改操作的时候， StringBuilder 对象是在循环体中构造的，所以每次循环都会创建一个 StringBuilder 对象，而我们通过显示的使用 StringBuilder对象就可以避免这个问题。</p></li><li><p>如果想要打印对象的内存地址，需要在重写的 toString() 方法中使用 super.toString() 方法。</p></li><li><p>一旦某个类的 Class 对象被载入内存，它就用来创建这个类的所有对象。</p></li><li><p>类加载过程 加载，链接，初始化。</p></li><li><p>使用嵌套类继承本身类并且实现 Null 接口(自己提前定义好的) 这样可以统一定义空对象而且避免空指针。</p></li><li><p>如果在集合内部使用数组的话，最好使用 Object[] 来存储而不是 T[] 。因为我们不太可能忘记这个数组的运行时类型，从而意外地引入缺陷(尽管大多数缺陷能再运行时被探测到)。</p></li><li><p>使用 System.arraycopy() 方法来赋值数组比for循环赋值来的更加高效便捷，对于对象的赋值，只是复制引用(浅拷贝)。</p></li><li><p>Arrays 类提供了重载之后的 equals() 方法 数组相等的条件是 数组元素个数相等，且对应位置上的元素也相等。</p></li><li><p>在已排序好的数组中可以使用 Arrays。binarySearch() 执行快速查找。</p></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;一些乱七八糟的总结&quot;&gt;&lt;a href=&quot;#一些乱七八糟的总结&quot; class=&quot;headerlink&quot; title=&quot;一些乱七八糟的总结&quot;&gt;&lt;/a&gt;一些乱七八糟的总结&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;当 String 类型的对象进行修改操作的时候，编译器会自动引入 
      
    
    </summary>
    
      <category term="Java SE" scheme="https://francisqiang.github.io/categories/Java-SE/"/>
    
    
  </entry>
  
  <entry>
    <title>Java编程思想读书笔记二</title>
    <link href="https://francisqiang.github.io/2019/09/10/Java%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%BA%8C/"/>
    <id>https://francisqiang.github.io/2019/09/10/Java编程思想读书笔记二/</id>
    <published>2019-09-10T05:14:30.000Z</published>
    <updated>2019-09-16T02:02:12.926Z</updated>
    
    <content type="html"><![CDATA[<h2 id="一些乱七八糟的总结"><a href="#一些乱七八糟的总结" class="headerlink" title="一些乱七八糟的总结"></a>一些乱七八糟的总结</h2><ul><li><p>public访问权限：接口访问权限，任何都可以访问</p></li><li><p>protected访问权限：继承访问权限，任何子类可以访问，包内可以访问</p></li><li><p>默认访问权限：包访问权限，包内可以访问</p></li><li><p>private访问权限：私有访问权限，只有自己可以访问、</p></li><li><p>一个既是 <em>static</em>又是 <em>final</em> 的域值占据一段不能改变的存储空间</p></li><li><p>在java的早期实现中，如果将方法声明为 final 就是同意编译器针对对该方法的所有调用都转为内嵌调用，这样将消除方法调用的开销，可以提升性能，但是当方法很大，程序代码会很膨胀，因而可能看不到内嵌带来的任何性能提升。现在java版本，虚拟机能探测到这样的情况并优化去掉这些效率反而降低的额外的内嵌调用，*所以现在不需要使用final方法进行优化了。</p></li><li><p>在main方法调用前会先进性静态变量的初始化，如果有继承那么先加载父类的静态变量然后再加载派生类的静态变量。</p></li><li><p>多态是后期绑定，想想向上转型。</p></li><li><p>Java中除了 static 方法和 final 方法 ( private 属于 final 方法)，其他所有的方法都是后期绑定。所以这两个特殊的方法不具有多态性。</p></li><li><p>如果为一个类创建了dispose清理方法，然后我们继承了这个类，所以我们需要在导出类中覆盖dispose方法并且调用基类的dispose方法。</p></li><li><p>基类初始化 -&gt; 基类构造 -&gt; 导出类初始化 -&gt; 导出类构造</p></li><li><p>销毁的顺序和初始化的顺序相反</p></li><li><p>用继承表达行为间的差异，并用字段表达状态上的变化</p></li><li><p>应尽量避免在不同接口中使用相同的方法名</p></li><li><p>当放入接口中的任何域都自动是 static 和 final 的</p></li><li><p>匿名类内部只能使用 final， 匿名类没有命名构造器</p></li><li><p>将内部类声明为 static 它只能访问外围的 static 类型的变量方法等</p></li><li><p>可以使用嵌套类测试main、方法，这样可以避免需要编译所有代码。</p></li><li><p>可以使用内部类实现接口从而代替外部类同时实现多个接口</p></li><li><p>继承内部类的时候需要先进行外部类的实例化</p></li><li><p>内部类不会被覆盖 不同类的内部类是完全两个不同的个体</p></li><li><p>TreeSet将元素存储在红黑树，而HashSet是散列函数，LinkedHashList因为查询速度的原因也使用了散列</p></li><li><p>异常链，即在捕捉到异常的时候再抛出异常。抛出异常的时候将上一个异常作为参数放入构造器中</p><p>如</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    xxxx</span><br><span class="line">&#125; <span class="keyword">catch</span> (IOException e) &#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> ClassNotFoundException(<span class="string">"xxxx"</span>, e);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></li><li><p>即使try中有return，finally块中的语句还是会执行，但是在finally中抛出异常会直接生吞try中的异常。</p></li><li><p>在覆盖方法的时候，只能抛出基类方法的异常说明里列出的那些异常(可以是派生的异常)</p></li><li><p>注意构造器抛出异常导致finally清除出现异常，最好的是在最外围再嵌套一个try catch。</p></li><li><p>异常匹配会匹配最近的合适的异常。</p></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;一些乱七八糟的总结&quot;&gt;&lt;a href=&quot;#一些乱七八糟的总结&quot; class=&quot;headerlink&quot; title=&quot;一些乱七八糟的总结&quot;&gt;&lt;/a&gt;一些乱七八糟的总结&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;&lt;p&gt;public访问权限：接口访问权限，任何都可以访问&lt;/p&gt;
&lt;/l
      
    
    </summary>
    
      <category term="Java SE" scheme="https://francisqiang.github.io/categories/Java-SE/"/>
    
    
  </entry>
  
  <entry>
    <title>Java编程思想读书笔记一</title>
    <link href="https://francisqiang.github.io/2019/08/29/Java%E7%BC%96%E7%A8%8B%E6%80%9D%E6%83%B3%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B8%80/"/>
    <id>https://francisqiang.github.io/2019/08/29/Java编程思想读书笔记一/</id>
    <published>2019-08-29T06:31:49.000Z</published>
    <updated>2019-08-29T07:26:59.252Z</updated>
    
    <content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>  以前一直不敢读Java编程思想这本书，第一个原因是因为从别人口中听来这是一本进阶的书，我感觉自己的水平还达不到，第二个是因为这本书太厚了，且不说七八百页，每页密密麻麻的字就让人望而却步。现如今尝试一下拜读此书，希望自己能获取什么。</p><p>  该系列博客仅为个人读书笔记，所以只是为了给自己看的，想着我的网站也没多少人会看，可能网站没备案导致搜索引擎搜不到，而最重要的是自己的文章只是泛泛而谈，精华确实很少，所以准备再花一段时间提升自己然后转战到类似掘金，简书等站点。</p><h2 id="一些乱七八糟的总结"><a href="#一些乱七八糟的总结" class="headerlink" title="一些乱七八糟的总结"></a>一些乱七八糟的总结</h2><ul><li><p>当使用父类作为多态的时候，应充分考虑抽象性</p><p>因为如果将子类 <strong><em>向上转型</em></strong> 为基类的话，那么子类很多拓展的功能是不能使用的。例如:</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">skin</span><span class="params">(Fruit fruit)</span> </span>&#123;</span><br><span class="line">    ...</span><br><span class="line">    fruit.xxx;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  这个时候应该考虑到，我们操作的是水果，我们中能调用水果中的方法而不能调用子类如苹果扩展出来的新方法。</p><ul><li><p>Java要确定每种 <strong><em>基本类型</em></strong> 所占存储空间的大小</p><p>因为他们的大小不像其他语言一样随着硬件架构的变化而变化。</p></li><li><p>可以使用 BigInteger 或 BigDecimal 来实现高精度。</p><p>计算机的二进制会产生精度误差，使用这两个可以避免，但是效率会低。</p></li><li><p>Java 中没有作用域隐藏</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">&#123;</span><br><span class="line">    <span class="keyword">int</span> x = <span class="number">12</span>;</span><br><span class="line">    &#123;</span><br><span class="line">        <span class="keyword">int</span> x = <span class="number">96</span>;  <span class="comment">// 非法的</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  上述代码在 java 中是非法的编译器会告诉你 x 已经定义，而 C++ 中可以，因为它会把外面的隐藏。</p><ul><li><p>方法和参数列表合起来被称为 <strong><em>方法签名</em></strong> 它们唯一标识着某个方法。</p></li><li><p>main 函数中的 String[] args代表命令行参数，就比如JVM，Tomcat等启动命令附带的参数。</p></li><li><p>equals() 方法默认行为是比较引用</p></li><li><p>Java 允许我们把任何基本数据类型转换成别的基本数据类型，但布尔型除外</p></li><li><p>如果int 和 long相乘得到的结果会变为long，也就是会往 <strong><em>最大的数据类型</em></strong> 转换。</p></li><li><p>如果两个较大的基本数据类型相加如 int ，其结果可能会溢出。</p></li><li><p>在循环外可以定义标签来解决多层循环嵌套无法跳出的情况。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">outer:</span><br><span class="line"><span class="keyword">while</span>(<span class="keyword">true</span>) &#123;</span><br><span class="line">    ...</span><br><span class="line">    inner:</span><br><span class="line">    <span class="keyword">while</span>(<span class="keyword">true</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span>(...) &#123;</span><br><span class="line">            <span class="comment">// break 会跳到标签，但是不会再次进入循环语句</span></span><br><span class="line">            <span class="keyword">break</span> outer;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span>(...) &#123;</span><br><span class="line">            <span class="comment">// continue 会调到标签并且会再次进入循环语句</span></span><br><span class="line">            <span class="keyword">continue</span> inner;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>对象可能不被垃圾回收，垃圾回收不等于 C++ 中的析构。</p></li><li><p>不要使用finalize() 作为通用的清理方法。</p><p>之所以有finalize 是因为给对象分配内存的时候可能采用的不是new 而是使用c或c++的方式，而释放他们需要使用free类似函数，所以需要在finalize中用本地方法调用它。</p></li><li><p>new可以被看作 static 方法，比如</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Initialization</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 这条语句会和static方法一样在构造对象之前执行</span></span><br><span class="line">    Table table = <span class="keyword">new</span> Table();</span><br><span class="line">    <span class="comment">// 但这个不行</span></span><br><span class="line">    Table table1;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  总结一下对象的创建过程:</p><ol><li><p>即使没有显示地使用 static 关键字，构造器也是静态方法。因此当首次类型为Dog的对象的时候，或者Dog的静态方法/静态域首次被访问时，java解释器必须查找类路径，以定位Dog.class文件。</p></li><li><p>然后载入Dog.class(创建一个Class对象) 有关静态初始化的所有动作都会执行。因此，静态初始化只在Class对象首次加载的时候进行一次。</p></li><li><p>当用 new Dog() 创建爱你对象的时候，首先将在堆上为Dog对象分配足够的存储空间。</p></li><li><p>这块存储空间会被清零，这就自动将Dog对象中的所有基本类型数据都设置成了默认值，而引用被设置成了null。</p></li><li><p>执行所有出现于字段定义处的初始化动作。</p></li><li><p>执行构造器。</p></li></ol><ul><li><p>静态块代码只有在首次生成这个类的一个对象时，或者首次访问属于那个类的 <strong><em>静态数据成员</em></strong>时会执行，如果没有则不执行。</p></li><li><p>实例化子句会在构造器执行之前执行，而且每次都会执行。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Cat</span> </span>&#123;</span><br><span class="line">    &#123;</span><br><span class="line">        System.out.println(<span class="string">"这是实例化子句"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>可变参数其实传进去的就是一个数组，尽量少使用可变参数，因为可变参数会产生冲突，可以给产生冲突的方法都加上可变参数，但不一定解决。</p></li><li><p>switch语句和enum搭配一起使用特别实用。</p></li></ul>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;前言&quot;&gt;&lt;a href=&quot;#前言&quot; class=&quot;headerlink&quot; title=&quot;前言&quot;&gt;&lt;/a&gt;前言&lt;/h2&gt;&lt;p&gt;  以前一直不敢读Java编程思想这本书，第一个原因是因为从别人口中听来这是一本进阶的书，我感觉自己的水平还达不到，第二个是因为这本书太厚了
      
    
    </summary>
    
      <category term="Java SE" scheme="https://francisqiang.github.io/categories/Java-SE/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美学习笔记十</title>
    <link href="https://francisqiang.github.io/2019/08/22/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%8D%81/"/>
    <id>https://francisqiang.github.io/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/</id>
    <published>2019-08-22T04:00:50.000Z</published>
    <updated>2019-08-22T07:04:24.784Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Java并发保重线程同步器原理剖析"><a href="#Java并发保重线程同步器原理剖析" class="headerlink" title="Java并发保重线程同步器原理剖析"></a>Java并发保重线程同步器原理剖析</h2><h3 id="CountDownLatch-原理剖析"><a href="#CountDownLatch-原理剖析" class="headerlink" title="CountDownLatch 原理剖析"></a>CountDownLatch 原理剖析</h3><h4 id="CountDownLatch-介绍"><a href="#CountDownLatch-介绍" class="headerlink" title="CountDownLatch 介绍"></a>CountDownLatch 介绍</h4><p>  日常开发中可能我们可能遇到需要开启多个子线程去并行执行任务，并且 <strong><em>主线程需要等待所有子线程执行完毕后再进行汇总</em></strong> 的场景。我们可以使用 join() 方法(等待该子线程线程执行完毕)，但是join()不灵活而且很多场景可能使用不了，所以JDK中提供了 <strong><em>CountDownLatch</em></strong> 这个类。我们来看一下 CountDownLatch 的使用案例。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JoinCountDownLatch</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> CountDownLatch countDownLatch = <span class="keyword">new</span> CountDownLatch(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    Thread thread1 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">          System.out.println(<span class="string">"child thread1 running!"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch.countDown();</span><br><span class="line">        System.out.println(<span class="string">"child thread1 over!"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    Thread thread2 = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">          System.out.println(<span class="string">"child thread2 running!"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch.countDown();</span><br><span class="line">        System.out.println(<span class="string">"child thread2 over!"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    thread1.start();</span><br><span class="line">    thread2.start();</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">"wait all child thread over"</span>);</span><br><span class="line"></span><br><span class="line">    countDownLatch.await();</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">"all child thread over"</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  运行结果:</p><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/1.jpg" alt="CountDownLatch演示"></p><p>  我们可以看到 main 函数最后一条语句总是等待两个子线程运行结束才会运行。</p><p>  当然我们还可以使用线程池的方式创建，以避免直接操作Thread。而且使用线程池来管理线程一般直接添加 Runnable 到线程池，这个时候我们就没有办法调用 join 方法了，所以说 CountDownLatch 比 join 更具有灵活性。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">JoinCountDownLatch</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">volatile</span> CountDownLatch countDownLatch = <span class="keyword">new</span> CountDownLatch(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line"></span><br><span class="line">    ExecutorService executorService = Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">          System.out.println(<span class="string">"child thread1 running!"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch.countDown();</span><br><span class="line">        System.out.println(<span class="string">"child thread1 over!"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">1000</span>; i++) &#123;</span><br><span class="line">          System.out.println(<span class="string">"child thread2 running!"</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        countDownLatch.countDown();</span><br><span class="line">        System.out.println(<span class="string">"child thread2 over!"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">"wait all child thread over"</span>);</span><br><span class="line"></span><br><span class="line">    countDownLatch.await();</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">"all child thread over"</span>);</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="CountDownLatch-实现原理探究"><a href="#CountDownLatch-实现原理探究" class="headerlink" title="CountDownLatch 实现原理探究"></a>CountDownLatch 实现原理探究</h4><p>  在学习 AQS 的时候提到过， AQS 是同步器的基本组成部分，而且其中 AQS 的 state 是用来表示 CountDownLatch 的计数器的。我们可以查看 CountDownLatch 的类图结构。</p><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/2.jpg" alt="CountDownLatch演示"></p><p>  因为 Sync 是继承了 AQS 的，他实现了一些 AQS 的方法，所以可以说 CountDownLatch 是基于 AQS 实现的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CountDownLatch</span><span class="params">(<span class="keyword">int</span> count)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (count &lt; <span class="number">0</span>) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"count &lt; 0"</span>);</span><br><span class="line">    <span class="keyword">this</span>.sync = <span class="keyword">new</span> Sync(count);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 设置 state</span></span><br><span class="line">Sync(<span class="keyword">int</span> count) &#123;</span><br><span class="line">    setState(count);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>void await()</p><p>当线程调用 CountDownLatch 对象的 await() 方法后，<em>当前线程会被阻塞(上面案例是主线程调用的await 所以主线程会被阻塞)</em>，<strong>当所有线程调用了 CountDownLatch 的 countDown 方法后，即计数器的值为0的时候，调用 await 方法的线程会返回，或者当其他线程调用了当前被阻塞线程的 interrupt() 方法中断了饿当前线程，当前线程就会抛出 InterruptedException 异常返回</strong>。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">await</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="comment">// 调用的是sync方法 其实就是调用的 AQS</span></span><br><span class="line">    sync.acquireSharedInterruptibly(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquireSharedInterruptibly</span><span class="params">(<span class="keyword">int</span> arg)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (Thread.interrupted())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> InterruptedException();、</span><br><span class="line">    <span class="comment">// 调用 tryAcquireShared 判断 这里AQS没有实现 调用的是实现类Sync的tryAcquireShared</span></span><br><span class="line">    <span class="keyword">if</span> (tryAcquireShared(arg) &lt; <span class="number">0</span>)</span><br><span class="line">        doAcquireSharedInterruptibly(arg);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Sync的 tryAcquireShared</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">tryAcquireShared</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 返回的state 不为0就返回 -1调用doAcquireSharedInterruptibly阻塞</span></span><br><span class="line">    <span class="comment">// 为0不阻塞</span></span><br><span class="line">    <span class="keyword">return</span> (getState() == <span class="number">0</span>) ? <span class="number">1</span> : -<span class="number">1</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 回顾一下 AQS 的阻塞 这是获取共享资源被阻塞 </span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doAcquireSharedInterruptibly</span><span class="params">(<span class="keyword">int</span> arg)</span></span></span><br><span class="line"><span class="function">    <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> Node node = addWaiter(Node.SHARED);</span><br><span class="line">    <span class="keyword">boolean</span> failed = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">final</span> Node p = node.predecessor();</span><br><span class="line">            <span class="keyword">if</span> (p == head) &#123;</span><br><span class="line">                <span class="keyword">int</span> r = tryAcquireShared(arg);</span><br><span class="line">                <span class="keyword">if</span> (r &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    setHeadAndPropagate(node, r);</span><br><span class="line">                    p.next = <span class="keyword">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                    failed = <span class="keyword">false</span>;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                parkAndCheckInterrupt())</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> InterruptedException();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (failed)</span><br><span class="line">            cancelAcquire(node);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>void countDown() 方法</p><p><em>线程调用该方法后 计数器的值递减，如果递减后计数器为0则唤醒因为调用 await 方法而被阻塞的线程，否则什么都不做</em>。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">countDown</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.releaseShared(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 这是 AQS 定义的</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">releaseShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryReleaseShared(arg)) &#123;</span><br><span class="line">        doReleaseShared();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 这是Sync实现的</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">tryReleaseShared</span><span class="params">(<span class="keyword">int</span> releases)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Decrement count; signal when transition to zero</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">// 获取 state</span></span><br><span class="line">        <span class="keyword">int</span> c = getState();</span><br><span class="line">        <span class="comment">// 入过为0则false 意思就是什么都不做</span></span><br><span class="line">        <span class="keyword">if</span> (c == <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">int</span> nextc = c-<span class="number">1</span>;</span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(c, nextc))</span><br><span class="line">            <span class="comment">// 如果CAS设置成功那么判断此时state是否为0 如果为0那么返回true</span></span><br><span class="line">            <span class="comment">// 返回true代表要对阻塞线程进行唤醒</span></span><br><span class="line">            <span class="comment">// 返回false代表什么都不做</span></span><br><span class="line">            <span class="keyword">return</span> nextc == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// AQS 中唤醒阻塞线程</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doReleaseShared</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h != tail) &#123;</span><br><span class="line">            <span class="keyword">int</span> ws = h.waitStatus;</span><br><span class="line">            <span class="keyword">if</span> (ws == Node.SIGNAL) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!compareAndSetWaitStatus(h, Node.SIGNAL, <span class="number">0</span>))</span><br><span class="line">                    <span class="keyword">continue</span>;            <span class="comment">// loop to recheck cases</span></span><br><span class="line">                unparkSuccessor(h);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (ws == <span class="number">0</span> &amp;&amp;</span><br><span class="line">                     !compareAndSetWaitStatus(h, <span class="number">0</span>, Node.PROPAGATE))</span><br><span class="line">                <span class="keyword">continue</span>;                <span class="comment">// loop on failed CAS</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (h == head)                   <span class="comment">// loop if head changed</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="CountDownLatch-小结"><a href="#CountDownLatch-小结" class="headerlink" title="CountDownLatch 小结"></a>CountDownLatch 小结</h4><p>  CountDownLatch 通过使用 AQS 实现，其中使用 AQS 的状态变量来存放计数器的值，当调用countDown方法的时候使state递减，调用await未得到满足的时候会 调用线程会被放入 AQS 阻塞队列中等待。 当其他线程调用 countDown方法并得到递减后的state为0的时候会调用 AQS 的 doReleaseShared 方法来激活由于调用 await() 方法而被阻塞的线程。</p><h3 id="回环屏障-CyclicBarrier"><a href="#回环屏障-CyclicBarrier" class="headerlink" title="回环屏障 CyclicBarrier"></a>回环屏障 CyclicBarrier</h3><p>  对于 CountDownLatch 来说，线程同步后，等到计数器为0之后在调用 await 和 countDown 方法都会立即返回，也就是说 <strong><em>CountDownLatch 是一次性的</em></strong>。而 CyclicBarrier 会在所有子线程执行完毕后 <strong><em>重置 CyclicBarrier 的状态</em></strong> 。</p><h4 id="CyclicBarrier-使用案例介绍"><a href="#CyclicBarrier-使用案例介绍" class="headerlink" title="CyclicBarrier 使用案例介绍"></a>CyclicBarrier 使用案例介绍</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CyclicBarrierTest1</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 这里构造方法里可以添加任务 这个任务会在所有调用await方法的线程全部到达</span></span><br><span class="line">  <span class="comment">// 屏障点(计数器为0)的时候调用</span></span><br><span class="line">  <span class="comment">// 并且等到这个任务执行完毕 被阻塞的线程会被唤醒继续执行</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> CyclicBarrier cyclicBarrier = <span class="keyword">new</span> CyclicBarrier(<span class="number">2</span>,</span><br><span class="line">      () -&gt; System.out.println(Thread.currentThread() + <span class="string">"task1 merge result"</span>));</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    ExecutorService executorService = Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"task-1"</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"enter in barrier"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          <span class="comment">// 计数器会递减</span></span><br><span class="line">          cyclicBarrier.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BrokenBarrierException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"out barrier"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"task-2"</span>);</span><br><span class="line"></span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"enter in barrier"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          cyclicBarrier.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BrokenBarrierException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 被唤醒后继续执行</span></span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"out barrier"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.shutdown();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  运行结果:</p><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/4.jpg" alt="CountDownLatch演示"></p><p>  我们再来看一个 重复使用 的例子。这里我们没执行完一个步骤就汇总一次</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CyclicBarrierTest2</span> </span>&#123;</span><br><span class="line">  <span class="comment">// 计数器初始化为2 并且定义了汇总任务</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> CyclicBarrier cyclicBarrier = <span class="keyword">new</span> CyclicBarrier(<span class="number">2</span>,</span><br><span class="line">      () -&gt; System.out.println(Thread.currentThread() + <span class="string">"merge"</span>));</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    ExecutorService executorService = Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"step1"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          cyclicBarrier.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BrokenBarrierException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"step2"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          cyclicBarrier.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BrokenBarrierException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"out barrier"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"step1"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          cyclicBarrier.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BrokenBarrierException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"step2"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          cyclicBarrier.await();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125; <span class="keyword">catch</span> (BrokenBarrierException e) &#123;</span><br><span class="line">          e.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"out barrier"</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.shutdown();</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  运行结果:</p><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/3.jpg" alt="CountDownLatch演示"></p><h4 id="CyclicBarrier-实现原理探究"><a href="#CyclicBarrier-实现原理探究" class="headerlink" title="CyclicBarrier 实现原理探究"></a>CyclicBarrier 实现原理探究</h4><p>  我们首先看一下 CyclicBarrier 的类图。</p><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/5.jpg" alt="CyclicBarrier"></p><p>  由此我们可以知道 CyclicBarrier 是通过 <strong><em>独占锁</em></strong> 来实现的。<em>parties</em>用来记录线程个数，这里表示多少个线程调用await方法后 所有线程才会冲破屏障。<strong><em>count 一开始等于 parties，count计数器变为0之后会将parties的值重新赋值给count，以达到重复利用的功能。</em></strong></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CyclicBarrier</span><span class="params">(<span class="keyword">int</span> parties, Runnable barrierAction)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (parties &lt;= <span class="number">0</span>) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">    <span class="keyword">this</span>.parties = parties;</span><br><span class="line">    <span class="comment">// 将parties 赋值给count</span></span><br><span class="line">    <span class="keyword">this</span>.count = parties;</span><br><span class="line">    <span class="keyword">this</span>.barrierCommand = barrierAction;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CyclicBarrier</span><span class="params">(<span class="keyword">int</span> parties)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(parties, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 独占锁</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"><span class="comment">// 使用trip条件变量实现同步</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Condition trip = lock.newCondition();</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">int</span> parties;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Runnable barrierCommand;</span><br><span class="line"><span class="keyword">private</span> Generation generation = <span class="keyword">new</span> Generation();</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">int</span> count;</span><br><span class="line"><span class="comment">// 里面的broken记录该屏障是否被打破</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">Generation</span> </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> broken = <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>int await() 方法</p><p>当前线程调用该方法会被阻塞，知道满足下面条件之一才会返回:</p><ol><li>parties个线程调用了该方法，即到达屏障点</li><li>其他线程调用了当前线程的interrupt() 方法</li><li>与当前屏障点关联的 broken 标志被设置为 true 会抛出 BrokenbarrierException 然后返回。</li></ol></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">await</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException, BrokenBarrierException </span>&#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> dowait(<span class="keyword">false</span>, <span class="number">0L</span>);</span><br><span class="line">    &#125; <span class="keyword">catch</span> (TimeoutException toe) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> Error(toe); <span class="comment">// cannot happen</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>int dowait() 方法</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">dowait</span><span class="params">(<span class="keyword">boolean</span> timed, <span class="keyword">long</span> nanos)</span></span></span><br><span class="line"><span class="function">    <span class="keyword">throws</span> InterruptedException, BrokenBarrierException,</span></span><br><span class="line"><span class="function">           TimeoutException </span>&#123;</span><br><span class="line">    <span class="comment">// 获取锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 判断broken标志</span></span><br><span class="line">        <span class="keyword">final</span> Generation g = generation;</span><br><span class="line">        <span class="keyword">if</span> (g.broken)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> BrokenBarrierException();</span><br><span class="line">        <span class="comment">// 是否被打断</span></span><br><span class="line">        <span class="keyword">if</span> (Thread.interrupted()) &#123;</span><br><span class="line">            <span class="comment">// 被打断也要重置和唤醒</span></span><br><span class="line">            breakBarrier();</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> InterruptedException();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 将count递减</span></span><br><span class="line">        <span class="keyword">int</span> index = --count;</span><br><span class="line">        <span class="comment">// 如果执行后为0 那么执行屏障的任务</span></span><br><span class="line">        <span class="keyword">if</span> (index == <span class="number">0</span>) &#123;  <span class="comment">// tripped</span></span><br><span class="line">            <span class="keyword">boolean</span> ranAction = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="keyword">final</span> Runnable command = barrierCommand;</span><br><span class="line">                <span class="keyword">if</span> (command != <span class="keyword">null</span>)</span><br><span class="line">                    command.run();</span><br><span class="line">                ranAction = <span class="keyword">true</span>;</span><br><span class="line">                <span class="comment">// 激活其他因调用await而被阻塞的线程 并重置cyclicBarrier</span></span><br><span class="line">                nextGeneration();</span><br><span class="line">                <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="comment">// 重置和唤醒</span></span><br><span class="line">                <span class="keyword">if</span> (!ranAction)</span><br><span class="line">                    breakBarrier();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// loop until tripped, broken, interrupted, or timed out</span></span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 没有设置超时时间</span></span><br><span class="line">                <span class="keyword">if</span> (!timed)</span><br><span class="line">                    <span class="comment">// 放入条件变量阻塞队列</span></span><br><span class="line">                    trip.await();</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (nanos &gt; <span class="number">0L</span>)</span><br><span class="line">                    nanos = trip.awaitNanos(nanos);</span><br><span class="line">            &#125; <span class="keyword">catch</span> (InterruptedException ie) &#123;</span><br><span class="line">                <span class="keyword">if</span> (g == generation &amp;&amp; ! g.broken) &#123;</span><br><span class="line">                    breakBarrier();</span><br><span class="line">                    <span class="keyword">throw</span> ie;</span><br><span class="line">                &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// We're about to finish waiting even if we had not</span></span><br><span class="line">                    <span class="comment">// been interrupted, so this interrupt is deemed to</span></span><br><span class="line">                    <span class="comment">// "belong" to subsequent execution.</span></span><br><span class="line">                    Thread.currentThread().interrupt();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (g.broken)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> BrokenBarrierException();</span><br><span class="line">            <span class="keyword">if</span> (g != generation)</span><br><span class="line">                <span class="keyword">return</span> index;</span><br><span class="line">            <span class="keyword">if</span> (timed &amp;&amp; nanos &lt;= <span class="number">0L</span>) &#123;</span><br><span class="line">                breakBarrier();</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> TimeoutException();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 唤醒阻塞队列并重置</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">nextGeneration</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 唤醒条件队列的所有阻塞线程</span></span><br><span class="line">    trip.signalAll();</span><br><span class="line">    <span class="comment">// 重置</span></span><br><span class="line">    count = parties;</span><br><span class="line">    generation = <span class="keyword">new</span> Generation();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">breakBarrier</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    generation.broken = <span class="keyword">true</span>;</span><br><span class="line">    count = parties;</span><br><span class="line">    trip.signalAll();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="CyclicBarrier-小结"><a href="#CyclicBarrier-小结" class="headerlink" title="CyclicBarrier 小结"></a>CyclicBarrier 小结</h4><p>  与 CountDownLatch 不同的是 CyclicBarrier 可以实现复用，并且特别适用分段任务有序执行的场景。CyclicBarrier适用独占锁来保证计数器的原子性更新，并使用条件队列来实现线程同步。</p><h3 id="信号量-Semaphore-原理探究"><a href="#信号量-Semaphore-原理探究" class="headerlink" title="信号量 Semaphore 原理探究"></a>信号量 Semaphore 原理探究</h3><p>  Semaphore 也是 Java 中的一个同步器，和前面的 CountDownLatch 和 CyclicBarrier 不同的是 Semaphore内部的计数器是递增的，并且在初始化的时候可以指定一个初始值，但是 <strong><em>并不需要知道需要同步的线程个数， 而是在需要同步的地方调用 acquire 方法时指定需要同步的线程个数</em></strong> 。</p><h4 id="Semaphore-案例介绍"><a href="#Semaphore-案例介绍" class="headerlink" title="Semaphore 案例介绍"></a>Semaphore 案例介绍</h4><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SemaphoreTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 生成 Semaphore 初始化计数器为0 因为是递增 所以只需要在需要</span></span><br><span class="line">  <span class="comment">// 的时候指定 递增到多少 不需要一开始指定需要同步的线程个数</span></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> Semaphore semaphore = <span class="keyword">new</span> Semaphore(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line"></span><br><span class="line">    ExecutorService executorService = Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"over"</span>);</span><br><span class="line">        semaphore.release();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"over"</span>);</span><br><span class="line">        semaphore.release();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 需要时指定acquire</span></span><br><span class="line">    semaphore.acquire(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">"merge"</span>);</span><br><span class="line"></span><br><span class="line">    executorService.shutdown();</span><br><span class="line"></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  运行结果:</p><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/6.jpg" alt="Semaphore"></p><p>  我们再来看一下 使用 Semaphore 实现复用。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">SemaphoreTest2</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">static</span> Semaphore semaphore = <span class="keyword">new</span> Semaphore(<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line"></span><br><span class="line">    ExecutorService executorService = Executors.newFixedThreadPool(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"taskA over"</span>);</span><br><span class="line">        semaphore.release();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"taskA over"</span>);</span><br><span class="line">        semaphore.release();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    semaphore.acquire(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"taskB over"</span>);</span><br><span class="line">        semaphore.release();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    executorService.submit(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(Thread.currentThread() + <span class="string">"taskB over"</span>);</span><br><span class="line">        semaphore.release();</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line"></span><br><span class="line">    semaphore.acquire(<span class="number">2</span>);</span><br><span class="line"></span><br><span class="line">    System.out.println(<span class="string">"all task is over"</span>);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    executorService.shutdown();</span><br><span class="line"></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/7.jpg" alt="Semaphore"></p><p>  怎么能够复用呢？ 其实是因为主线程调用 acquire 方法返回后 信号量会重新变成0。</p><h4 id="Semaphore-实现原理探究"><a href="#Semaphore-实现原理探究" class="headerlink" title="Semaphore 实现原理探究"></a>Semaphore 实现原理探究</h4><p>  <img src="/2019/08/22/Java并发编程学习——Java并发编程之美学习笔记十/Semaphore.png" alt="Semaphore"></p><p>  由类图可知，我们还是使用 AQS 实现的，并且还实现了获取信号量时是采用 公平策略 还是 非公平策略。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// permits是初始化的计数器值</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Semaphore</span><span class="params">(<span class="keyword">int</span> permits)</span> </span>&#123;</span><br><span class="line">    sync = <span class="keyword">new</span> NonfairSync(permits);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Semaphore</span><span class="params">(<span class="keyword">int</span> permits, <span class="keyword">boolean</span> fair)</span> </span>&#123;</span><br><span class="line">    sync = fair ? <span class="keyword">new</span> FairSync(permits) : <span class="keyword">new</span> NonfairSync(permits);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>void acquire() 方法</p><p>当前线程调用该方法是 <strong><em>希望获取一个信号量资源</em></strong>。如果信号量个数大于0则当前信号量的计数会减一，然后该方法直接返回。否则如果当前信号量个数等于0，则当前线程会被放入 AQS 的阻塞队列。 当其他线程调用该线程的 interrupt() 方法中断了当前线程，当前线程会抛出 InterruptedException 然后返回。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    sync.acquireSharedInterruptibly(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//  AQS 实现的</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquireSharedInterruptibly</span><span class="params">(<span class="keyword">int</span> arg)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="comment">// 如果打断 抛出异常</span></span><br><span class="line">    <span class="keyword">if</span> (Thread.interrupted())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> InterruptedException();</span><br><span class="line">    <span class="comment">// 调用sync自己实现的tryAcquireShared 尝试获取共享资源</span></span><br><span class="line">    <span class="comment">// 如果小于0 阻塞当前调用线程</span></span><br><span class="line">    <span class="keyword">if</span> (tryAcquireShared(arg) &lt; <span class="number">0</span>)</span><br><span class="line">        doAcquireSharedInterruptibly(arg);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 我们首先看非公平实现</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">tryAcquireShared</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> nonfairTryAcquireShared(acquires);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">int</span> <span class="title">nonfairTryAcquireShared</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 无限循环</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">// 获取状态值</span></span><br><span class="line">        <span class="keyword">int</span> available = getState();</span><br><span class="line">        <span class="comment">// 剩余量</span></span><br><span class="line">        <span class="keyword">int</span> remaining = available - acquires;</span><br><span class="line">        <span class="comment">// 如果剩余量小于0 那么返回剩余量 此时为负值 那么会直接调用doAcquireSharedInterruptibly</span></span><br><span class="line">        <span class="comment">// 不小于0 那么cas设置状态值 如果成功返回剩余量 设置不成功就一直循环</span></span><br><span class="line">        <span class="comment">// 如果返回大于0返回剩余值</span></span><br><span class="line">        <span class="keyword">if</span> (remaining &lt; <span class="number">0</span> ||</span><br><span class="line">            compareAndSetState(available, remaining))</span><br><span class="line">            <span class="keyword">return</span> remaining;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 我们看一下 AQS 阻塞 放入阻塞队列里</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doAcquireSharedInterruptibly</span><span class="params">(<span class="keyword">int</span> arg)</span></span></span><br><span class="line"><span class="function">    <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> Node node = addWaiter(Node.SHARED);</span><br><span class="line">    <span class="keyword">boolean</span> failed = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="keyword">final</span> Node p = node.predecessor();</span><br><span class="line">            <span class="keyword">if</span> (p == head) &#123;</span><br><span class="line">                <span class="keyword">int</span> r = tryAcquireShared(arg);</span><br><span class="line">                <span class="keyword">if</span> (r &gt;= <span class="number">0</span>) &#123;</span><br><span class="line">                    setHeadAndPropagate(node, r);</span><br><span class="line">                    p.next = <span class="keyword">null</span>; <span class="comment">// help GC</span></span><br><span class="line">                    failed = <span class="keyword">false</span>;</span><br><span class="line">                    <span class="keyword">return</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (shouldParkAfterFailedAcquire(p, node) &amp;&amp;</span><br><span class="line">                parkAndCheckInterrupt())</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> InterruptedException();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (failed)</span><br><span class="line">            cancelAcquire(node);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>void acquire(int permits)</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> permits)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (permits &lt; <span class="number">0</span>) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">    <span class="comment">// 这里指定了个数</span></span><br><span class="line">    sync.acquireSharedInterruptibly(permits);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>void release()</p><p>该方法是把当前的 Semaphore 对象的信号量值增加1，如果当前线程又因为调用 acquire 方法被阻塞放入 AQS 阻塞队列中，则会 <strong><em>根据公平策略选择一个信号量个数能被满足的线程进行激活</em></strong>， 激活的线程会尝试获取刚增加的信号量。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">release</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.releaseShared(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// AQS</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">releaseShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryReleaseShared(arg)) &#123;</span><br><span class="line">        doReleaseShared();</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// Sync实现到的tryReleaseShared</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryReleaseShared</span><span class="params">(<span class="keyword">int</span> releases)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 无限循环</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">// 获取当前信号量</span></span><br><span class="line">        <span class="keyword">int</span> current = getState();</span><br><span class="line">        <span class="comment">// 获取加上release的信号量</span></span><br><span class="line">        <span class="keyword">int</span> next = current + releases;</span><br><span class="line">        <span class="comment">// 如果相加后小于current 说明发生了整型溢出则抛出异常</span></span><br><span class="line">        <span class="keyword">if</span> (next &lt; current) <span class="comment">// overflow</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">"Maximum permit count exceeded"</span>);</span><br><span class="line">        <span class="comment">// 尝试CAS设置信号量 直到成功为止</span></span><br><span class="line">        <span class="comment">// 成功后会调用doReleaseShared 可选择AQS阻塞队列符合要求的线程进行激活</span></span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(current, next))</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// AQS 进行激活</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doReleaseShared</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h != tail) &#123;</span><br><span class="line">            <span class="keyword">int</span> ws = h.waitStatus;</span><br><span class="line">            <span class="keyword">if</span> (ws == Node.SIGNAL) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!compareAndSetWaitStatus(h, Node.SIGNAL, <span class="number">0</span>))</span><br><span class="line">                    <span class="keyword">continue</span>;            <span class="comment">// loop to recheck cases</span></span><br><span class="line">                unparkSuccessor(h);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (ws == <span class="number">0</span> &amp;&amp;</span><br><span class="line">                     !compareAndSetWaitStatus(h, <span class="number">0</span>, Node.PROPAGATE))</span><br><span class="line">                <span class="keyword">continue</span>;                <span class="comment">// loop on failed CAS</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (h == head)                   <span class="comment">// loop if head changed</span></span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h4 id="Semaphore-小结"><a href="#Semaphore-小结" class="headerlink" title="Semaphore 小结"></a>Semaphore 小结</h4><p>  Semaphore 内部使用了一个递增的计数器，这样就可以不在初始化的时候指定需要同步的线程个数了。它通过 AQS 实现，并且在获取信号量时有公平和非公平策略选择。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Java并发保重线程同步器原理剖析&quot;&gt;&lt;a href=&quot;#Java并发保重线程同步器原理剖析&quot; class=&quot;headerlink&quot; title=&quot;Java并发保重线程同步器原理剖析&quot;&gt;&lt;/a&gt;Java并发保重线程同步器原理剖析&lt;/h2&gt;&lt;h3 id=&quot;Count
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美学习笔记九</title>
    <link href="https://francisqiang.github.io/2019/08/20/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B9%9D/"/>
    <id>https://francisqiang.github.io/2019/08/20/Java并发编程学习——Java并发编程之美学习笔记九/</id>
    <published>2019-08-20T05:52:43.000Z</published>
    <updated>2019-08-20T09:07:01.368Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Java并发包中-ScheduledThreadPoolExecutor-原理探究"><a href="#Java并发包中-ScheduledThreadPoolExecutor-原理探究" class="headerlink" title="Java并发包中 ScheduledThreadPoolExecutor 原理探究"></a>Java并发包中 ScheduledThreadPoolExecutor 原理探究</h2><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>  上篇文章中有提到过 Executors ，这是一个工具类，他提供了很多静态方法返回不同的<em>线程池实例</em>。 而其中的 newScheduledThreadPool 方法就提供了成成延迟线程池的实例。其中线程池队列是DelayWorkQueue 其和 DelayQueue 一样是一个延迟队列。</p><h3 id="类图结构"><a href="#类图结构" class="headerlink" title="类图结构"></a>类图结构</h3><p>  <img src="/2019/08/20/Java并发编程学习——Java并发编程之美学习笔记九/ScheduledThreadPoolExecutor.png" alt="ScheduledThreadPoolExecutor"></p><p>  ScheduledFutureTask 是具有返回值的任务，继承自FutureTask。FutureTask 的内部有一个变量 state 用来表示任务的状态。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">int</span> state;</span><br><span class="line"><span class="comment">// 初始</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> NEW          = <span class="number">0</span>;</span><br><span class="line"><span class="comment">// 执行中</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> COMPLETING   = <span class="number">1</span>;</span><br><span class="line"><span class="comment">// 正常运行结束</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> NORMAL       = <span class="number">2</span>;</span><br><span class="line"><span class="comment">// 运行中异常</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> EXCEPTIONAL  = <span class="number">3</span>;</span><br><span class="line"><span class="comment">// 任务被取消</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> CANCELLED    = <span class="number">4</span>;</span><br><span class="line"><span class="comment">// 任务正在被中断</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INTERRUPTING = <span class="number">5</span>;</span><br><span class="line"><span class="comment">// 任务已经被中断</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> INTERRUPTED  = <span class="number">6</span>;</span><br></pre></td></tr></table></figure><p>  有可能的任务状态转换途径为:</p><p>  NEW -&gt; COMPLETING -&gt; NORMAL</p><p>  NEW -&gt; COMPLETING -&gt; EXCEPTIONAL</p><p>  NEW -&gt; CANCLLED</p><p>  NEW -&gt; INTERRUPTING -&gt; INTERRUPTED</p><p>  ScheduledFutureTask 内部还有一个变量 period 用来表示任务的类型。当 period 为0的时候说明当前任务是一次性的，period为负数的时候说明当前任务为 fixed-delay 固定延迟的定时可重复执行任务，period为整数说明当前任务为 fixed-rate 任务，是固定频率的定时可重复执行任务。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ScheduledExecutorService <span class="title">newScheduledThreadPool</span><span class="params">(intcorePoolSize)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> ScheduledThreadPoolExecutor(corePoolSize);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ScheduledExecutorService <span class="title">newScheduledThreadPool</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">int</span> corePoolSize, ThreadFactory threadFactory)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> ScheduledThreadPoolExecutor(corePoolSize, threadFactory);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 只有核心线程数目的构造函数 阻塞队列使用的是 DelayWorkQueue</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ScheduledThreadPoolExecutor</span><span class="params">(<span class="keyword">int</span> corePoolSize)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">super</span>(corePoolSize, Integer.MAX_VALUE, <span class="number">0</span>, NANOSECONDS,</span><br><span class="line">          <span class="keyword">new</span> DelayedWorkQueue());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="原理剖析"><a href="#原理剖析" class="headerlink" title="原理剖析"></a>原理剖析</h3><p>  主要讲解三个重要函数</p><ul><li>schedule(Callable&lt; V &gt; callable, long delay, TimeUnit unit)</li><li>scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)</li><li>scheduleAtFixedRate(Callable&lt; V &gt; callable, long delay, TimeUnit unit)</li></ul><h4 id="schedule-Callable-lt-V-gt-callable-long-delay-TimeUnit-unit"><a href="#schedule-Callable-lt-V-gt-callable-long-delay-TimeUnit-unit" class="headerlink" title="schedule(Callable&lt; V &gt; callable, long delay, TimeUnit unit)"></a>schedule(Callable&lt; V &gt; callable, long delay, TimeUnit unit)</h4><p>  该方法的作用是 <strong>提交一个延迟执行的任务</strong> ，任务从提交时间算起延迟单位为 unit 的delay 时间后开始执行。 提交的任务不是周期性任务， 任务只会执行一次。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> ScheduledFuture&lt;?&gt; schedule(Runnable command,</span><br><span class="line">                                   <span class="keyword">long</span> delay,</span><br><span class="line">                                   TimeUnit unit) &#123;</span><br><span class="line">    <span class="comment">// 判空</span></span><br><span class="line">    <span class="keyword">if</span> (command == <span class="keyword">null</span> || unit == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="comment">// 将任务转换为 RunnableScheduledFuture 类型</span></span><br><span class="line">    RunnableScheduledFuture&lt;?&gt; t = decorateTask(command,</span><br><span class="line">        <span class="keyword">new</span> ScheduledFutureTask&lt;Void&gt;(command, <span class="keyword">null</span>,</span><br><span class="line">                                      triggerTime(delay, unit)));</span><br><span class="line">    <span class="comment">// 添加任务到延迟队列</span></span><br><span class="line">    <span class="comment">// 因为延迟队列中的元素需要实现 元素实现是可比较的和继承Delayed并且拥有getDelay()方法</span></span><br><span class="line">    <span class="comment">// 而上面的 ScheduledFutureTask就实现了getDelay和compareTo方法</span></span><br><span class="line">    <span class="comment">// 其实最终这个t是ScheduledFutureTask类型的</span></span><br><span class="line">    delayedExecute(t);</span><br><span class="line">    <span class="keyword">return</span> t;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 我们来具体看一下 ScheduledFutureTask 相应的构造函数</span></span><br><span class="line">ScheduledFutureTask(Runnable r, V result, <span class="keyword">long</span> ns) &#123;</span><br><span class="line">    <span class="comment">// 调用了父类的构造函数 父类是FutureTask</span></span><br><span class="line">    <span class="keyword">super</span>(r, result);</span><br><span class="line">    <span class="keyword">this</span>.time = ns;</span><br><span class="line">    <span class="keyword">this</span>.period = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">this</span>.sequenceNumber = sequencer.getAndIncrement();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 这里可以想到Thread的一种创建方式</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">FutureTask</span><span class="params">(Runnable runnable, V result)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 将runnable转换为 callable</span></span><br><span class="line">    <span class="keyword">this</span>.callable = Executors.callable(runnable, result);</span><br><span class="line">    <span class="comment">// 设置当前任务状态为NEW</span></span><br><span class="line">    <span class="keyword">this</span>.state = NEW;       <span class="comment">// ensure visibility of callable</span></span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// wtf？？</span></span><br><span class="line"><span class="comment">// 貌似什么都没做 返回一个 RunnableScheduledFuture 类型的task</span></span><br><span class="line"><span class="comment">// 注意我们前面调用的时候传入的是 ScheduledFutureTask 而 ScheduledFutureTask </span></span><br><span class="line"><span class="comment">// 其实是实现了 RunnableScheduledFuture 的</span></span><br><span class="line"><span class="keyword">protected</span> &lt;V&gt; <span class="function">RunnableScheduledFuture&lt;V&gt; <span class="title">decorateTask</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">    Runnable runnable, RunnableScheduledFuture&lt;V&gt; task)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> task;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">delayedExecute</span><span class="params">(RunnableScheduledFuture&lt;?&gt; task)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判断线程池是否是 SHUTDOWN 如果是则执行拒绝策略</span></span><br><span class="line">    <span class="keyword">if</span> (isShutdown())</span><br><span class="line">        reject(task);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 获取BlockingQueue 然后添加任务到里面</span></span><br><span class="line">        <span class="comment">// DelayQueue就是实现了 BlockingQueue</span></span><br><span class="line">        <span class="keyword">super</span>.getQueue().add(task);</span><br><span class="line">        <span class="comment">// 再次获取状态</span></span><br><span class="line">        <span class="comment">// 如果是SHUTDOWN 且该任务可以运行给定当前运行状态和运行后关闭参数 那么移除task 然后取消任务</span></span><br><span class="line">        <span class="keyword">if</span> (isShutdown() &amp;&amp;</span><br><span class="line">            !canRunInCurrentRunState(task.isPeriodic()) &amp;&amp;</span><br><span class="line">            remove(task))</span><br><span class="line">            task.cancel(<span class="keyword">false</span>);</span><br><span class="line">        <span class="comment">// 不然保证至少有一个线程在处理任务</span></span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            ensurePrestart();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 注意这里是执行任务了 就如上篇文章说到的 addWorker里面会执行任务</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">ensurePrestart</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取worker数量</span></span><br><span class="line">    <span class="keyword">int</span> wc = workerCountOf(ctl.get());</span><br><span class="line">    <span class="comment">// 如果小于核心 那么添加线程</span></span><br><span class="line">    <span class="keyword">if</span> (wc &lt; corePoolSize)</span><br><span class="line">        addWorker(<span class="keyword">null</span>, <span class="keyword">true</span>);</span><br><span class="line">    <span class="comment">// 小于0也添加线程</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (wc == <span class="number">0</span>)</span><br><span class="line">        addWorker(<span class="keyword">null</span>, <span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们来看一下 ScheduledFutureTask 是如何执行任务的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 是否执行一次</span></span><br><span class="line">    <span class="keyword">boolean</span> periodic = isPeriodic();</span><br><span class="line">    <span class="comment">// 任务不可以运行给定当前运行状态和运行后关闭参数</span></span><br><span class="line">    <span class="comment">// 取消任务</span></span><br><span class="line">    <span class="keyword">if</span> (!canRunInCurrentRunState(periodic))</span><br><span class="line">        cancel(<span class="keyword">false</span>);</span><br><span class="line">    <span class="comment">// 只执行一次</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (!periodic)</span><br><span class="line">        ScheduledFutureTask.<span class="keyword">super</span>.run();</span><br><span class="line">    <span class="comment">// 定时执行</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (ScheduledFutureTask.<span class="keyword">super</span>.runAndReset()) &#123;</span><br><span class="line">        <span class="comment">// 设置time = time + period</span></span><br><span class="line">        setNextRunTime();</span><br><span class="line">        <span class="comment">// 重新加入该任务到delay队列</span></span><br><span class="line">        reExecutePeriodic(outerTask);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 父类的run FutureTask</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判断任务状态</span></span><br><span class="line">    <span class="keyword">if</span> (state != NEW ||</span><br><span class="line">        !UNSAFE.compareAndSwapObject(<span class="keyword">this</span>, runnerOffset,</span><br><span class="line">                                     <span class="keyword">null</span>, Thread.currentThread()))</span><br><span class="line">        <span class="keyword">return</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Callable&lt;V&gt; c = callable;</span><br><span class="line">        <span class="keyword">if</span> (c != <span class="keyword">null</span> &amp;&amp; state == NEW) &#123;</span><br><span class="line">            V result;</span><br><span class="line">            <span class="keyword">boolean</span> ran;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 调用call方法并返回值</span></span><br><span class="line">                result = c.call();</span><br><span class="line">                ran = <span class="keyword">true</span>;</span><br><span class="line">            &#125; <span class="keyword">catch</span> (Throwable ex) &#123;</span><br><span class="line">                result = <span class="keyword">null</span>;</span><br><span class="line">                ran = <span class="keyword">false</span>;</span><br><span class="line">                <span class="comment">// 如果任务失败 设置当前任务状态为 EXCEPTION</span></span><br><span class="line">                setException(ex);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (ran)</span><br><span class="line">                <span class="comment">// 最后设置任务状态为NORMAL</span></span><br><span class="line">                <span class="comment">// 所以这里只会执行一次 控制了任务状态</span></span><br><span class="line">                set(result);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 后续处理</span></span><br><span class="line">        runner = <span class="keyword">null</span>;</span><br><span class="line">        <span class="comment">// state must be re-read after nulling runner to prevent</span></span><br><span class="line">        <span class="comment">// leaked interrupts</span></span><br><span class="line">        <span class="keyword">int</span> s = state;</span><br><span class="line">        <span class="keyword">if</span> (s &gt;= INTERRUPTING)</span><br><span class="line">            handlePossibleCancellationInterrupt(s);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>  ScheduledThreadPoolExecutor 的实现原理是通过内部使用 DelayQueue 延迟队列存放具体任务，以达到延迟的效果。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Java并发包中-ScheduledThreadPoolExecutor-原理探究&quot;&gt;&lt;a href=&quot;#Java并发包中-ScheduledThreadPoolExecutor-原理探究&quot; class=&quot;headerlink&quot; title=&quot;Java并发包中 S
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美学习笔记八</title>
    <link href="https://francisqiang.github.io/2019/08/20/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%85%AB/"/>
    <id>https://francisqiang.github.io/2019/08/20/Java并发编程学习——Java并发编程之美学习笔记八/</id>
    <published>2019-08-20T01:07:24.000Z</published>
    <updated>2019-08-20T03:21:47.779Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Java并发包中线程池-ThreadPoolExecutor-原理探究"><a href="#Java并发包中线程池-ThreadPoolExecutor-原理探究" class="headerlink" title="Java并发包中线程池 ThreadPoolExecutor 原理探究"></a>Java并发包中线程池 ThreadPoolExecutor 原理探究</h2><h3 id="介绍"><a href="#介绍" class="headerlink" title="介绍"></a>介绍</h3><p>  线程池主要解决的两个问题: <strong><em>一是当执行大量异步任务是线程池能提供较好的性能，使线程可复用而不需要再次new，二是提供一种资源限制和管理手段，比如可以限制线程的个数，动态新增线程等。</em></strong></p><p>  另外线程池也提供了许多<em>可调参数</em>和<em>可扩展性接口</em>，以满足不同情况的需要。我们可以使用更方便的 Executors 的工厂方法去创建线程池，比如其中的 newCachedThreadPool (线程池线程个数最多科大 Integet.MAX_VALUE 线程自动回收), newFixedThreadPool (固定大小)和 newSingleThreadExecutor (单个线程)。</p><h3 id="类图介绍"><a href="#类图介绍" class="headerlink" title="类图介绍"></a>类图介绍</h3><p>  <img src="/2019/08/20/Java并发编程学习——Java并发编程之美学习笔记八/ThreadPoolExecutor.png" alt="ThreadPoolExecutor"></p><p>  ThreadPoolExecutor 继承了 AbstractExecutorService, 而 AbstractExecutorService 实现了 ExecutorService, ExecutorService 则实现了 Executor。 ThreadPoolExecutor 中有五个内部类，其中有四个是关于 policy 的，这是对于拒绝策略的实现(当线程池的状态不能再添加新任务执行的策略), 还有一个 Worker 类， 他继承了 AQS 和实现了 Runnable，里面封装了firstTask记录着第一次执行的任务，thread保存的线程，这个线程的 Runnable 是 Worker 本身，当初始化的时候会将 this 传入 thread ，当 thread 启动的时候会调用 Worker 实现的 run() 方法， 其中会调用runWorker(this),在这个方法里面就会取出 firstTask 然后调用它的 run 方法。</p><p>  这里有一个 ctl 的原子变量， 他和前面的读写锁有着异曲同工之妙，它通过一个变量记录着线程池状态和线程池线程个数，其中高三位表示线程池状态，其余表示线程池个数</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 初始化线程池状态为RUNNING 个数为0</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> AtomicInteger ctl = <span class="keyword">new</span> AtomicInteger(ctlOf(RUNNING, <span class="number">0</span>));</span><br><span class="line"><span class="comment">// 线程池线程个数</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> COUNT_BITS = Integer.SIZE - <span class="number">3</span>;</span><br><span class="line"><span class="comment">// 容量</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> CAPACITY   = (<span class="number">1</span> &lt;&lt; COUNT_BITS) - <span class="number">1</span>;</span><br><span class="line"><span class="comment">// runState is stored in the high-order bits</span></span><br><span class="line"><span class="comment">// RUNNING代表接受新任务并且处理阻塞队列的任务</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> RUNNING    = -<span class="number">1</span> &lt;&lt; COUNT_BITS;</span><br><span class="line"><span class="comment">// SHUTTDOWN表示不接受新任务但会处理阻塞队列里的任务</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SHUTDOWN   =  <span class="number">0</span> &lt;&lt; COUNT_BITS;</span><br><span class="line"><span class="comment">// STOP表示拒绝新任务并且抛弃阻塞队列里的任务 同时中断正在执行的任务</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> STOP       =  <span class="number">1</span> &lt;&lt; COUNT_BITS;</span><br><span class="line"><span class="comment">// 所有任务都执行完毕(包括阻塞队列里的) 将会调用terminated方法</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TIDYING    =  <span class="number">2</span> &lt;&lt; COUNT_BITS;</span><br><span class="line"><span class="comment">// 终止状态 terminated方法调用完成以后的状态</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> TERMINATED =  <span class="number">3</span> &lt;&lt; COUNT_BITS;</span><br></pre></td></tr></table></figure><p>  线程池参数如下</p><p>  corePoolSize: 线程池核心线程数。</p><p>  workQueue: 用于保存等待执行的任务的阻塞队列,比如基于数组的有界ArrayBlockingQueue，基于链表的无界 LinkedBlockingQueue等等等。</p><p>  maximunPooSize: 线程池最大线程数</p><p>  threadFactory: 创建线程的工厂</p><p>  RejectedExecutionHandler: 饱和拒绝策略 前面提到过</p><p>  keepAliveTime: 存活时间。如果当前线程池中的线程数量比核心线程数量多的时候，并且是闲置状态，则这些闲置的线程能存活的最大时间。</p><p>  TimeUnit: 存活时间的时间单位。</p><p>  mainLock: 独占锁， <strong><em>用来控制新增 Worker 线程操作的原子性</em></strong> 。</p><p>  termination: mainLock对应的条件队列，在线程调用 awaitTermination 时被阻塞的线程放入此队列。</p><p>  Worker: 上文提到过 是具体承载任务的对象，它继承了 AQS 实现了自己的简单不可重入独占锁，其中state = 0表示锁未被获取，为1表示锁已经被获取，为-1表示创建的默认状态，为了避免该线程在运行newWorker()方法前被中断。</p><h3 id="源码分析"><a href="#源码分析" class="headerlink" title="源码分析"></a>源码分析</h3><ul><li><p>public void execute(Runnable command)</p><p>execute 方法的作用是提交任务 command 到线程池进行执行。</p><p><img src="/2019/08/20/Java并发编程学习——Java并发编程之美学习笔记八/1.jpg" alt="execute"></p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">execute</span><span class="params">(Runnable command)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判空</span></span><br><span class="line">    <span class="keyword">if</span> (command == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="comment">// 获取线程池状态和线程数量</span></span><br><span class="line">    <span class="keyword">int</span> c = ctl.get();</span><br><span class="line">    <span class="comment">// 如果线程数量小于核心数量</span></span><br><span class="line">    <span class="keyword">if</span> (workerCountOf(c) &lt; corePoolSize) &#123;</span><br><span class="line">        <span class="comment">// 调用addWorker增加</span></span><br><span class="line">        <span class="keyword">if</span> (addWorker(command, <span class="keyword">true</span>))</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        c = ctl.get();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果是RUNNING状态则添加任务到阻塞队列</span></span><br><span class="line">    <span class="keyword">if</span> (isRunning(c) &amp;&amp; workQueue.offer(command)) &#123;</span><br><span class="line">        <span class="comment">// 再次获取</span></span><br><span class="line">        <span class="keyword">int</span> recheck = ctl.get();</span><br><span class="line">        <span class="comment">// 如果不是RUNNING则从队列中删除任务</span></span><br><span class="line">        <span class="keyword">if</span> (! isRunning(recheck) &amp;&amp; remove(command))</span><br><span class="line">            <span class="comment">// 实施拒绝策略</span></span><br><span class="line">            reject(command);</span><br><span class="line">        <span class="comment">// 如果线程数量为0增加线程</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (workerCountOf(recheck) == <span class="number">0</span>)</span><br><span class="line">            addWorker(<span class="keyword">null</span>, <span class="keyword">false</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果队列满则新增线程 失败则实行拒绝策略</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (!addWorker(command, <span class="keyword">false</span>))</span><br><span class="line">        reject(command);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">addWorker</span><span class="params">(Runnable firstTask, <span class="keyword">boolean</span> core)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 双重循环退出第一层循环的标志</span></span><br><span class="line">    retry:</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">// 获取到线程池状态和线程数量</span></span><br><span class="line">        <span class="keyword">int</span> c = ctl.get();</span><br><span class="line">        <span class="comment">// 线程池状态</span></span><br><span class="line">        <span class="keyword">int</span> rs = runStateOf(c);</span><br><span class="line">        <span class="comment">// 检查队列是否只在必要时为空</span></span><br><span class="line">        <span class="comment">// 将!展开得</span></span><br><span class="line">        <span class="comment">// rs &gt;= SHUTDOWN &amp;&amp; (rs != SHUTDOWN || firstTask != null || workQueue.isEmpty())</span></span><br><span class="line">        <span class="comment">// 以下几种情况会返回false</span></span><br><span class="line">        <span class="comment">// 当前线程池状态为STOP,TIDYING或者TERMINATED</span></span><br><span class="line">        <span class="comment">// 当前线程状态为SHUTDOWN 并且已经有了第一个任务</span></span><br><span class="line">        <span class="comment">// 当前线程状态为SHUTDOWN并且任务队列为空</span></span><br><span class="line">        <span class="keyword">if</span> (rs &gt;= SHUTDOWN &amp;&amp;</span><br><span class="line">            ! (rs == SHUTDOWN &amp;&amp;</span><br><span class="line">               firstTask == <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">               ! workQueue.isEmpty()))</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="comment">// 获取线程数量</span></span><br><span class="line">            <span class="keyword">int</span> wc = workerCountOf(c);</span><br><span class="line">            <span class="comment">// 如果线程数量大于容量或者当前添加任务不是核心的时候当前线程数量大于最大数量</span></span><br><span class="line">            <span class="comment">// 如果是核心那么大于等于核心线程数量的时候返回false</span></span><br><span class="line">            <span class="keyword">if</span> (wc &gt;= CAPACITY ||</span><br><span class="line">                wc &gt;= (core ? corePoolSize : maximumPoolSize))</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">            <span class="comment">// CAS增加线程数量</span></span><br><span class="line">            <span class="keyword">if</span> (compareAndIncrementWorkerCount(c))</span><br><span class="line">                <span class="comment">// 成功跳出整个循环</span></span><br><span class="line">                <span class="keyword">break</span> retry;</span><br><span class="line">            c = ctl.get();  <span class="comment">// Re-read ctl</span></span><br><span class="line">            <span class="comment">// 如果CAS失败查看线程池状态是否改变了</span></span><br><span class="line">            <span class="comment">// 变化则重新调到最外层循环再次获取</span></span><br><span class="line">            <span class="keyword">if</span> (runStateOf(c) != rs)</span><br><span class="line">                <span class="keyword">continue</span> retry;</span><br><span class="line">            <span class="comment">// 没有改变继续内层循环</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 到这里说明CAS成功</span></span><br><span class="line">    <span class="keyword">boolean</span> workerStarted = <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">boolean</span> workerAdded = <span class="keyword">false</span>;</span><br><span class="line">    Worker w = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 创建worker 这时候会使用工厂来创建thread 但不一定能创建成功</span></span><br><span class="line">        w = <span class="keyword">new</span> Worker(firstTask);</span><br><span class="line">        <span class="keyword">final</span> Thread t = w.thread;</span><br><span class="line">        <span class="comment">// 如果thread不为空</span></span><br><span class="line">        <span class="keyword">if</span> (t != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 获取到独占锁</span></span><br><span class="line">            <span class="keyword">final</span> ReentrantLock mainLock = <span class="keyword">this</span>.mainLock;</span><br><span class="line">            mainLock.lock();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 再次获取状态 避免前面操作的时候线程池状态改变了</span></span><br><span class="line">                <span class="keyword">int</span> rs = runStateOf(ctl.get());</span><br><span class="line">                <span class="comment">// 如果线程池状态为RUNNING 或者为 SHUTDOWN 并且第一个任务为空</span></span><br><span class="line">                <span class="keyword">if</span> (rs &lt; SHUTDOWN ||</span><br><span class="line">                    (rs == SHUTDOWN &amp;&amp; firstTask == <span class="keyword">null</span>)) &#123;</span><br><span class="line">                    <span class="comment">// 提前检查线程是否已经开始运行 是则抛出线程状态异常</span></span><br><span class="line">                    <span class="keyword">if</span> (t.isAlive()) <span class="comment">// precheck that t is startable</span></span><br><span class="line">                        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalThreadStateException();</span><br><span class="line">                    <span class="comment">// 新增到工作集中</span></span><br><span class="line">                    workers.add(w);</span><br><span class="line">                    <span class="keyword">int</span> s = workers.size();</span><br><span class="line">                    <span class="keyword">if</span> (s &gt; largestPoolSize)</span><br><span class="line">                        largestPoolSize = s;</span><br><span class="line">                    workerAdded = <span class="keyword">true</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                mainLock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (workerAdded) &#123;</span><br><span class="line">                <span class="comment">// 新增成功启动线程</span></span><br><span class="line">                t.start();</span><br><span class="line">                workerStarted = <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (! workerStarted)</span><br><span class="line">            addWorkerFailed(w);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> workerStarted;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>工作线程worker的执行</p><p>当用户线程添加到线程池后，由worker来执行，先看下Worker的构造函数。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><span class="line">Worker(Runnable firstTask) &#123;</span><br><span class="line">    <span class="comment">// 设置worker状态为-1 避免当前Worker在调用runWorker之前被中断</span></span><br><span class="line">    <span class="comment">// 可能其他线程调用了shutdownNow时 worker因为</span></span><br><span class="line">    <span class="comment">// state &gt;= 0而导致此时被中断</span></span><br><span class="line">    setState(-<span class="number">1</span>); <span class="comment">// inhibit interrupts until runWorker</span></span><br><span class="line">    <span class="keyword">this</span>.firstTask = firstTask;</span><br><span class="line">    <span class="comment">// 从工厂中获取线程 此线程会将this作为Runnable参数 因为Worker实现了饿Runnable接口</span></span><br><span class="line">    <span class="keyword">this</span>.thread = getThreadFactory().newThread(<span class="keyword">this</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 我们再来看一下runWorker()</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">runWorker</span><span class="params">(Worker w)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取当前线程</span></span><br><span class="line">    Thread wt = Thread.currentThread();</span><br><span class="line">    <span class="comment">// 获取worker中的第一个任务</span></span><br><span class="line">    Runnable task = w.firstTask;</span><br><span class="line">    <span class="comment">// 置为空</span></span><br><span class="line">    w.firstTask = <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 解锁 其中会把state变为 0</span></span><br><span class="line">    w.unlock(); <span class="comment">// allow interrupts</span></span><br><span class="line">    <span class="keyword">boolean</span> completedAbruptly = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (task != <span class="keyword">null</span> || (task = getTask()) != <span class="keyword">null</span>) &#123;</span><br><span class="line">            w.lock();</span><br><span class="line">            <span class="comment">// 如果池停止，则确保线程被中断；如果没有，则确保线程不中断</span></span><br><span class="line">            <span class="keyword">if</span> ((runStateAtLeast(ctl.get(), STOP) ||</span><br><span class="line">                 (Thread.interrupted() &amp;&amp;</span><br><span class="line">                  runStateAtLeast(ctl.get(), STOP))) &amp;&amp;</span><br><span class="line">                !wt.isInterrupted())</span><br><span class="line">                wt.interrupt();</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">// 执行任务前做一些事情</span></span><br><span class="line">                beforeExecute(wt, task);</span><br><span class="line">                Throwable thrown = <span class="keyword">null</span>;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 执行</span></span><br><span class="line">                    task.run();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (RuntimeException x) &#123;</span><br><span class="line">                    thrown = x; <span class="keyword">throw</span> x;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Error x) &#123;</span><br><span class="line">                    thrown = x; <span class="keyword">throw</span> x;</span><br><span class="line">                &#125; <span class="keyword">catch</span> (Throwable x) &#123;</span><br><span class="line">                    thrown = x; <span class="keyword">throw</span> <span class="keyword">new</span> Error(x);</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    <span class="comment">// 执行完成做一些清理工作</span></span><br><span class="line">                    afterExecute(task, thrown);</span><br><span class="line">                &#125;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                <span class="comment">// 释放</span></span><br><span class="line">                task = <span class="keyword">null</span>;</span><br><span class="line">                <span class="comment">// 完成任务++</span></span><br><span class="line">                w.completedTasks++;</span><br><span class="line">                w.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        completedAbruptly = <span class="keyword">false</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 清理工作</span></span><br><span class="line">        processWorkerExit(w, completedAbruptly);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>shutdown 操作</p><p>调用shutdown方法后 线程池就不会接受新的任务了，但是工作队里里的任务还要执行的，该方法立即返回 不等待队列任务完成再返回。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">shutdown</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock mainLock = <span class="keyword">this</span>.mainLock;</span><br><span class="line">    mainLock.lock();</span><br><span class="line">    <span class="comment">// 获取独占锁</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 检查权限</span></span><br><span class="line">        checkShutdownAccess();</span><br><span class="line">        <span class="comment">// 提前设置状态</span></span><br><span class="line">        advanceRunState(SHUTDOWN);</span><br><span class="line">        <span class="comment">// 给所有空闲线程设置中断标志</span></span><br><span class="line">        interruptIdleWorkers();</span><br><span class="line">        onShutdown(); <span class="comment">// hook for ScheduledThreadPoolExecutor</span></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        mainLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    tryTerminate();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">interruptIdleWorkers</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    interruptIdleWorkers(<span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">interruptIdleWorkers</span><span class="params">(<span class="keyword">boolean</span> onlyOne)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock mainLock = <span class="keyword">this</span>.mainLock;</span><br><span class="line">    mainLock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (Worker w : workers) &#123;</span><br><span class="line">            Thread t = w.thread;</span><br><span class="line">            <span class="comment">// 如果线程没被中断 则尝试获取worker的锁</span></span><br><span class="line">            <span class="keyword">if</span> (!t.isInterrupted() &amp;&amp; w.tryLock()) &#123;</span><br><span class="line">                <span class="keyword">try</span> &#123;</span><br><span class="line">                    <span class="comment">// 中断</span></span><br><span class="line">                    t.interrupt();</span><br><span class="line">                &#125; <span class="keyword">catch</span> (SecurityException ignore) &#123;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    w.unlock();</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果打断一个 那么跳出循环 不然中断所有workers里的线程 (工作集合)</span></span><br><span class="line">            <span class="keyword">if</span> (onlyOne)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        mainLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>  线程池巧妙地使用了Integer类型表示线程池状态和线程数量，在整个线程池中主要的操作其实就是execute，当执行的时候会判断线程池状态 线程数量等因素。如果不满足那么会将任务(Runnable)加入阻塞队列 ，如果成功那么会加入工作集中并且执行。整个线程池能做到线程复用主要是吧Runnable和Thread分开来了，如果线程存活的时候且任务为空，当一个任务进来的时候该线程就能执行该任务。这样就减少了一些不必要的开销。当然还有很多线程池管理的细节，这里就不细说了。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Java并发包中线程池-ThreadPoolExecutor-原理探究&quot;&gt;&lt;a href=&quot;#Java并发包中线程池-ThreadPoolExecutor-原理探究&quot; class=&quot;headerlink&quot; title=&quot;Java并发包中线程池 ThreadPool
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美学习笔记七</title>
    <link href="https://francisqiang.github.io/2019/08/18/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%B8%83/"/>
    <id>https://francisqiang.github.io/2019/08/18/Java并发编程学习——Java并发编程之美学习笔记七/</id>
    <published>2019-08-18T03:08:58.000Z</published>
    <updated>2019-08-18T12:18:20.657Z</updated>
    
    <content type="html"><![CDATA[<h2 id="ConcurrentLinkedQueue-原理探究"><a href="#ConcurrentLinkedQueue-原理探究" class="headerlink" title="ConcurrentLinkedQueue 原理探究"></a>ConcurrentLinkedQueue 原理探究</h2><h3 id="ConcurrentLinkedQueue-类图结构"><a href="#ConcurrentLinkedQueue-类图结构" class="headerlink" title="ConcurrentLinkedQueue 类图结构"></a>ConcurrentLinkedQueue 类图结构</h3><p>  <img src="/2019/08/18/Java并发编程学习——Java并发编程之美学习笔记七/ConcurrentLinkedQueue.png" alt="ConcurrentLinkedQueue"></p><p>  ConcurrentLinedQueue 内部使用<em>单向链表</em>的方式实现，其中有两个<em>volatile</em>类型的Node节点分别用来存放队列的首尾节点。</p><p>  Node节点里面维护了一个使用 volatile 修饰的变量item，用来存放节点的值，next用来存放下一个节点，其内部使用 Unsafe 工具类提供的 CAS 算法来保证入队时操作链表的原子性。</p><p>  从无参构造函数来看，默认头尾节点指向item为null的哨兵节点。ConcurrentLinkedQueue提供了一个传递Collection的有参构造函数，它会把Collection中的item封装成Node然后存放在队列中。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ConcurrentLinkedQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    head = tail = <span class="keyword">new</span> Node&lt;E&gt;(<span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ConcurrentLinkedQueue</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    Node&lt;E&gt; h = <span class="keyword">null</span>, t = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">for</span> (E e : c) &#123;</span><br><span class="line">        checkNotNull(e);</span><br><span class="line">        Node&lt;E&gt; newNode = <span class="keyword">new</span> Node&lt;E&gt;(e);</span><br><span class="line">        <span class="keyword">if</span> (h == <span class="keyword">null</span>)</span><br><span class="line">            h = t = newNode;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            t.lazySetNext(newNode);</span><br><span class="line">            t = newNode;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (h == <span class="keyword">null</span>)</span><br><span class="line">        h = t = <span class="keyword">new</span> Node&lt;E&gt;(<span class="keyword">null</span>);</span><br><span class="line">    head = h;</span><br><span class="line">    tail = t;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ConcurrentLinkedQueue-原理介绍"><a href="#ConcurrentLinkedQueue-原理介绍" class="headerlink" title="ConcurrentLinkedQueue 原理介绍"></a>ConcurrentLinkedQueue 原理介绍</h3><p>  ConcurrentLinkedQueue的原理很复杂，因为它是通过CAS实现来实现线程安全的 并且是无锁的 所以会考虑到很多种情况，这里只对代码和方法作用作简单的解析，如果想深入原理可以参考Java并发编程之美的第七章。</p><ul><li><p>offer 操作</p><p>offer(E e) 是在队列末尾添加一个元素。 <strong><em>由于 ConcurrentLinkedQueue 是无界队列，所以该方法一直会返回true。 另外使用的是 CAS 无阻塞算法， 因此该方法不会阻塞挂起调用的线程。</em></strong></p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">offer</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判断是否为null 如果为null抛出 NPE 异常</span></span><br><span class="line">    checkNotNull(e);</span><br><span class="line">    <span class="comment">// 封装成Node</span></span><br><span class="line">    <span class="keyword">final</span> Node&lt;E&gt; newNode = <span class="keyword">new</span> Node&lt;E&gt;(e);</span><br><span class="line">    <span class="comment">// 通过循环加入队列末尾</span></span><br><span class="line">    <span class="keyword">for</span> (Node&lt;E&gt; t = tail, p = t;;) &#123;</span><br><span class="line">        Node&lt;E&gt; q = p.next;</span><br><span class="line">        <span class="keyword">if</span> (q == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">// 如果 p 的next为null 代表p为最后一个</span></span><br><span class="line">            <span class="comment">// 执行插入</span></span><br><span class="line">            <span class="keyword">if</span> (p.casNext(<span class="keyword">null</span>, newNode)) &#123;</span><br><span class="line">                <span class="keyword">if</span> (p != t)</span><br><span class="line">                    casTail(t, newNode);  </span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 多线程操作的时候由于 poll操作可能会把head变成自引用</span></span><br><span class="line">        <span class="keyword">else</span> <span class="keyword">if</span> (p == q)</span><br><span class="line">            <span class="comment">// 需要重新设置head节点</span></span><br><span class="line">            p = (t != (t = tail)) ? t : head;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="comment">// 不然重新寻找尾结点</span></span><br><span class="line">            p = (p != t &amp;&amp; t != (t = tail)) ? t : q;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  对于入队操作，offer会考虑到很多中情况，比如当进行到某些操作另一个线程调用了offer或者poll方法导致队列变化，正如上面代码中无限循环中的判断语句，其中的判断语句就是为了发生的多线程并发情况做准备，而整个offer(E e)方法最重要的就是使用CAS来控制某一时刻只有一个线程可以追加元素到队列末尾，而CAS失败的线程会通过无限循环判断队列的变化来再次进行CAS尝试，也就是说offer通过 <strong><em>无限循环不断进行 CAS 尝试方式来代替阻塞算法挂起调用线程</em></strong> 。 相比阻塞算法，这就是 <strong><em>使用 CPU 资源换取则色所带来的的开销</em></strong> 。</p><ul><li><p>poll 操作</p><p>poll() 是在队列头部获取并移除元素，如果队列为空则返回null。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">poll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// goto标记</span></span><br><span class="line">    restartFromHead:</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="keyword">for</span> (Node&lt;E&gt; h = head, p = h, q;;) &#123;</span><br><span class="line">            E item = p.item;</span><br><span class="line">            <span class="comment">// 当前节点有值则通过CAS设置为null</span></span><br><span class="line">            <span class="keyword">if</span> (item != <span class="keyword">null</span> &amp;&amp; p.casItem(item, <span class="keyword">null</span>)) &#123;</span><br><span class="line">                <span class="comment">// 更新头结点</span></span><br><span class="line">                <span class="keyword">if</span> (p != h)</span><br><span class="line">                    updateHead(h, ((q = p.next) != <span class="keyword">null</span>) ? q : p);</span><br><span class="line">                <span class="keyword">return</span> item;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 当前队列为空则返回null</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> ((q = p.next) == <span class="keyword">null</span>) &#123;</span><br><span class="line">                updateHead(h, p);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果当前节点被自引用了 则跳出循环在进入循环寻找新的头结点</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (p == q)</span><br><span class="line">                <span class="keyword">continue</span> restartFromHead;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                p = q;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  可以看到的是，poll操作也考虑到了很多并发情况并做出处理，不得不说 Goug Lee 真的是一个天才，poll操作也是采用无限循环CAS代替阻塞算法的。</p><ul><li><p>peek 操作</p><p>peek() 是获取头部的一个元素但是不移除。和poll差不多 只不过这里不使用CAS来移除。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">peek</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    restartFromHead:</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="keyword">for</span> (Node&lt;E&gt; h = head, p = h, q;;) &#123;</span><br><span class="line">            E item = p.item;</span><br><span class="line">            <span class="keyword">if</span> (item != <span class="keyword">null</span> || (q = p.next) == <span class="keyword">null</span>) &#123;</span><br><span class="line">                updateHead(h, p);</span><br><span class="line">                <span class="keyword">return</span> item;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (p == q)</span><br><span class="line">                <span class="keyword">continue</span> restartFromHead;</span><br><span class="line">            <span class="keyword">else</span></span><br><span class="line">                p = q;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>size 操作</p><p>size() 是获取队列元素个数，采用的是遍历队列并自增count, 由于这里没有添加任何的锁所以在进行size操作的时候可能会有其他线程进行poll remove offer等操作，所以size并不是一个精确的值。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">for</span> (Node&lt;E&gt; p = first(); p != <span class="keyword">null</span>; p = succ(p))</span><br><span class="line">        <span class="keyword">if</span> (p.item != <span class="keyword">null</span>)</span><br><span class="line">            <span class="comment">// Collection.size() spec says to max out</span></span><br><span class="line">            <span class="keyword">if</span> (++count == Integer.MAX_VALUE)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">    <span class="keyword">return</span> count;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>remove 操作</p><p>remove(Object object) 是删除指定元素,如果存在多个则删除第一个，如果队列为空则返回 false 。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">remove</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (o != <span class="keyword">null</span>) &#123;</span><br><span class="line">        Node&lt;E&gt; next, pred = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">for</span> (Node&lt;E&gt; p = first(); p != <span class="keyword">null</span>; pred = p, p = next) &#123;</span><br><span class="line">            <span class="keyword">boolean</span> removed = <span class="keyword">false</span>;</span><br><span class="line">            E item = p.item;</span><br><span class="line">            <span class="keyword">if</span> (item != <span class="keyword">null</span>) &#123;</span><br><span class="line">                <span class="keyword">if</span> (!o.equals(item)) &#123;</span><br><span class="line">                    next = succ(p);</span><br><span class="line">                    <span class="keyword">continue</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                removed = p.casItem(item, <span class="keyword">null</span>);</span><br><span class="line">            &#125;</span><br><span class="line">            next = succ(p);</span><br><span class="line">            <span class="keyword">if</span> (pred != <span class="keyword">null</span> &amp;&amp; next != <span class="keyword">null</span>) <span class="comment">// unlink</span></span><br><span class="line">                pred.casNext(p, next);</span><br><span class="line">            <span class="keyword">if</span> (removed)</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>contains 操作</p><p>contains 是判断队列中是否含有指定元素，由于是遍历整个队列并且没有加锁，所以该操作也像size一样不是精确的。比如当调用该方法的时候该元素还在队列中，但是在遍历的时候其他线程调用了remove移除该元素，那么就会返回false。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">contains</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (o == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">for</span> (Node&lt;E&gt; p = first(); p != <span class="keyword">null</span>; p = succ(p)) &#123;</span><br><span class="line">        E item = p.item;</span><br><span class="line">        <span class="keyword">if</span> (item != <span class="keyword">null</span> &amp;&amp; o.equals(item))</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ConcurrentLinkedQueue-小结"><a href="#ConcurrentLinkedQueue-小结" class="headerlink" title="ConcurrentLinkedQueue 小结"></a>ConcurrentLinkedQueue 小结</h3><p>  ConcurrentLinkedQueue 底层是使用<em>单向链表</em>来保存队列元素的，每个元素包装成<em>Node</em>节点，队列是靠头尾节点来维护的，创建队列的时候头尾节点指向一个item为null的哨兵节点。第一次执行 peek 或者 first 操作的时候会把 head 指向第一个真正的队列元素。由于使用非阻塞 CAS 算法，在进行size contains操作的时候可能其他线程同时进行了offer remove等操作导致size contains不精确。</p><p>  入队，出队都是操作使用 <strong>volatile</strong> 修饰的 tail， head节点。要保证在多线程情况下入队线程安全，只要保证两个Node操作的 <em>可见性 和 原子性</em>，由于volatile已经保证了可见性，所以只需要保证原子性就行。</p><p>  而offer采用CAS来保证两个线程同时调用CAS进行设置的时候只有一个会成功，失败的线程会无限进行CAS尝试直到成功，这样就保证了两个Node节点操作的原子性，而poll方法也是类似的原理。</p><h2 id="LinkedBlockingQueue-原理探究"><a href="#LinkedBlockingQueue-原理探究" class="headerlink" title="LinkedBlockingQueue 原理探究"></a>LinkedBlockingQueue 原理探究</h2><h3 id="LinkedBlockingQueue-类图结构"><a href="#LinkedBlockingQueue-类图结构" class="headerlink" title="LinkedBlockingQueue 类图结构"></a>LinkedBlockingQueue 类图结构</h3><p>  <img src="/2019/08/18/Java并发编程学习——Java并发编程之美学习笔记七/LinkedBlockingQueue.png" alt="LinkedBlockingQueue"></p><p>  LinkedBlockingQueue 也是通过单向链表来实现的。last 和 head 是用来存放尾结点和头结点的，而原子变量 count 用来记录队列中的元素个数。另外还有两个 ReentrantLock 实例 takeLock 和 putLock，其中takeLock是线程进行获取元素的锁，putLock是线程进行添加元素的锁。</p><p>  另外还有两个条件变量 notEmpty 和 notFull。 它们内部都已一个条件队列，其实这就是一个 <strong><em>生产者——消费者模型</em></strong> 。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/** Lock held by take, poll, etc */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ReentrantLock takeLock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"><span class="comment">/** Wait queue for waiting takes */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Condition notEmpty = takeLock.newCondition();</span><br><span class="line"><span class="comment">/** Lock held by put, offer, etc */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> ReentrantLock putLock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"><span class="comment">/** Wait queue for waiting puts */</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">final</span> Condition notFull = putLock.newCondition();</span><br></pre></td></tr></table></figure><p>  当线程在 LinkedBlockingQueue 实例上执行 take，poll等操作的时候需要获取takeLock，从而保证同一时刻只有一个线程能操作链表头结点。另外由于条件变量 notEmpty 内部的条件队列的维护使用的是takeLock的锁状态管理机制， 所以在调用notEmpty的await和 signal方法前线程必须获得takeLock。当获得takeLock的线程调用了notEmpty的await方法时会被阻塞释放锁并放入相应的条件队列中，只有其他线程调用了notEmpty.signal方法的时候才能被唤醒重新竞争。</p><p>  和上述一样，当线程在 LinkedBlockingQueue 实例上执行 put offer等操作的时候需要获取putLock，调用notFull的await和 signal 方法前线程也需要获取putLock等。二者相似，这里不再赘述。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// LinkedBlockingQueue 是一个有界队列 需要制定capacity</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedBlockingQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(Integer.MAX_VALUE);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedBlockingQueue</span><span class="params">(<span class="keyword">int</span> capacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (capacity &lt;= <span class="number">0</span>) <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">    <span class="keyword">this</span>.capacity = capacity;</span><br><span class="line">    last = head = <span class="keyword">new</span> Node&lt;E&gt;(<span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">LinkedBlockingQueue</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 首先调用默认狗仔方法</span></span><br><span class="line">    <span class="keyword">this</span>(Integer.MAX_VALUE);</span><br><span class="line">    <span class="comment">// 因为要存入元素 所以要事先获取putLock</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock putLock = <span class="keyword">this</span>.putLock;</span><br><span class="line">    putLock.lock(); <span class="comment">// Never contended, but necessary for visibility</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">int</span> n = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">for</span> (E e : c) &#123;</span><br><span class="line">            <span class="keyword">if</span> (e == <span class="keyword">null</span>)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">            <span class="keyword">if</span> (n == capacity)</span><br><span class="line">                <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Queue full"</span>);</span><br><span class="line">            enqueue(<span class="keyword">new</span> Node&lt;E&gt;(e));</span><br><span class="line">            ++n;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 设置大小</span></span><br><span class="line">        count.set(n);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        putLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="LinkedBlockingQueue-原理介绍"><a href="#LinkedBlockingQueue-原理介绍" class="headerlink" title="LinkedBlockingQueue 原理介绍"></a>LinkedBlockingQueue 原理介绍</h3><ul><li><p>offer 操作</p><p>向队列尾部插入一个元素，如果队列中有空闲则插入成功返回true，如果队列已满则丢弃当前元素返回false，如果元素为null 则抛出NPE，另外该方法是非阻塞的。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">offer</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判空</span></span><br><span class="line">    <span class="keyword">if</span> (e == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="comment">// 获取count</span></span><br><span class="line">    <span class="keyword">final</span> AtomicInteger count = <span class="keyword">this</span>.count;</span><br><span class="line">    <span class="comment">// 如果队列已满</span></span><br><span class="line">    <span class="keyword">if</span> (count.get() == capacity)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    <span class="keyword">int</span> c = -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 封装成Node</span></span><br><span class="line">    Node&lt;E&gt; node = <span class="keyword">new</span> Node&lt;E&gt;(e);</span><br><span class="line">    <span class="comment">// 获取putLock</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock putLock = <span class="keyword">this</span>.putLock;</span><br><span class="line">    putLock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 为什么获取锁还要再次判断？</span></span><br><span class="line">        <span class="comment">// 因为有可能出现两个线程同时进入方法然后同时要获取锁</span></span><br><span class="line">        <span class="comment">// 比如线程A首先获取锁然后添加元素导致队列已满</span></span><br><span class="line">        <span class="comment">// 线程B原本以判断过队列是否已满 那时是未满 但这次进去就满了</span></span><br><span class="line">        <span class="comment">// 所以还要判断一次</span></span><br><span class="line">        <span class="comment">// 如果队列未满</span></span><br><span class="line">        <span class="keyword">if</span> (count.get() &lt; capacity) &#123;</span><br><span class="line">            <span class="comment">// 入队</span></span><br><span class="line">            enqueue(node);</span><br><span class="line">            <span class="comment">// 增加</span></span><br><span class="line">            c = count.getAndIncrement();</span><br><span class="line">            <span class="comment">// 如果队列还有空闲位置</span></span><br><span class="line">            <span class="keyword">if</span> (c + <span class="number">1</span> &lt; capacity)</span><br><span class="line">                notFull.signal();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        putLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 这个时候count肯定是大于0的 因为前面做了自增</span></span><br><span class="line">    <span class="comment">// 这个时候则通知因为队列空而阻塞的线程</span></span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>)</span><br><span class="line">        signalNotEmpty();</span><br><span class="line">    <span class="keyword">return</span> c &gt;= <span class="number">0</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>put 操作</p><p>向队列尾部插入一个元素，如果队列有空闲则插入成功后返回，如果队列已满则<em>阻塞当前线程</em>，知道队列有空闲插入成功后返回。<em>如果在阻塞过程中被其他线程设置了中断标志，则被阻塞线程会抛出InterruptException而返回</em>。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="comment">// 判空</span></span><br><span class="line">    <span class="keyword">if</span> (e == <span class="keyword">null</span>) <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="keyword">int</span> c = -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 封装Node</span></span><br><span class="line">    Node&lt;E&gt; node = <span class="keyword">new</span> Node&lt;E&gt;(e);</span><br><span class="line">    <span class="comment">// 获取锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock putLock = <span class="keyword">this</span>.putLock;</span><br><span class="line">    <span class="keyword">final</span> AtomicInteger count = <span class="keyword">this</span>.count;</span><br><span class="line">    <span class="comment">// 这里调用了lockInterruptibly 这是能够响应中断的关键</span></span><br><span class="line">    putLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 放置虚假唤醒</span></span><br><span class="line">        <span class="comment">// 如果队列满</span></span><br><span class="line">        <span class="keyword">while</span> (count.get() == capacity) &#123;</span><br><span class="line">            notFull.await();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 不然入队</span></span><br><span class="line">        enqueue(node);</span><br><span class="line">        c = count.getAndIncrement();</span><br><span class="line">        <span class="comment">// 队列还有空闲位置 通知因为队列满而被阻塞的线程</span></span><br><span class="line">        <span class="keyword">if</span> (c + <span class="number">1</span> &lt; capacity)</span><br><span class="line">            notFull.signal();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        putLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 此时count大于0 通知因为空而被阻塞的线程</span></span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>)</span><br><span class="line">        signalNotEmpty();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>poll 操作</p><p>从队列头移除一个元素，如果队列为空则返回null，该方法是非阻塞的。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">poll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> AtomicInteger count = <span class="keyword">this</span>.count;</span><br><span class="line">    <span class="comment">// 判断队列是否为空</span></span><br><span class="line">    <span class="keyword">if</span> (count.get() == <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    E x = <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">int</span> c = -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 获取takeLock</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock takeLock = <span class="keyword">this</span>.takeLock;</span><br><span class="line">    takeLock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 如果队列不为空则出队</span></span><br><span class="line">        <span class="keyword">if</span> (count.get() &gt; <span class="number">0</span>) &#123;</span><br><span class="line">            x = dequeue();</span><br><span class="line">            c = count.getAndDecrement();</span><br><span class="line">            <span class="comment">// 如果出队之后还有元素 通知因为队列空而被阻塞的队列</span></span><br><span class="line">            <span class="keyword">if</span> (c &gt; <span class="number">1</span>)</span><br><span class="line">                notEmpty.signal();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 释放锁</span></span><br><span class="line">        takeLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 此时队列肯定有空闲位置</span></span><br><span class="line">    <span class="keyword">if</span> (c == capacity)</span><br><span class="line">        signalNotFull();</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>peek 操作</p><p>获取队列头元素但是不溢出，如果队列为空则返回null，该方法是非阻塞的。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">peek</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (count.get() == <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 获取take锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock takeLock = <span class="keyword">this</span>.takeLock;</span><br><span class="line">    takeLock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        Node&lt;E&gt; first = head.next;</span><br><span class="line">        <span class="keyword">if</span> (first == <span class="keyword">null</span>)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="keyword">return</span> first.item;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        takeLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>take 操作</p><p>获取当前队列头部元素并且移除，如果队列为空则阻塞知道队列不为空然后返回元素，如果阻塞过程被其他线程设置了中断标志则抛出InterruptedException然后返回。和前面的 put 操作正好相反。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    E x;</span><br><span class="line">    <span class="keyword">int</span> c = -<span class="number">1</span>;</span><br><span class="line">    <span class="keyword">final</span> AtomicInteger count = <span class="keyword">this</span>.count;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock takeLock = <span class="keyword">this</span>.takeLock;</span><br><span class="line">    <span class="comment">// 抛出InterruptedException的关键</span></span><br><span class="line">    takeLock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (count.get() == <span class="number">0</span>) &#123;</span><br><span class="line">            notEmpty.await();</span><br><span class="line">        &#125;</span><br><span class="line">        x = dequeue();</span><br><span class="line">        c = count.getAndDecrement();</span><br><span class="line">        <span class="keyword">if</span> (c &gt; <span class="number">1</span>)</span><br><span class="line">            notEmpty.signal();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        takeLock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (c == capacity)</span><br><span class="line">        signalNotFull();</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>remove 操作</p><p>删除队列中指定元素 有就删除并返回true，没有则返回false</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">remove</span><span class="params">(Object o)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 为空则直接false</span></span><br><span class="line">    <span class="keyword">if</span> (o == <span class="keyword">null</span>) <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    <span class="comment">// 获取双重锁 因为这里要先获取然后移除</span></span><br><span class="line">    fullyLock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 遍历链表</span></span><br><span class="line">        <span class="keyword">for</span> (Node&lt;E&gt; trail = head, p = trail.next;</span><br><span class="line">             p != <span class="keyword">null</span>;</span><br><span class="line">             trail = p, p = p.next) &#123;</span><br><span class="line">            <span class="keyword">if</span> (o.equals(p.item)) &#123;</span><br><span class="line">                <span class="comment">// 移除</span></span><br><span class="line">                unlink(p, trail);</span><br><span class="line">                <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 释放双重锁</span></span><br><span class="line">        fullyUnlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 注意这里双重锁的获取和释放的顺序是颠倒的</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">fullyLock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    putLock.lock();</span><br><span class="line">    takeLock.lock();</span><br><span class="line">&#125;</span><br><span class="line">    <span class="function"><span class="keyword">void</span> <span class="title">fullyUnlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    takeLock.unlock();</span><br><span class="line">    putLock.unlock();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>size 操作</p><p>获取当前队列元素个数</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 直接返回count 因为count原子变量一直被维护着。</span></span><br><span class="line">    <span class="keyword">return</span> count.get();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="LinkedBlockingQueue-小结"><a href="#LinkedBlockingQueue-小结" class="headerlink" title="LinkedBlockingQueue 小结"></a>LinkedBlockingQueue 小结</h3><p>  LinkedBlockingQueue 的内部通过单向链表来实现的， 对头尾节点的操作分别使用了不同的独占锁，每个独占锁对应相应的Condition和条件队列，其实实现的是一个生产消费模型。</p><h2 id="ArrayBlockingQueue-原理探究"><a href="#ArrayBlockingQueue-原理探究" class="headerlink" title="ArrayBlockingQueue 原理探究"></a>ArrayBlockingQueue 原理探究</h2><h3 id="ArrayBlockingQueue-类图结构"><a href="#ArrayBlockingQueue-类图结构" class="headerlink" title="ArrayBlockingQueue 类图结构"></a>ArrayBlockingQueue 类图结构</h3><p>  <img src="/2019/08/18/Java并发编程学习——Java并发编程之美学习笔记七/ArrayBlockingQueue.png" alt="ArrayBlockingQueue"></p><p>  我们可以看出，ArrayBlockingQueue 内部有个数组items用来存放队列元素，putIndex 用来表示入队元素下标， takeIndex 用来表示出队元素下标， count 统计队列元素个数。这些变量都没有使用volatile 因为访问这些变量都是在锁块内，而锁已经保证了可见性。另外有一个独占锁lock用来保证出队入队的操作的原子性，这保证了同时只有一个线程能进行入队，出队操作，另外，notEmpty，notFull条件变量用来进行出队入队的同步。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 有界队列需要传入容量大小</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayBlockingQueue</span><span class="params">(<span class="keyword">int</span> capacity)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 默认非公平锁</span></span><br><span class="line">    <span class="keyword">this</span>(capacity, <span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayBlockingQueue</span><span class="params">(<span class="keyword">int</span> capacity, <span class="keyword">boolean</span> fair)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (capacity &lt;= <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">    <span class="keyword">this</span>.items = <span class="keyword">new</span> Object[capacity];</span><br><span class="line">    <span class="comment">// 同时生成锁和条件</span></span><br><span class="line">    lock = <span class="keyword">new</span> ReentrantLock(fair);</span><br><span class="line">    notEmpty = lock.newCondition();</span><br><span class="line">    notFull =  lock.newCondition();</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ArrayBlockingQueue</span><span class="params">(<span class="keyword">int</span> capacity, <span class="keyword">boolean</span> fair,</span></span></span><br><span class="line"><span class="function"><span class="params">                          Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 首先构建</span></span><br><span class="line">    <span class="keyword">this</span>(capacity, fair);</span><br><span class="line">    <span class="comment">// 获取锁然后将集合中的元素放入数组中</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock(); <span class="comment">// Lock only for visibility, not mutual exclusion</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">int</span> i = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">for</span> (E e : c) &#123;</span><br><span class="line">                checkNotNull(e);</span><br><span class="line">                items[i++] = e;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125; <span class="keyword">catch</span> (ArrayIndexOutOfBoundsException ex) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">// 维持count</span></span><br><span class="line">        count = i;</span><br><span class="line">        <span class="comment">// 如果个数已经到了容量那么设置putIndex为0</span></span><br><span class="line">        putIndex = (i == capacity) ? <span class="number">0</span> : i;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 释放锁</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ArrayBlockingQueue-原理介绍"><a href="#ArrayBlockingQueue-原理介绍" class="headerlink" title="ArrayBlockingQueue 原理介绍"></a>ArrayBlockingQueue 原理介绍</h3><ul><li><p>offer 操作</p><p>向队列尾部插入一个元素，如果队列有空闲空间则插入返回true，如果已满则丢弃并返回false，该方法是非阻塞的。作用和LinkedBlockingQueue中的offer方法基本一样。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">offer</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判空</span></span><br><span class="line">    checkNotNull(e);</span><br><span class="line">    <span class="comment">// 获取独占锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 如果已满则丢弃返回false</span></span><br><span class="line">        <span class="keyword">if</span> (count == items.length)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 入队</span></span><br><span class="line">            enqueue(e);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 释放锁</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">enqueue</span><span class="params">(E x)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// assert lock.getHoldCount() == 1;</span></span><br><span class="line">    <span class="comment">// assert items[putIndex] == null;</span></span><br><span class="line">    <span class="comment">// 获取items数组</span></span><br><span class="line">    <span class="keyword">final</span> Object[] items = <span class="keyword">this</span>.items;</span><br><span class="line">    <span class="comment">// 存入入队索引对应的空间</span></span><br><span class="line">    items[putIndex] = x;</span><br><span class="line">    <span class="comment">// 如果队列满 则将putIndex置为0</span></span><br><span class="line">    <span class="keyword">if</span> (++putIndex == items.length)</span><br><span class="line">        putIndex = <span class="number">0</span>;</span><br><span class="line">    count++;</span><br><span class="line">    <span class="comment">// 通知因为队列空而被阻塞的线程</span></span><br><span class="line">    notEmpty.signal();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>put 操作</p><p>向队列尾部插入一个元素，如果队列有空闲则插入返回true，如果队列已满则阻塞当前线程知道队列有空闲插入后返回true，如果阻塞过程中被设置中断标志则抛出InterruptedException返回。和LinkedBlockingQueue的put也一样。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">put</span><span class="params">(E e)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    checkNotNull(e);</span><br><span class="line">    <span class="comment">// 获取锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 关键</span></span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 防止虚假唤醒使用while</span></span><br><span class="line">        <span class="keyword">while</span> (count == items.length)</span><br><span class="line">            <span class="comment">// 如果满则阻塞</span></span><br><span class="line">            notFull.await();</span><br><span class="line">        enqueue(e);</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>poll 操作</p><p>从队列头获取并移除一个元素，如果队列为空则返回null，是非阻塞方法，和LinkedBlockingQueue 的poll方法一样。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">poll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 获取独占锁</span></span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 如果不为空则出队 为空返回null</span></span><br><span class="line">        <span class="keyword">return</span> (count == <span class="number">0</span>) ? <span class="keyword">null</span> : dequeue();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> E <span class="title">dequeue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// assert lock.getHoldCount() == 1;</span></span><br><span class="line">    <span class="comment">// assert items[takeIndex] != null;</span></span><br><span class="line">    <span class="keyword">final</span> Object[] items = <span class="keyword">this</span>.items;</span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">    E x = (E) items[takeIndex];</span><br><span class="line">    <span class="comment">// 将takeIndex索引下的元素置为null</span></span><br><span class="line">    items[takeIndex] = <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 如果出队索引是最后一个 那么重置为0</span></span><br><span class="line">    <span class="keyword">if</span> (++takeIndex == items.length)</span><br><span class="line">        takeIndex = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// count减一</span></span><br><span class="line">    count--;</span><br><span class="line">    <span class="keyword">if</span> (itrs != <span class="keyword">null</span>)</span><br><span class="line">        itrs.elementDequeued();</span><br><span class="line">    <span class="comment">// 通知因为队列满而被阻塞的线程</span></span><br><span class="line">    notFull.signal();</span><br><span class="line">    <span class="keyword">return</span> x;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>take 操作</p><p>获取当前队列头部元素移除，如果队列空则阻塞线程知道队列不为空被激活然后移除返回，如果阻塞过程被设置中断标志则抛出InterruptedException然后返回。和LinkedBlockingQueue的take一样。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 关键</span></span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">while</span> (count == <span class="number">0</span>)</span><br><span class="line">            notEmpty.await();</span><br><span class="line">        <span class="keyword">return</span> dequeue();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>peek 操作</p><p>获取头部元素但不从队列中移除它，如果队列为空则返回null，是非阻塞方法，和LinkedBlockingQueue的peek一样。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">peek</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 如果队列为空返回null</span></span><br><span class="line">        <span class="keyword">return</span> itemAt(takeIndex); <span class="comment">// null when queue is empty</span></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>size 操作</p><p>计算当前队列元素个数</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 在这里要加锁</span></span><br><span class="line">    <span class="comment">// 因为不加锁 如果其他线程调用offer poll等操作的时候会对count进行修改</span></span><br><span class="line">    <span class="comment">// 而这里count没有使用volatile修饰 不能保证可见性，所以需要在锁块里。</span></span><br><span class="line">    <span class="comment">// 这样返回的就是从主内存中的count，如果不加锁count调用此方法的时候会被放入缓存中！</span></span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 直接返回维护的count</span></span><br><span class="line">        <span class="keyword">return</span> count;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ArrayBlockingQueue-小结"><a href="#ArrayBlockingQueue-小结" class="headerlink" title="ArrayBlockingQueue 小结"></a>ArrayBlockingQueue 小结</h3><p>  ArrayBlockingQueue 通过使用全局独占锁实现了同时只能有一个线程操作队列(包括入队 出队 获取个数等操作)，由此可见，这个锁的粒度比较大，所以并发性能有一定影响。</p><h2 id="PriorityBlockingQueue-原理探究"><a href="#PriorityBlockingQueue-原理探究" class="headerlink" title="PriorityBlockingQueue 原理探究"></a>PriorityBlockingQueue 原理探究</h2><h3 id="PriorityBlockingQueue-介绍"><a href="#PriorityBlockingQueue-介绍" class="headerlink" title="PriorityBlockingQueue 介绍"></a>PriorityBlockingQueue 介绍</h3><p>  PriorityBlockingQueue 是带优先级的无界阻塞队列，每次出队都返回优先级最高或者最低的元素。其内部使用的是平衡二叉树堆实现的，所以遍历队列元素不保证有序，默认使用对象的compareTo方法提供比较规则，如果需要自定义可以自定义comparators。</p><h3 id="PriorityBlockingQueue-类图结构"><a href="#PriorityBlockingQueue-类图结构" class="headerlink" title="PriorityBlockingQueue 类图结构"></a>PriorityBlockingQueue 类图结构</h3><p>  <img src="/2019/08/18/Java并发编程学习——Java并发编程之美学习笔记七/PriorityBlockingQueue.png" alt="PriorityBlockingQueue"></p><p>  PriorityBlockingQueue 中有一个数组queue用来存放队列元素，size用来存放队列个数。 allocationSpinLock 是一个自旋锁，其使用 CAS 来保证<em>同时只有一个线程能进行扩容操作</em>，状态为0或者1,0表示当前没有线程进行扩容。</p><p>  由于是一个优先级队列，所以有一个比较器 comparator 用来比较元素大小。lock独占锁用来控制同时只有一个线程能进行入队，出队操作。notEmpty条件用来实现take方法的阻塞，没有notFull是因为这是一个无界的优先级队列。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 默认队列大小</span></span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> DEFAULT_INITIAL_CAPACITY = <span class="number">11</span>;</span><br><span class="line"><span class="comment">// 默认构造</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">PriorityBlockingQueue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(DEFAULT_INITIAL_CAPACITY, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 指定大小</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">PriorityBlockingQueue</span><span class="params">(<span class="keyword">int</span> initialCapacity)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(initialCapacity, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 指定大小和比较器 如果默认则使用当前元素自身的比较器</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">PriorityBlockingQueue</span><span class="params">(<span class="keyword">int</span> initialCapacity,</span></span></span><br><span class="line"><span class="function"><span class="params">                             Comparator&lt;? <span class="keyword">super</span> E&gt; comparator)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (initialCapacity &lt; <span class="number">1</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException();</span><br><span class="line">    <span class="keyword">this</span>.lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line">    <span class="keyword">this</span>.notEmpty = lock.newCondition();</span><br><span class="line">    <span class="keyword">this</span>.comparator = comparator;</span><br><span class="line">    <span class="keyword">this</span>.queue = <span class="keyword">new</span> Object[initialCapacity];</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="PriorityBlockingQueue-原理介绍"><a href="#PriorityBlockingQueue-原理介绍" class="headerlink" title="PriorityBlockingQueue 原理介绍"></a>PriorityBlockingQueue 原理介绍</h3><ul><li><p>offer 操作</p><p>在队列中插入一个元素，由于是无界队列，所以一直返回true，这里没有在队头插入是因为这是优先级队列。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">offer</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判空</span></span><br><span class="line">    <span class="keyword">if</span> (e == <span class="keyword">null</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> NullPointerException();</span><br><span class="line">    <span class="comment">// 获取独占锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">int</span> n, cap;</span><br><span class="line">    Object[] array;</span><br><span class="line">    <span class="comment">// 如果size 队列个数大于容量了则进行扩容操作</span></span><br><span class="line">    <span class="keyword">while</span> ((n = size) &gt;= (cap = (array = queue).length))</span><br><span class="line">        <span class="comment">// 扩容</span></span><br><span class="line">        tryGrow(array, cap);</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 将队列比较器赋值过去</span></span><br><span class="line">        Comparator&lt;? <span class="keyword">super</span> E&gt; cmp = comparator;</span><br><span class="line">        <span class="comment">// 如果为空</span></span><br><span class="line">        <span class="keyword">if</span> (cmp == <span class="keyword">null</span>)</span><br><span class="line">            siftUpComparable(n, e, array);</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="comment">// 不为空</span></span><br><span class="line">            siftUpUsingComparator(n, e, array, cmp);</span><br><span class="line">        size = n + <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 入队成功后通知因为队伍空阻塞的线程</span></span><br><span class="line">        notEmpty.signal();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 解锁</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们需要看看如果size大于容量的时候 优先队列是怎么进行扩容操作的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">tryGrow</span><span class="params">(Object[] array, <span class="keyword">int</span> oldCap)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 首先将进行offer时所抢占的锁释放</span></span><br><span class="line">    <span class="comment">// 因为扩容是一个费事的操作 这个时候释放出来让其他线程执行</span></span><br><span class="line">    <span class="comment">// 会提高并发能力</span></span><br><span class="line">    lock.unlock(); <span class="comment">// must release and then re-acquire main lock</span></span><br><span class="line">    Object[] newArray = <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 判断allocationSpinLock自旋锁标志 如果为0代表没有线程进行扩容</span></span><br><span class="line">    <span class="comment">// 如果为0使用CAS设置为1 控制只有当前线程能执行扩容</span></span><br><span class="line">    <span class="keyword">if</span> (allocationSpinLock == <span class="number">0</span> &amp;&amp;</span><br><span class="line">        UNSAFE.compareAndSwapInt(<span class="keyword">this</span>, allocationSpinLockOffset,</span><br><span class="line">                                 <span class="number">0</span>, <span class="number">1</span>)) &#123;</span><br><span class="line">        <span class="comment">// oldCapr如果小于64 则扩容为 oldCap + 2</span></span><br><span class="line">        <span class="comment">// 如果否则扩容50%，并且限制最大为MAX_ARRAY_SIZE</span></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="keyword">int</span> newCap = oldCap + ((oldCap &lt; <span class="number">64</span>) ?</span><br><span class="line">                                   (oldCap + <span class="number">2</span>) : <span class="comment">// grow faster if small</span></span><br><span class="line">                                   (oldCap &gt;&gt; <span class="number">1</span>));</span><br><span class="line">            <span class="keyword">if</span> (newCap - MAX_ARRAY_SIZE &gt; <span class="number">0</span>) &#123;    <span class="comment">// possible overflow</span></span><br><span class="line">                <span class="keyword">int</span> minCap = oldCap + <span class="number">1</span>;</span><br><span class="line">                <span class="keyword">if</span> (minCap &lt; <span class="number">0</span> || minCap &gt; MAX_ARRAY_SIZE)</span><br><span class="line">                    <span class="keyword">throw</span> <span class="keyword">new</span> OutOfMemoryError();</span><br><span class="line">                newCap = MAX_ARRAY_SIZE;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">if</span> (newCap &gt; oldCap &amp;&amp; queue == array)</span><br><span class="line">                newArray = <span class="keyword">new</span> Object[newCap];</span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">            allocationSpinLock = <span class="number">0</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果同时多个线程进行CAS CAS失败的线程会进入这里</span></span><br><span class="line">    <span class="comment">// newArray == null 代表第一个线程进去还没扩容完成 其余CAS失败的线程执行到这</span></span><br><span class="line">    <span class="comment">// yield尽量让出CPU资源 目的是让扩容的线程执行完扩容之后优先获得锁</span></span><br><span class="line">    <span class="comment">// 但这里的yield让出资源时不保证的 有可能扩容线程还未扩容完 yield就已经结束</span></span><br><span class="line">    <span class="comment">// 然后CAS失败的线程获取锁直接退出到offer方法</span></span><br><span class="line">    <span class="keyword">if</span> (newArray == <span class="keyword">null</span>) <span class="comment">// back off if another thread is allocating</span></span><br><span class="line">        Thread.yield();</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="comment">// 如果扩容完毕并且获取到锁 复制当前queue里面的元素到newArray</span></span><br><span class="line">    <span class="keyword">if</span> (newArray != <span class="keyword">null</span> &amp;&amp; queue == array) &#123;</span><br><span class="line">        queue = newArray;</span><br><span class="line">        System.arraycopy(array, <span class="number">0</span>, newArray, <span class="number">0</span>, oldCap);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们还需要看如何建堆的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> &lt;T&gt; <span class="function"><span class="keyword">void</span> <span class="title">siftUpComparable</span><span class="params">(<span class="keyword">int</span> k, T x, Object[] array)</span> </span>&#123;</span><br><span class="line">    Comparable&lt;? <span class="keyword">super</span> T&gt; key = (Comparable&lt;? <span class="keyword">super</span> T&gt;) x;</span><br><span class="line">    <span class="comment">// n为队列个数</span></span><br><span class="line">    <span class="comment">// 如果大于0代表队列中有元素 所以需要“排序”</span></span><br><span class="line">    <span class="keyword">while</span> (k &gt; <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 通过位移获取插入节点的父节点索引 相当于 (k - 1) / 2</span></span><br><span class="line">        <span class="keyword">int</span> parent = (k - <span class="number">1</span>) &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">        Object e = array[parent];</span><br><span class="line">        <span class="comment">// 如果key 大于 父节点 那么跳出循环</span></span><br><span class="line">        <span class="comment">// 不然交换并继续循环 由此可见这是一个最小堆</span></span><br><span class="line">        <span class="keyword">if</span> (key.compareTo((T) e) &gt;= <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">break</span>;</span><br><span class="line">        array[k] = e;</span><br><span class="line">        k = parent;</span><br><span class="line">    &#125;</span><br><span class="line">    array[k] = key;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>poll 操作</p><p>poll操作时获取队列内部堆树的根节点元素，如果队列为空返回null</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">poll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> dequeue();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> E <span class="title">dequeue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取最大索引</span></span><br><span class="line">    <span class="keyword">int</span> n = size - <span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 队列为空返回null</span></span><br><span class="line">    <span class="keyword">if</span> (n &lt; <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        Object[] array = queue;</span><br><span class="line">        <span class="comment">// 获取队首元素</span></span><br><span class="line">        E result = (E) array[<span class="number">0</span>];</span><br><span class="line">        <span class="comment">// 获取队尾元素并赋值为null</span></span><br><span class="line">        E x = (E) array[n];</span><br><span class="line">        array[n] = <span class="keyword">null</span>;</span><br><span class="line">        Comparator&lt;? <span class="keyword">super</span> E&gt; cmp = comparator;</span><br><span class="line">        <span class="comment">// 将变量x插入数组下标为0的位置</span></span><br><span class="line">        <span class="comment">// 就是将堆底元素删除并插入堆顶然后重新排序</span></span><br><span class="line">        <span class="keyword">if</span> (cmp == <span class="keyword">null</span>)</span><br><span class="line">            siftDownComparable(<span class="number">0</span>, x, array, n);</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            siftDownUsingComparator(<span class="number">0</span>, x, array, n, cmp);</span><br><span class="line">        size = n;</span><br><span class="line">        <span class="keyword">return</span> result;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>take 操作</p><p>获取队列内部堆树的根节点元素，如果队列为空则<em>阻塞</em>。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 调用对中断响应的lockInterruptibly</span></span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    E result;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 防止虚假唤醒</span></span><br><span class="line">        <span class="keyword">while</span> ( (result = dequeue()) == <span class="keyword">null</span>)</span><br><span class="line">            <span class="comment">// 阻塞到条件队列中</span></span><br><span class="line">            notEmpty.await();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>size 操作</p><p>计算队列元素个数。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取锁 由于是加锁的所以获取的size是精确的。</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> size;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="PriorityBlockingQueue-小结"><a href="#PriorityBlockingQueue-小结" class="headerlink" title="PriorityBlockingQueue 小结"></a>PriorityBlockingQueue 小结</h3><p>  PriorityBlockingQueue 内部使用 <strong><em>二叉树堆</em></strong> 来维护元素优先级，使用数组作为元素存储的数据结构，这个数组是可扩容的，当添加元素的时候需要重置顺序，通过 <strong><em>递归比较插入节点和父节点</em></strong>，当移除根元素的时候需要 <strong><em>删除末尾节点并将这个结点放入根节点的位置重新设置堆的顺序</em></strong>，这也是堆实现的通用方法。队列还维护一个comparator 用户可以自定义。总体通过一个独占锁来控制同时只有一个线程可以进行出队和入队操作，只是用了一个notEmpty的条件变量因为他是一个无界队列，执行put方法永远不会处于await和take方法是阻塞队列并且可以中断。</p><h2 id="DelayQueue-原理探究"><a href="#DelayQueue-原理探究" class="headerlink" title="DelayQueue 原理探究"></a>DelayQueue 原理探究</h2><p>  DelayQueue并发队列是一个 <strong><em>无界阻塞延迟队列</em></strong> 。队列中每个元素都有过期时间，当从队列中获取元素的时候只有过期元素才会出队，队列头元素就是最快要过期的元素。</p><h3 id="Delay-类图结构"><a href="#Delay-类图结构" class="headerlink" title="Delay 类图结构"></a>Delay 类图结构</h3><p>  <img src="/2019/08/18/Java并发编程学习——Java并发编程之美学习笔记七/DelayQueue.png" alt="DelayQueue"></p><p>  DelayQueue 内部使用 PriorityQueue 存放数据， 使用 ReentrantLock 实现线程同步。另外队列里的元素要实现 Delayed 接口， 由于每个元素都有过期时间所以要实现获知当前元素还有多少时间过期的接口，由于内部使用优先队列来实现，所以要实现元素之间相互比较的接口。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">Delayed</span> <span class="keyword">extends</span> <span class="title">Comparable</span>&lt;<span class="title">Delayed</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">// 获取还剩多长时间</span></span><br><span class="line">    <span class="function"><span class="keyword">long</span> <span class="title">getDelay</span><span class="params">(TimeUnit unit)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  其中 lock 是独占锁，available 是对应的条件变量， <em>其中 leader 变量的使用基于 Leader-Follower 模式的变体，用于尽量减少不必要线程的等待</em> 。 <strong><em>当一个线程调用队列的take方法变为leader线程后，它会调用条件变量 available 的awaitNanos(delay)等待delay时间，但是其他线程(follower线程)会调用 available 条件的await()进行无限等待。 leader线程延迟时间过期后会退出take方法， 并通过调用 available.signal()方法唤醒一个follower线程被选举为leader线程。</em></strong></p><h3 id="DelayQueue-主要函数原理讲解"><a href="#DelayQueue-主要函数原理讲解" class="headerlink" title="DelayQueue 主要函数原理讲解"></a>DelayQueue 主要函数原理讲解</h3><ul><li><p>offer 操作</p><p>插入元素到队列，如果元素为null抛出NPE，否则由于无界一直返回true，插入元素要实现   Delayed 接口。</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">offer</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取独占锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 添加元素到优先级队列</span></span><br><span class="line">        q.offer(e);</span><br><span class="line">        <span class="comment">// 获取头元素 如果是e</span></span><br><span class="line">        <span class="comment">// 那么设置leader为null</span></span><br><span class="line">        <span class="keyword">if</span> (q.peek() == e) &#123;</span><br><span class="line">            leader = <span class="keyword">null</span>;</span><br><span class="line">            <span class="comment">// 通知某个等待线程</span></span><br><span class="line">            available.signal();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>take 操作</p><p>获取并移除队列里延迟过期的元素，如果队列里没有延迟过期的元素则等待</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">take</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    <span class="comment">// 获取锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 可以被中断</span></span><br><span class="line">    lock.lockInterruptibly();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            <span class="comment">// 获取优先队列队首元素，如果为空则等待</span></span><br><span class="line">            E first = q.peek();</span><br><span class="line">            <span class="keyword">if</span> (first == <span class="keyword">null</span>)</span><br><span class="line">                available.await();</span><br><span class="line">            <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// 获取延迟时间</span></span><br><span class="line">                <span class="keyword">long</span> delay = first.getDelay(NANOSECONDS);</span><br><span class="line">                <span class="comment">// 如果小于0那么直接移除</span></span><br><span class="line">                <span class="keyword">if</span> (delay &lt;= <span class="number">0</span>)</span><br><span class="line">                    <span class="keyword">return</span> q.poll();</span><br><span class="line">                first = <span class="keyword">null</span>; <span class="comment">// don't retain ref while waiting</span></span><br><span class="line">                <span class="comment">// 查看leader是否为null 不为null说明其他线程也在执行take</span></span><br><span class="line">                <span class="keyword">if</span> (leader != <span class="keyword">null</span>)</span><br><span class="line">                    <span class="comment">// 其他线程在执行则等待无限时间</span></span><br><span class="line">                    available.await();</span><br><span class="line">                <span class="keyword">else</span> &#123;</span><br><span class="line">                    <span class="comment">// 将当前线程设为leader</span></span><br><span class="line">                    Thread thisThread = Thread.currentThread();</span><br><span class="line">                    leader = thisThread;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="comment">// 阻塞delay时间</span></span><br><span class="line">                        <span class="comment">// 这是会释放锁 其他线程能进行操作</span></span><br><span class="line">                        available.awaitNanos(delay);</span><br><span class="line">                    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                        <span class="comment">// 最后将leader设置为null</span></span><br><span class="line">                        <span class="keyword">if</span> (leader == thisThread)</span><br><span class="line">                            leader = <span class="keyword">null</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 如果leader为null 且队头元素不为null 激活等待线程</span></span><br><span class="line">        <span class="keyword">if</span> (leader == <span class="keyword">null</span> &amp;&amp; q.peek() != <span class="keyword">null</span>)</span><br><span class="line">            available.signal();</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li><p>poll 操作</p><p>获取并移除过期元素，没有过期元素返回null</p></li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">poll</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (size == <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">    <span class="keyword">int</span> s = --size;</span><br><span class="line">    modCount++;</span><br><span class="line">    E result = (E) queue[<span class="number">0</span>];</span><br><span class="line">    E x = (E) queue[s];</span><br><span class="line">    queue[s] = <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 移除后调整堆顺序</span></span><br><span class="line">    <span class="keyword">if</span> (s != <span class="number">0</span>)</span><br><span class="line">        siftDown(<span class="number">0</span>, x);</span><br><span class="line">    <span class="keyword">return</span> result;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>size 操作</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">size</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">return</span> q.size();</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="DelayQueue-总结"><a href="#DelayQueue-总结" class="headerlink" title="DelayQueue 总结"></a>DelayQueue 总结</h3><p>  DelayQueu内部使用了PriorityQueue存放数据，使用ReentrantLock实现线程同步，队列里的元素要实现Delayed接口，在出队时要判断元素是否过期了。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;ConcurrentLinkedQueue-原理探究&quot;&gt;&lt;a href=&quot;#ConcurrentLinkedQueue-原理探究&quot; class=&quot;headerlink&quot; title=&quot;ConcurrentLinkedQueue 原理探究&quot;&gt;&lt;/a&gt;Concurre
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美学习笔记六</title>
    <link href="https://francisqiang.github.io/2019/08/16/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%85%AD/"/>
    <id>https://francisqiang.github.io/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记六/</id>
    <published>2019-08-16T07:25:17.000Z</published>
    <updated>2019-08-16T13:47:11.914Z</updated>
    
    <content type="html"><![CDATA[<h2 id="LockSupport工具类"><a href="#LockSupport工具类" class="headerlink" title="LockSupport工具类"></a>LockSupport工具类</h2><p>  LockSupport是JDk rt.jar下面的一个工具类，主要作用是 <strong><em>挂起和唤醒线程</em></strong> ，该工具类是 <strong><em>创建锁和其他同步类的基础</em></strong> 。</p><p>  <img src="/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记六/1.jpg" alt="LockSupport"></p><p>  LockSupport与每个使用它的线程都会关联一个许可证，在默认情况下调用LockSupport的方法的线程是不持有许可证的。LockSupport是使用Unsafe类实现的。</p><h3 id="LockSupport中的-void-park-方法"><a href="#LockSupport中的-void-park-方法" class="headerlink" title="LockSupport中的 void park() 方法"></a>LockSupport中的 void park() 方法</h3><p>  <strong><em>如果调用park()的线程已经拿到了与LockSupport关联的许可证，则调用LockSupport.park()时会马上返回，否则调用线程会被禁止参与线程的调度，也就是会被阻塞挂起</em></strong>。</p><p>  如果其他线程调用 unpark(Thread thread) 并将该线程作为参数传入， 此时因为调用 park()方法而被阻塞的线程会返回，另外如果其他线程调用了阻塞线程的Interrupt()方法， 设置了中断标志或者线程被虚假唤醒，则阻塞线程也会返回，需要注意的是返回的时候不会抛出InterruptedException异常。</p><h3 id="LockSupport中的-void-unpark-Thread-thread-方法"><a href="#LockSupport中的-void-unpark-Thread-thread-方法" class="headerlink" title="LockSupport中的 void unpark(Thread thread) 方法"></a>LockSupport中的 void unpark(Thread thread) 方法</h3><p>  <strong><em>当一个线程调用unpark方法，其中的参数线程如果没有持有与LockSupport类相关的许可证，那么会立即持有， 如果线程在之前已经调用了park()方法而被阻塞，那么该线程会立即返回，如果没有调用park()方法，那么调用unpark()之后调用park()会直接返回(因为通过unpark()获取了许可证)</em></strong></p><h3 id="LockSupport中的-void-parkNanos-long-nanos-方法"><a href="#LockSupport中的-void-parkNanos-long-nanos-方法" class="headerlink" title="LockSupport中的 void parkNanos(long nanos) 方法"></a>LockSupport中的 void parkNanos(long nanos) 方法</h3><p>  和park()方法差不多，只不过如果调用线程没有许可证，那么会被阻塞指定时间而返回。</p><h3 id="LockSupport中的-park-Object-blocker-方法"><a href="#LockSupport中的-park-Object-blocker-方法" class="headerlink" title="LockSupport中的 park(Object blocker) 方法"></a>LockSupport中的 park(Object blocker) 方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">park</span><span class="params">(Object blocker)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取当前线程</span></span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    <span class="comment">// 设置Blocker</span></span><br><span class="line">    setBlocker(t, blocker);</span><br><span class="line">    <span class="comment">// 挂起线程</span></span><br><span class="line">    UNSAFE.park(<span class="keyword">false</span>, <span class="number">0L</span>);</span><br><span class="line">    <span class="comment">// 线程被激活后清楚blocker</span></span><br><span class="line">    setBlocker(t, <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  Thread类中有一个volatile Object parkBlocker用来存放传递的blocker，也就是把blocker变量存放到了线程本地。一般使用LockSupport.park(this),在打印线程堆栈的时候就可以获得相关阻塞类的信息了。</p><h3 id="LockSupport中的-void-parkNanos-Object-blocker-long-nanos"><a href="#LockSupport中的-void-parkNanos-Object-blocker-long-nanos" class="headerlink" title="LockSupport中的 void parkNanos(Object blocker, long nanos)"></a>LockSupport中的 void parkNanos(Object blocker, long nanos)</h3><p>  比上面方法多一个超时时间。</p><h3 id="LockSupport中的-void-parkUntil-Object-blocker-long-deadline"><a href="#LockSupport中的-void-parkUntil-Object-blocker-long-deadline" class="headerlink" title="LockSupport中的 void parkUntil(Object blocker, long deadline)"></a>LockSupport中的 void parkUntil(Object blocker, long deadline)</h3><p>  多设置一个最终时间</p><h3 id="例子"><a href="#例子" class="headerlink" title="例子"></a>例子</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">FIFOMutex</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> AtomicBoolean locked = <span class="keyword">new</span> AtomicBoolean(<span class="keyword">false</span>);</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Queue&lt;Thread&gt; waiters = <span class="keyword">new</span> ConcurrentLinkedQueue&lt;&gt;();</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判断是否被中断</span></span><br><span class="line">    <span class="keyword">boolean</span> wasInterrupted = <span class="keyword">false</span>;</span><br><span class="line">    <span class="comment">// 获取当前线程并加入队列中</span></span><br><span class="line">    Thread current = Thread.currentThread();</span><br><span class="line">    waiters.add(current);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 只有队首能获取锁</span></span><br><span class="line">    <span class="keyword">while</span> (waiters.peek() != current || locked.compareAndSet(<span class="keyword">false</span>, <span class="keyword">true</span>)) &#123;</span><br><span class="line">      LockSupport.park(<span class="keyword">this</span>);</span><br><span class="line">      <span class="keyword">if</span> (Thread.interrupted()) &#123;</span><br><span class="line">        wasInterrupted = <span class="keyword">true</span>;</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果当前线程是队首并且没有其他线程抢占这个锁那么移除</span></span><br><span class="line">    waiters.remove();</span><br><span class="line">    <span class="comment">// 如果刚刚被中断则相应中断</span></span><br><span class="line">    <span class="keyword">if</span> (wasInterrupted) &#123;</span><br><span class="line">      current.interrupt();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 解锁的时候将布尔设置为false</span></span><br><span class="line">    locked.set(<span class="keyword">false</span>);</span><br><span class="line">    <span class="comment">// 激活等待中的队首线程</span></span><br><span class="line">    LockSupport.unpark(waiters.peek());</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="抽象同步队列-AQS-概述"><a href="#抽象同步队列-AQS-概述" class="headerlink" title="抽象同步队列 AQS 概述"></a>抽象同步队列 AQS 概述</h2><p>  AbstractQueuedSynchronizer 抽象同步队列简称 AQS。它是实现同步器的基本组件， 并发包中锁的底层就是使用AQS实现的。</p><p>  <img src="/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记六/AbstractQueuedSynchronizer.jpg" alt="AQS"></p><p>  我们大致能看到AQS是一个FIFO的双向队列，其内部通过<strong>head</strong>和<strong>tail</strong>记录头尾节点，队列中元素类型为Node，其中Node中的<strong>thread</strong>变量用来存放AQS队列中的线程。Node节点内部的<strong>SHARED</strong>用来标记该线程是获取<strong>共享资源</strong>时被阻塞挂起进入AQS队列的，<strong>EXCLUSIVE</strong>用来标记该线程是获取<strong>独占资源</strong>时被阻塞挂起进入AQS队列的。<strong>wiatStatus</strong>用来记录当前线程的等待状态，可以为<strong>CANCELLED</strong>(线程被取消了)，<strong>SIGNAL</strong>(线程需要被唤醒)，<strong>CONDITION</strong>(线程在条件队列里等待)，<strong>PROPAGATE</strong>(释放共享资源的时候需要通知其他节点)；<strong>prev</strong>用来记录当前节点的前驱节点，<strong>next</strong>记录当前节点的后继节点。</p><p>  在AQS中维持一个单一的状态信息<strong>state</strong>，可以通过getState，setState，compareAndSetState函数来操作， <strong><em>对于 ReentrantLock 的实现来说，state可以用来表示当前线程获取锁的可重入次数；对于读写锁 ReentrantReadWriteLock 来说，state的高16位表示读状态，低16位用来表示获取到写锁的线程的可重入次数。对于 semaphore 来说，state用来表示当前可用信号的个数，对于 CountDownlatch 来说，state用来表示计数器当前的值</em></strong> 。</p><p>  AQS有一个内部类 <strong><em>ConditionObject ，用来结合锁实现线程同步</em></strong>。ConditionObject可以直接访问AQS的内部变量，比如state状态值和AQS队列，ConditionObject是一个条件变量，<strong>每一个条件变量对应着一个条件队列</strong>(单向链表队列)。其用来存放调用条件变量的await()方法后被阻塞的线程，条件队列的头尾元素分别为firstWaiter和lastWaiter。</p><p>  对于AQS来说， <strong><em>线程同步的关键在于对state的操作</em></strong> 。独占方式下获取和释放资源使用的方法是acquire(int), void acquireInterruptibly(int), boolean release(int)。共享方式:acquireShared(int),acquireSharedInterruptibly(int),boolean releaseShared(int)方法。</p><p>  共享方式指的是能有多个线程获取资源(只要符合要求)，独占指的是只能有一个线程获取资源。</p><h3 id="独占方式获取和释放"><a href="#独占方式获取和释放" class="headerlink" title="独占方式获取和释放"></a><strong>独占方式获取和释放</strong></h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在调用acquire获取的时候首先会调用tryAcquire来设置当前状态值</span></span><br><span class="line"><span class="comment">// 如果失败则会调用acquireQueued将其标志为EXCLUSIVE并放入AQS队列中</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">        selfInterrupt();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 我们可以看到tryAcquire是需要子类去实现的</span></span><br><span class="line"><span class="comment">// 就比如ReentrantLock实现了对应的tryAcquire，使得state代表独占可重入</span></span><br><span class="line"><span class="comment">// 当state=0的时候代表没有线程占用，为大于等于1代表有线程占用并且state为可重入次数</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 在释放的时候首先会调用tryRelease()方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">release</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">            <span class="comment">// 唤醒头结点的后继节点</span></span><br><span class="line">            unparkSuccessor(h);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 同样的tryRelease也需要子类实现去怎么设置state的值</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">boolean</span> <span class="title">tryRelease</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="共享方式获取和释放"><a href="#共享方式获取和释放" class="headerlink" title="共享方式获取和释放"></a><strong>共享方式获取和释放</strong></h3><p>  对于共享方式和独占方式基本差不多，只不过在获取资源失败的时候线程会被标记为SHARED并放入AQS队列。还需要注意的是对于acquire系列的方法有一些是带Interruptibly的，这些带有Interruptibly的需要对中断进行相应，即其他线程中断了次挂起线程后，这个线程需要抛出InterruptedException然后返回。</p><h3 id="条件变量的支持"><a href="#条件变量的支持" class="headerlink" title="条件变量的支持"></a>条件变量的支持</h3><p>  对于 synchronized 来说只能支持一个共享变量的notify或wait方法， 而 <strong><em>AQS的锁能对应多个条件变量</em></strong> ，和synchronized一样在<strong>调用条件变量的await和signal方法之前必须先获取条件变量对应的锁</strong>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 创建一个ReentrantLock独占可重入锁</span></span><br><span class="line">ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line"><span class="comment">// 通过锁创建一个条件变量</span></span><br><span class="line">Condition codition = lock.newCondition();</span><br><span class="line"><span class="comment">// 获取锁</span></span><br><span class="line">lock.lock();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    begin wait...</span><br><span class="line">    <span class="comment">// 进入条件变量对应的阻塞队列</span></span><br><span class="line">    condition.await();</span><br><span class="line">    <span class="comment">// 被唤醒后</span></span><br><span class="line">    end wait...</span><br><span class="line">&#125; <span class="keyword">catch</span> (Excepetion e) &#123;</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    <span class="comment">// 解锁</span></span><br><span class="line">    lock.unlock()</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取锁</span></span><br><span class="line">lock.lock();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line">    begin signal...</span><br><span class="line">    <span class="comment">// 唤醒条件变量对应的阻塞队列中的线程</span></span><br><span class="line">    condition.signal();</span><br><span class="line">    end signal...</span><br><span class="line">&#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">    e.printStackTrace();</span><br><span class="line">&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">    <span class="comment">// 解锁</span></span><br><span class="line">    lock.unlock();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  在其中有一个 lock.newCondition() 这个其实是new了一个在AQS中内部声明的一个ConditionObject对象，可以上去查看刚刚的类图。这个ConditionObject是AQS的一个内部类，每个条件变量维护了一个条件队列(我们可以看到ConditionObject中有firstWaiter lastWaiter等)用来存放调用相应condition的await()方法而被阻塞的线程。</p><p>  我们来看condition的await方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">await</span><span class="params">()</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (Thread.interrupted())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> InterruptedException();</span><br><span class="line">        <span class="comment">// 创建节点并插入尾部</span></span><br><span class="line">        Node node = addConditionWaiter();</span><br><span class="line">        <span class="comment">// 释放当前线程获取的锁</span></span><br><span class="line">        <span class="comment">// 将状态存入</span></span><br><span class="line">        <span class="keyword">int</span> savedState = fullyRelease(node);</span><br><span class="line">        <span class="keyword">int</span> interruptMode = <span class="number">0</span>;</span><br><span class="line">        <span class="keyword">while</span> (!isOnSyncQueue(node)) &#123;</span><br><span class="line">            <span class="comment">// 调用lockSupport的park挂起线程</span></span><br><span class="line">            LockSupport.park(<span class="keyword">this</span>);</span><br><span class="line">            <span class="keyword">if</span> ((interruptMode = checkInterruptWhileWaiting(node)) != <span class="number">0</span>)</span><br><span class="line">                <span class="keyword">break</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (acquireQueued(node, savedState) &amp;&amp; interruptMode != THROW_IE)</span><br><span class="line">            interruptMode = REINTERRUPT;</span><br><span class="line">        <span class="keyword">if</span> (node.nextWaiter != <span class="keyword">null</span>) <span class="comment">// clean up if cancelled</span></span><br><span class="line">            unlinkCancelledWaiters();</span><br><span class="line">        <span class="keyword">if</span> (interruptMode != <span class="number">0</span>)</span><br><span class="line">            reportInterruptAfterWait(interruptMode);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 插入条件队列尾部</span></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Node <span class="title">addConditionWaiter</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Node t = lastWaiter;</span><br><span class="line">        <span class="comment">// If lastWaiter is cancelled, clean out.</span></span><br><span class="line">        <span class="keyword">if</span> (t != <span class="keyword">null</span> &amp;&amp; t.waitStatus != Node.CONDITION) &#123;</span><br><span class="line">            unlinkCancelledWaiters();</span><br><span class="line">            t = lastWaiter;</span><br><span class="line">        &#125;</span><br><span class="line">        Node node = <span class="keyword">new</span> Node(Thread.currentThread(), Node.CONDITION);</span><br><span class="line">        <span class="keyword">if</span> (t == <span class="keyword">null</span>)</span><br><span class="line">            firstWaiter = node;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            t.nextWaiter = node;</span><br><span class="line">        lastWaiter = node;</span><br><span class="line">        <span class="keyword">return</span> node;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 释放当前线程获取的锁</span></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">int</span> <span class="title">fullyRelease</span><span class="params">(Node node)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">boolean</span> failed = <span class="keyword">true</span>;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="keyword">int</span> savedState = getState();</span><br><span class="line">        <span class="keyword">if</span> (release(savedState)) &#123;</span><br><span class="line">            failed = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">return</span> savedState;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalMonitorStateException();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="keyword">if</span> (failed)</span><br><span class="line">            node.waitStatus = Node.CANCELLED;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们再来看看signal方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">signal</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!isHeldExclusively())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalMonitorStateException();</span><br><span class="line">    <span class="comment">// 获取条件队列中的队头节点</span></span><br><span class="line">    Node first = firstWaiter;</span><br><span class="line">    <span class="keyword">if</span> (first != <span class="keyword">null</span>)</span><br><span class="line">        doSignal(first);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">doSignal</span><span class="params">(Node first)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">    <span class="keyword">if</span> ( (firstWaiter = first.nextWaiter) == <span class="keyword">null</span>)</span><br><span class="line">            lastWaiter = <span class="keyword">null</span>;</span><br><span class="line">        first.nextWaiter = <span class="keyword">null</span>;</span><br><span class="line">    <span class="comment">// 转移节点到AQS队列中</span></span><br><span class="line">    &#125; <span class="keyword">while</span> (!transferForSignal(first) &amp;&amp;</span><br><span class="line">             (first = firstWaiter) != <span class="keyword">null</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">transferForSignal</span><span class="params">(Node node)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 进入AQS队列中</span></span><br><span class="line">    Node p = enq(node);</span><br><span class="line">    <span class="keyword">int</span> ws = p.waitStatus;</span><br><span class="line">    <span class="keyword">if</span> (ws &gt; <span class="number">0</span> || !compareAndSetWaitStatus(p, ws,Node.SIGNAL))</span><br><span class="line">        <span class="comment">// 唤醒线程</span></span><br><span class="line">        LockSupport.unpark(node.thread);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  总结一下： <strong><em>当多个线程调用lock.lock()方法的时候，未竞争到锁的线程会被转换成一个NOde节点插入AQS队列中，如果获取到了锁的线程(因为某种条件不满足)调用了await()方法的时候，会被转换成Node节点并插入相应的Condition条件队列中并且释放锁，这个时候未获取到锁的线程(在AQS队列中)就可以重新竞争锁了，如果另外一个线程调用条件变量的signal()或者signalAll()方法，会把条件变量队列中的一个或全部Node移动到AQS阻塞队列里</em></strong>。</p><h2 id="独占锁-ReentrantLock-的原理"><a href="#独占锁-ReentrantLock-的原理" class="headerlink" title="独占锁 ReentrantLock 的原理"></a>独占锁 ReentrantLock 的原理</h2><h3 id="类图结构"><a href="#类图结构" class="headerlink" title="类图结构"></a>类图结构</h3><p>  <img src="/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记六/ReentrantLock.jpg" alt="ReentrantLock"></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 默认非公平锁</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ReentrantLock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync = <span class="keyword">new</span> NonfairSync();</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">ReentrantLock</span><span class="params">(<span class="keyword">boolean</span> fair)</span> </span>&#123;</span><br><span class="line">    sync = fair ? <span class="keyword">new</span> FairSync() : <span class="keyword">new</span> NonfairSync();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  其中 ReentrantLock 中有一个内部类Sync 这个类继承了AQS，并且扩展了公平锁(FairSync)和非公平锁(NonFairSync)</p><p>  在Sync中AQS的state状态值表示 <strong><em>线程的可重入次数</em></strong>。</p><h3 id="获取锁"><a href="#获取锁" class="headerlink" title="获取锁"></a>获取锁</h3><ol><li>void lock()</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 在ReentrantLock中的lock方法调用了sync的lock</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.lock();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 在Sync中是一各抽象方法是子类去实现</span></span><br><span class="line"><span class="function"><span class="keyword">abstract</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span></span>;</span><br><span class="line"><span class="comment">// 公平锁实现</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    acquire(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 非公平锁实现</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// CAS设置状态值</span></span><br><span class="line">    <span class="comment">// 如果成功则设置锁的持有者为当前线程</span></span><br><span class="line">    <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, <span class="number">1</span>))</span><br><span class="line">        setExclusiveOwnerThread(Thread.currentThread());</span><br><span class="line">    <span class="comment">// 不然调用AQS的acquire</span></span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        acquire(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 再来回顾一下AQS的acquire方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 首先尝试调用tryAcquire</span></span><br><span class="line">    <span class="comment">// 不成功则加入AQS队列并设置标记为EXCLUSIVE(抢占独占资源而被阻塞的标志)</span></span><br><span class="line">    <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">        selfInterrupt();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 我们知道AQS并没有实现具体的tryAcquire方法 需要子类去实现</span></span><br><span class="line"><span class="comment">// 非公平锁实现的tryAcquire方法</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> nonfairTryAcquire(acquires);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 非公平锁</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">nonfairTryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取当前线程</span></span><br><span class="line">    <span class="keyword">final</span> Thread current = Thread.currentThread();</span><br><span class="line">    <span class="comment">// 获取当前状态值</span></span><br><span class="line">    <span class="keyword">int</span> c = getState();</span><br><span class="line">    <span class="comment">// 如果状态值为0 那么没有线程获取锁</span></span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 如果CAS设置成功</span></span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">            <span class="comment">// 设置锁持有者为当前线程</span></span><br><span class="line">            setExclusiveOwnerThread(current);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果状态值不为0且当前线程为锁的持有者 那么将状态值加上参数acquires</span></span><br><span class="line">    <span class="comment">// 可重入锁的关键</span></span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">        <span class="keyword">int</span> nextc = c + acquires;</span><br><span class="line">        <span class="comment">// 数值校验</span></span><br><span class="line">        <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>) <span class="comment">// overflow</span></span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">"Maximum lock count exceeded"</span>);</span><br><span class="line">        setState(nextc);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 公平锁的tryAcquire实现</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取当前线程</span></span><br><span class="line">    <span class="keyword">final</span> Thread current = Thread.currentThread();</span><br><span class="line">    <span class="comment">// 获取装填值</span></span><br><span class="line">    <span class="keyword">int</span> c = getState();</span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 注意这里增加了一个hasQueuedPredecessors()校验</span></span><br><span class="line">        <span class="comment">// 根据字面意思就是判断是否有前驱节点</span></span><br><span class="line">        <span class="comment">// 如果没有那么执行和刚刚一样</span></span><br><span class="line">        <span class="comment">// 如果有且不是锁持有者就直接退出竞争</span></span><br><span class="line">        <span class="keyword">if</span> (!hasQueuedPredecessors() &amp;&amp;</span><br><span class="line">            compareAndSetState(<span class="number">0</span>, acquires)) &#123;</span><br><span class="line">            setExclusiveOwnerThread(current);</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">else</span> <span class="keyword">if</span> (current == getExclusiveOwnerThread()) &#123;</span><br><span class="line">        <span class="keyword">int</span> nextc = c + acquires;</span><br><span class="line">        <span class="keyword">if</span> (nextc &lt; <span class="number">0</span>)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">"Maximum lock count exceeded"</span>);</span><br><span class="line">        setState(nextc);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 公平锁实现的关键</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">hasQueuedPredecessors</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Node t = tail; <span class="comment">// Read fields in reverse initialization order</span></span><br><span class="line">    Node h = head;</span><br><span class="line">    Node s;</span><br><span class="line">    <span class="comment">// 验证当前队列是否只有一个元素或者当前线程是否是队列第一个元素（是否在哨兵节点的后面）</span></span><br><span class="line">    <span class="keyword">return</span> h != t &amp;&amp;</span><br><span class="line">        ((s = h.next) == <span class="keyword">null</span> || s.thread != Thread.currentThread());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们来回顾一下非公平机制和公平机制到底是怎么实现的。</p><p>  比如这个时候有两个线程竞争ReentrantLock 线程A和线程B并且线程C已经获取了ReentrantLock，当线程A进入lock()方法然后进入acquire()方法再进入nonfairTryAcquire()方法(非公平策略)会因为这个锁已经被其他线程占有而被阻塞进入AQS队列，线程B也同时竞争锁，进入进入lock()方法然后进入acquire()方法再进入nonfairTryAcquire()方法(非公平策略)，而这个时候恰恰线程C释放了锁，那么线程B就会直接判断state值发现为0然后线程B就抢占了。我们可以发现，线程B是后来的但是先获得了锁，这就是非公平的体现。</p><p>  而公平策略中会在抢占锁之前判断前面有没有线程在等待，如果有那么该线程直接放弃竞争。所以公平锁的资源浪费会比非公平高。</p><ol start="2"><li><p>void lockInterruptibly() 方法</p><p>和lock()方法类似，但是它<strong>会对中断进行响应</strong>,就是当前线程在调用该方法的时候，其他线程如果调用了当前线程的interrupt()方法，该线程会抛出InterruptException异常然后返回。</p></li></ol><h3 id="释放锁"><a href="#释放锁" class="headerlink" title="释放锁"></a>释放锁</h3><ol><li><p>void unlock() 方法</p><p> 尝试释放锁，如果当前线程持有锁则将AQS的state值减一，如果减下来为0那么该线程会释放锁，否则仅仅减一。如果当前线程没有持有锁而调用该方法会抛出IllegalMonitorStateException异常。</p></li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.release(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">release</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 首先尝试</span></span><br><span class="line">    <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">        <span class="comment">// 如果释放成功那么唤醒下一个</span></span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">            unparkSuccessor(h);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryRelease</span><span class="params">(<span class="keyword">int</span> releases)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 将状态值减去参数</span></span><br><span class="line">    <span class="keyword">int</span> c = getState() - releases;</span><br><span class="line">    <span class="comment">// 如果没有获得锁的线程调用 抛出异常</span></span><br><span class="line">    <span class="keyword">if</span> (Thread.currentThread() != getExclusiveOwnerThread())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalMonitorStateException();</span><br><span class="line">    <span class="comment">// 设置free标志为false 后面要返回释放是否成功的</span></span><br><span class="line">    <span class="keyword">boolean</span> free = <span class="keyword">false</span>;</span><br><span class="line">    <span class="comment">// 如果减去后为0那么就释放</span></span><br><span class="line">    <span class="keyword">if</span> (c == <span class="number">0</span>) &#123;</span><br><span class="line">        free = <span class="keyword">true</span>;</span><br><span class="line">        <span class="comment">// 设置锁的持有者为空</span></span><br><span class="line">        setExclusiveOwnerThread(<span class="keyword">null</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 设置标志</span></span><br><span class="line">    setState(c);</span><br><span class="line">    <span class="comment">// 返回释放是否成功</span></span><br><span class="line">    <span class="keyword">return</span> free;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="读写锁-ReentrantReadWriteLock-原理"><a href="#读写锁-ReentrantReadWriteLock-原理" class="headerlink" title="读写锁 ReentrantReadWriteLock 原理"></a>读写锁 ReentrantReadWriteLock 原理</h2><h3 id="读写锁类图结构"><a href="#读写锁类图结构" class="headerlink" title="读写锁类图结构"></a>读写锁类图结构</h3><p>  <img src="/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记六/ReentrantReadWriteLock.jpg" alt="ReentrantReadWriteLock"></p><p>  读写锁内部维护了一个ReadLock和WriteLock，他们 <strong><em>依赖Sync来实现具体功能</em></strong>， 而Sync继承AQS并且提供了公平和非公平锁的实现。</p><p>  AQS中只维护了一个state变量，而读写锁中通过将state一分为二控制读状态和写状态。它巧妙的 <strong><em>将state的高16位表示读状态(也就是读锁的次数)，低16位表示写状态也就是写锁的可重入次数</em></strong>。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SHARED_SHIFT   = <span class="number">16</span>;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> SHARED_UNIT    = (<span class="number">1</span> &lt;&lt; SHARED_SHIFT);</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> MAX_COUNT      = (<span class="number">1</span> &lt;&lt; SHARED_SHIFT) - <span class="number">1</span>;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">int</span> EXCLUSIVE_MASK = (<span class="number">1</span> &lt;&lt; SHARED_SHIFT) - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/** Returns the number of shared holds represented in count  */</span></span><br><span class="line"><span class="comment">// 返回读锁线程数</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">sharedCount</span><span class="params">(<span class="keyword">int</span> c)</span>    </span>&#123; <span class="keyword">return</span> c &gt;&gt;&gt; SHARED_SHIFT; &#125;</span><br><span class="line"><span class="comment">/** Returns the number of exclusive holds represented in count  */</span></span><br><span class="line"><span class="comment">// 返回写锁可重入次数</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">int</span> <span class="title">exclusiveCount</span><span class="params">(<span class="keyword">int</span> c)</span> </span>&#123; <span class="keyword">return</span> c &amp; EXCLUSIVE_MASK; &#125;</span><br></pre></td></tr></table></figure><p>  其中 <strong>firstReader</strong> 用来获取第一个获取到读锁的线程， <strong>firstReaderHoldCount</strong>  则记录第一个获取到读锁的线程获取读锁的可重入次数， <strong>cachedHoldCounter</strong> 用来记录最后一个获取读锁的线程获取读锁的可重入次数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 内部类 里面维护了count和线程id</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">HoldCounter</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> count = <span class="number">0</span>;</span><br><span class="line">    <span class="comment">// Use id, not reference, to avoid garbage retention</span></span><br><span class="line">    <span class="keyword">final</span> <span class="keyword">long</span> tid = getThreadId(Thread.currentThread());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  <strong>readHolds</strong> 是 ThreadLocal变量，用来存放出去第一个获取读锁线程外的其他线程获取读锁的可重入次数， ThreadLocalHoldCounter 继承了ThreadLocal。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">ThreadLocalHoldCounter</span></span></span><br><span class="line"><span class="class">    <span class="keyword">extends</span> <span class="title">ThreadLocal</span>&lt;<span class="title">HoldCounter</span>&gt; </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> HoldCounter <span class="title">initialValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> HoldCounter();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="写锁的获取与释放"><a href="#写锁的获取与释放" class="headerlink" title="写锁的获取与释放"></a>写锁的获取与释放</h3><p>  写锁通过WriteLock来实现。</p><ol><li>void lock()</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// WriteLock的lock()</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.acquire(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// AQS的acquire</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquire</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!tryAcquire(arg) &amp;&amp;</span><br><span class="line">        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))</span><br><span class="line">        selfInterrupt();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 读写锁的sync的tryAcquire</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryAcquire</span><span class="params">(<span class="keyword">int</span> acquires)</span> </span>&#123;</span><br><span class="line">    <span class="comment">/*</span></span><br><span class="line"><span class="comment">     * Walkthrough:</span></span><br><span class="line"><span class="comment">     * 1. If read count nonzero or write count nonzero</span></span><br><span class="line"><span class="comment">     *    and owner is a different thread, fail.</span></span><br><span class="line"><span class="comment">     * 2. If count would saturate, fail. (This can only</span></span><br><span class="line"><span class="comment">     *    happen if count is already nonzero.)</span></span><br><span class="line"><span class="comment">     * 3. Otherwise, this thread is eligible for lock if</span></span><br><span class="line"><span class="comment">     *    it is either a reentrant acquire or</span></span><br><span class="line"><span class="comment">     *    queue policy allows it. If so, update state</span></span><br><span class="line"><span class="comment">     *    and set owner.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="comment">// 获取当前线程</span></span><br><span class="line">    Thread current = Thread.currentThread();</span><br><span class="line">    <span class="comment">// 获取当前状态 如果为0那么就代表没有线程获取读锁或者写锁</span></span><br><span class="line">    <span class="keyword">int</span> c = getState();</span><br><span class="line">    <span class="comment">// 获取写锁 即低16位</span></span><br><span class="line">    <span class="keyword">int</span> w = exclusiveCount(c);</span><br><span class="line">    <span class="comment">// 如果有线程获取写锁或者读锁</span></span><br><span class="line">    <span class="keyword">if</span> (c != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="comment">// 因为肯定有线程获取写锁或者读锁 而且w=0代表没有线程获取写锁</span></span><br><span class="line">        <span class="comment">// 则肯定有线程获取读锁</span></span><br><span class="line">        <span class="comment">// 这里代表的是有线程获取读锁</span></span><br><span class="line">        <span class="comment">// 或者有线程获取读锁或者写锁且当前线程不是写锁拥有者</span></span><br><span class="line">        <span class="comment">// 的时候返回false</span></span><br><span class="line">        <span class="keyword">if</span> (w == <span class="number">0</span> || current != getExclusiveOwnerThread())</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="comment">// 如果有线程获取写锁并且是当前线程的</span></span><br><span class="line">        <span class="keyword">if</span> (w + exclusiveCount(acquires) &gt; MAX_COUNT)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">"Maximum lock count exceeded"</span>);</span><br><span class="line">        <span class="comment">// Reentrant acquire</span></span><br><span class="line">        setState(c + acquires);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果当前没有线程获取读锁或者写锁 则尝试设置状态 不成功返回false 成功设置当前锁拥有者为当前线程并放回true</span></span><br><span class="line">    <span class="keyword">if</span> (writerShouldBlock() ||</span><br><span class="line">        !compareAndSetState(c, c + acquires))</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    setExclusiveOwnerThread(current);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们来看一下writerShouldBlock的实现</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 非公平实现</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">NonfairSync</span> <span class="keyword">extends</span> <span class="title">Sync</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">8159625535654395037L</span>;</span><br><span class="line">    <span class="comment">// 直接return false</span></span><br><span class="line">    <span class="comment">// 代表非公平直接竞争</span></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">writerShouldBlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>; <span class="comment">// writers can always barge</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">readerShouldBlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> apparentlyFirstQueuedIsExclusive();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 公平实现</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">FairSync</span> <span class="keyword">extends</span> <span class="title">Sync</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">2274990926593161451L</span>;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">writerShouldBlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> hasQueuedPredecessors();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">readerShouldBlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> hasQueuedPredecessors();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 判断AQS中是否有前驱的</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">hasQueuedPredecessors</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Node t = tail; <span class="comment">// Read fields in reverse initialization order</span></span><br><span class="line">    Node h = head;</span><br><span class="line">    Node s;</span><br><span class="line">    <span class="keyword">return</span> h != t &amp;&amp;</span><br><span class="line">        ((s = h.next) == <span class="keyword">null</span> || s.thread != Thread.currentThread());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="2"><li>boolean tryLock()</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">tryLock</span><span class="params">( )</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> sync.tryWriteLock();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 和lock差不多 但是当当前已经有线程持有写锁或者读锁 直接返回false 不阻塞</span></span><br><span class="line"><span class="comment">// 如果CAS成功直接返回true</span></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryWriteLock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Thread current = Thread.currentThread();</span><br><span class="line">    <span class="keyword">int</span> c = getState();</span><br><span class="line">    <span class="keyword">if</span> (c != <span class="number">0</span>) &#123;</span><br><span class="line">        <span class="keyword">int</span> w = exclusiveCount(c);</span><br><span class="line">        <span class="keyword">if</span> (w == <span class="number">0</span> || current != getExclusiveOwnerThread())</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        <span class="keyword">if</span> (w == MAX_COUNT)</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error(<span class="string">"Maximum lock count exceeded"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (!compareAndSetState(c, c + <span class="number">1</span>))</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    setExclusiveOwnerThread(current);</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ol start="3"><li>void unlock()</li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.release(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">release</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryRelease(arg)) &#123;</span><br><span class="line">        <span class="comment">// 成功·则通知唤醒下一个</span></span><br><span class="line">        Node h = head;</span><br><span class="line">        <span class="keyword">if</span> (h != <span class="keyword">null</span> &amp;&amp; h.waitStatus != <span class="number">0</span>)</span><br><span class="line">            unparkSuccessor(h);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryRelease</span><span class="params">(<span class="keyword">int</span> releases)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判断当前线程是否是该写锁的持有者</span></span><br><span class="line">    <span class="keyword">if</span> (!isHeldExclusively())</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalMonitorStateException();</span><br><span class="line">    <span class="keyword">int</span> nextc = getState() - releases;</span><br><span class="line">    <span class="comment">// 如果减到0那么释放</span></span><br><span class="line">    <span class="keyword">boolean</span> free = exclusiveCount(nextc) == <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">if</span> (free)</span><br><span class="line">        setExclusiveOwnerThread(<span class="keyword">null</span>);</span><br><span class="line">    setState(nextc);</span><br><span class="line">    <span class="keyword">return</span> free;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="读锁的获取域释放"><a href="#读锁的获取域释放" class="headerlink" title="读锁的获取域释放"></a>读锁的获取域释放</h3><ul><li>void lock()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">lock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.acquireShared(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// AQS的acquireShared</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">acquireShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (tryAcquireShared(arg) &lt; <span class="number">0</span>)</span><br><span class="line">        doAcquireShared(arg);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// AQS需要被实现的tryAcquireShared</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">tryAcquireShared</span><span class="params">(<span class="keyword">int</span> arg)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">throw</span> <span class="keyword">new</span> UnsupportedOperationException();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 读写锁实现的</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">int</span> <span class="title">tryAcquireShared</span><span class="params">(<span class="keyword">int</span> unused)</span> </span>&#123;</span><br><span class="line">    Thread current = Thread.currentThread();</span><br><span class="line">    <span class="keyword">int</span> c = getState();</span><br><span class="line">    <span class="comment">// 判断写锁是否被占用</span></span><br><span class="line">    <span class="keyword">if</span> (exclusiveCount(c) != <span class="number">0</span> &amp;&amp;</span><br><span class="line">        getExclusiveOwnerThread() != current)</span><br><span class="line">        <span class="keyword">return</span> -<span class="number">1</span>;</span><br><span class="line">    <span class="comment">// 如果当前线程是写锁拥有者也可以占用读锁</span></span><br><span class="line">    <span class="comment">// 记录当前读锁拥有数</span></span><br><span class="line">    <span class="keyword">int</span> r = sharedCount(c);</span><br><span class="line">    <span class="comment">// 判断readerShouldBlock</span></span><br><span class="line">    <span class="comment">// 队列里第一个是否在获取写锁 如果不是那么判断是否到达最大值 如果不是那么cas状态+1</span></span><br><span class="line">    <span class="keyword">if</span> (!readerShouldBlock() &amp;&amp;</span><br><span class="line">        r &lt; MAX_COUNT &amp;&amp;</span><br><span class="line">        compareAndSetState(c, c + SHARED_UNIT)) &#123;</span><br><span class="line">        <span class="comment">// 如果为0 那么设置firstReader</span></span><br><span class="line">        <span class="keyword">if</span> (r == <span class="number">0</span>) &#123;</span><br><span class="line">            firstReader = current;</span><br><span class="line">            firstReaderHoldCount = <span class="number">1</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> <span class="keyword">if</span> (firstReader == current) &#123;</span><br><span class="line">            <span class="comment">// 如果当前线程已经是first了那么增加firstReaderHoldCount</span></span><br><span class="line">            firstReaderHoldCount++;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 不然放入cachedHoldCounter中</span></span><br><span class="line">            HoldCounter rh = cachedHoldCounter;</span><br><span class="line">            <span class="keyword">if</span> (rh == <span class="keyword">null</span> || rh.tid != getThreadId(current))</span><br><span class="line">                cachedHoldCounter = rh = readHolds.get();</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (rh.count == <span class="number">0</span>)</span><br><span class="line">                readHolds.set(rh);</span><br><span class="line">            rh.count++;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 以上都不满足</span></span><br><span class="line">    <span class="comment">// 即写锁不被占用</span></span><br><span class="line">    <span class="comment">// 就自旋获取</span></span><br><span class="line">    <span class="keyword">return</span> fullTryAcquireShared(current);</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">NonfairSync</span> <span class="keyword">extends</span> <span class="title">Sync</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = -<span class="number">8159625535654395037L</span>;</span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">writerShouldBlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>; <span class="comment">// writers can always barge</span></span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 非公平的readerShouldBlock实现</span></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">readerShouldBlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> apparentlyFirstQueuedIsExclusive();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">apparentlyFirstQueuedIsExclusive</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Node h, s;</span><br><span class="line">    <span class="comment">// 如果队列里面存在一个元素 则判断第一个元素是否在尝试获取写锁</span></span><br><span class="line">    <span class="keyword">return</span> (h = head) != <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">        (s = h.next)  != <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">        !s.isShared()         &amp;&amp;</span><br><span class="line">        s.thread != <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><ul><li>void unlock()</li></ul><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">unlock</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    sync.releaseShared(<span class="number">1</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 读写锁实现的tryReleaseShared</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">tryReleaseShared</span><span class="params">(<span class="keyword">int</span> unused)</span> </span>&#123;</span><br><span class="line">    Thread current = Thread.currentThread();</span><br><span class="line">    <span class="comment">// 判断当前线程是不是第一个reader</span></span><br><span class="line">    <span class="keyword">if</span> (firstReader == current) &#123;</span><br><span class="line">        <span class="comment">// assert firstReaderHoldCount &gt; 0;</span></span><br><span class="line">        <span class="comment">// 如果是1 那么设置第一个读者为空</span></span><br><span class="line">        <span class="keyword">if</span> (firstReaderHoldCount == <span class="number">1</span>)</span><br><span class="line">            firstReader = <span class="keyword">null</span>;</span><br><span class="line">        <span class="keyword">else</span></span><br><span class="line">            <span class="comment">// 不然 --</span></span><br><span class="line">            firstReaderHoldCount--;</span><br><span class="line">    &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// 那么找其他线程 并通过线程id获取</span></span><br><span class="line">        HoldCounter rh = cachedHoldCounter;</span><br><span class="line">        <span class="keyword">if</span> (rh == <span class="keyword">null</span> || rh.tid != getThreadId(current))</span><br><span class="line">            rh = readHolds.get();</span><br><span class="line">        <span class="keyword">int</span> count = rh.count;</span><br><span class="line">        <span class="keyword">if</span> (count &lt;= <span class="number">1</span>) &#123;</span><br><span class="line">            readHolds.remove();</span><br><span class="line">            <span class="keyword">if</span> (count &lt;= <span class="number">0</span>)</span><br><span class="line">                <span class="keyword">throw</span> unmatchedUnlockException();</span><br><span class="line">        &#125;</span><br><span class="line">        --rh.count;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">// 获取状态</span></span><br><span class="line">        <span class="keyword">int</span> c = getState();</span><br><span class="line">        <span class="comment">// 释放读锁对读取器没有影响，但如果读锁和写锁现在都是空闲的，它可能允许等待写入器继续进行</span></span><br><span class="line">        <span class="keyword">int</span> nextc = c - SHARED_UNIT;</span><br><span class="line">        <span class="comment">// 循环知道自己的读技术-1 CAS更新成功</span></span><br><span class="line">        <span class="keyword">if</span> (compareAndSetState(c, nextc))</span><br><span class="line">            <span class="keyword">return</span> nextc == <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="JDK8中新增的-StampedLock-探究"><a href="#JDK8中新增的-StampedLock-探究" class="headerlink" title="JDK8中新增的 StampedLock 探究"></a>JDK8中新增的 StampedLock 探究</h2><h3 id="概述"><a href="#概述" class="headerlink" title="概述"></a>概述</h3><p>  StampedLock是JDK8中新增的锁，它提供了三种模式的读写锁</p><ol><li><strong><em>写锁</em></strong>。 是一个独占锁而且是不可重入锁，请求该锁成功后会返回一个stamp参数来表示该锁的版本，当释放该锁的时候需要调用unlockWrite并传递获取锁是的stamp参数，并且它提供一个非阻塞的 tryWriteLock 方法。</li><li><strong><em>悲观读锁</em></strong>。 是一个共享锁，在没有线程占有写锁的情况下可以获取。如果有线程占用写锁，那么竞争该锁的线程会被阻塞(和读写锁的读锁很像 但是他是不可重入锁) 这里悲观指 <strong><em>需要先将数据加锁， 这是在读少写多的去情况的一种考虑。</em></strong> 该锁请求成功后也会返回一个stamp值，并且在调用unlockRead方法时需要传入获取该锁时的stamp来释放锁。并且它提供了一个非阻塞的 tryReadLock 方法。</li><li><p><strong><em>乐观读锁</em></strong>。 在操作数据之前没有通过CAS设置锁的装填，仅仅通过位运算测试，如果当前没有线程持有写锁，则简单返回一个非0的stamp版本信息。获取该stamp后再具体操作数据前还需要调用validate方法验证该stamp是否已经不可用，也就是看调用tryOptimisticRead返回stamp后当当前是否有其他线程持有了写锁，如果是则返回0，否则就可以通过该stamp版本的锁对数据进行操作，适用于读多写少的情况。</p><p>这三种锁还能在一定条件下相互转换。</p></li></ol><h3 id="最佳实践"><a href="#最佳实践" class="headerlink" title="最佳实践"></a>最佳实践</h3><p>  我们可以巧妙的利用三种锁的转换和三种锁性能差异来设计一套最佳实践模板 这也是官方推荐的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 读模板</span></span><br><span class="line"><span class="keyword">final</span> StampedLock sl = <span class="keyword">new</span> StampedLock();</span><br><span class="line"><span class="comment">// 乐观读</span></span><br><span class="line"><span class="keyword">long</span> stamp = sl.tryOptimisticRead();</span><br><span class="line"><span class="comment">// 读⼊⽅法局部变量</span></span><br><span class="line">......</span><br><span class="line"><span class="comment">// 校验 stamp</span></span><br><span class="line"><span class="keyword">if</span> (!sl.validate(stamp))&#123;</span><br><span class="line"> <span class="comment">// 升级为悲观读锁</span></span><br><span class="line"> stamp = sl.readLock();</span><br><span class="line"> <span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// 读⼊⽅法局部变量</span></span><br><span class="line"> .....</span><br><span class="line"> &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line"> <span class="comment">// 释放悲观读锁</span></span><br><span class="line"> sl.unlockRead(stamp);</span><br><span class="line"> &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 使⽤⽅法局部变量执⾏业务操作</span></span><br><span class="line">......</span><br><span class="line"></span><br><span class="line"><span class="comment">// 写模板</span></span><br><span class="line"><span class="keyword">long</span> stamp = sl.writeLock();</span><br><span class="line"><span class="keyword">try</span> &#123;</span><br><span class="line"> <span class="comment">// 写共享变量</span></span><br><span class="line"> ......</span><br><span class="line">&#125; <span class="keyword">finally</span> &#123;</span><br><span class="line"> sl.unlockWrite(stamp);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;LockSupport工具类&quot;&gt;&lt;a href=&quot;#LockSupport工具类&quot; class=&quot;headerlink&quot; title=&quot;LockSupport工具类&quot;&gt;&lt;/a&gt;LockSupport工具类&lt;/h2&gt;&lt;p&gt;  LockSupport是JDk rt.j
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美学习笔记五</title>
    <link href="https://francisqiang.github.io/2019/08/16/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E4%BA%94/"/>
    <id>https://francisqiang.github.io/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记五/</id>
    <published>2019-08-16T04:52:26.000Z</published>
    <updated>2019-08-16T07:17:57.903Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Java并发包中并发List介绍"><a href="#Java并发包中并发List介绍" class="headerlink" title="Java并发包中并发List介绍"></a>Java并发包中并发List介绍</h2><p>  并发包中的List只有 <strong><em>CopyOnWriteArrayList</em></strong> 。 CopyOnWriteArrayList是一个线程安全的List，对其的修改操作都是在底层的一个复制的数组上进行的，也就是使用了 <strong><em>写时复制</em></strong> 策略。</p><p>  <img src="/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记五/CopyOnWriteArrayList.jpg" alt="运行结果"></p><p>  在CopyOnWriteArrayList中，每个对象里有一个array数组对象用来存放具体元素，<strong>ReentrantLock独占锁对象用来保证同时只有一个线程能对array进行修改</strong>。</p><p>  如果让我们自己做一个写时复制的线程安全的List我们会怎么做？我们需要考虑什么？</p><ul><li><p>何时初始化list，初始化list元素为多少个，list是有限大小吗？</p></li><li><p>如何保证线程安全？</p></li><li><p>如何保证使用迭代器遍历list时的数据一致性。</p></li></ul><h2 id="主要方法解析"><a href="#主要方法解析" class="headerlink" title="主要方法解析"></a>主要方法解析</h2><h3 id="构造方法"><a href="#构造方法" class="headerlink" title="构造方法"></a>构造方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 默认初始化个数0</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArrayList</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    setArray(<span class="keyword">new</span> Object[<span class="number">0</span>]);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 传入一个集合的时候，将集合复制到自身属性的array中</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArrayList</span><span class="params">(Collection&lt;? extends E&gt; c)</span> </span>&#123;</span><br><span class="line">    Object[] elements;</span><br><span class="line">    <span class="keyword">if</span> (c.getClass() == CopyOnWriteArrayList.class)</span><br><span class="line">        elements = ((CopyOnWriteArrayList&lt;?&gt;)c).getArray();</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        elements = c.toArray();</span><br><span class="line">        <span class="comment">// c.toArray might (incorrectly) not return Object[] (see 6260652)</span></span><br><span class="line">        <span class="keyword">if</span> (elements.getClass() != Object[].class)</span><br><span class="line">            elements = Arrays.copyOf(elements, elements.length, Object[].class);</span><br><span class="line">    &#125;</span><br><span class="line">    setArray(elements);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 传入为数组的时候直接复制</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">CopyOnWriteArrayList</span><span class="params">(E[] toCopyIn)</span> </span>&#123;</span><br><span class="line">    setArray(Arrays.copyOf(toCopyIn, toCopyIn.length, Object[].class));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="增加方法"><a href="#增加方法" class="headerlink" title="增加方法"></a>增加方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">add</span><span class="params">(E e)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 获取独占锁并加锁</span></span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 获取自身array并且复制一个快照</span></span><br><span class="line">        Object[] elements = getArray();</span><br><span class="line">        <span class="keyword">int</span> len = elements.length;</span><br><span class="line">        Object[] newElements = Arrays.copyOf(elements, len + <span class="number">1</span>);</span><br><span class="line">        <span class="comment">// 在快照上进行操作</span></span><br><span class="line">        newElements[len] = e;</span><br><span class="line">        <span class="comment">// 将新的快照赋值给array属性</span></span><br><span class="line">        setArray(newElements);</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 最后解锁</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="获取指定位置的元素"><a href="#获取指定位置的元素" class="headerlink" title="获取指定位置的元素"></a>获取指定位置的元素</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">final</span> Object[] getArray() &#123;</span><br><span class="line">    <span class="keyword">return</span> array;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line"><span class="function"><span class="keyword">private</span> E <span class="title">get</span><span class="params">(Object[] a, <span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> (E) a[index];</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">get</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> get(getArray(), index);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  获取操作并没有加锁，可能会产生 <strong><em>弱一致性问题</em></strong> 。比如当线程A执行get，线程B执行删除操作。线程A这里可以分为两个步骤 1. 获取当前array  2. 通过获取到的array根据索引获得指定元素。</p><p>  假如这个时候当线程A执行完步骤1，然后线程B执行删除操作，我们假设线程A需要获得的是index为1的元素，此时线程B将原来array复制到新的array中然后对这个新array进行remove(1)的操作，之后把这个新的array重新赋值到对象中的array中，当这一系列操作完成后，线程A获取指定元素，而这个时候线程A是通过原来array获取的，所以它仍能获取到index为1的元素。</p><p>  例如对于以下程序 运行结果可能会是2或者3</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CopyOnWriteArrayListTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    CopyOnWriteArrayList&lt;Integer&gt; copyOnWriteArrayList = <span class="keyword">new</span> CopyOnWriteArrayList&lt;&gt;();</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">1</span>);</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">2</span>);</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">3</span>);</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">4</span>);</span><br><span class="line">    Thread threadOne = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        copyOnWriteArrayList.remove(<span class="number">1</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    Thread threadTwo = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(copyOnWriteArrayList.get(<span class="number">1</span>));</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    threadOne.start();</span><br><span class="line">    threadTwo.start();</span><br><span class="line">    threadTwo.join();</span><br><span class="line">    threadOne.join();</span><br><span class="line">    System.out.println(<span class="string">"结束"</span>);</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="修改指定元素"><a href="#修改指定元素" class="headerlink" title="修改指定元素"></a>修改指定元素</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">set</span><span class="params">(<span class="keyword">int</span> index, E element)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 获取锁并加锁</span></span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 获取array</span></span><br><span class="line">        Object[] elements = getArray();</span><br><span class="line">        <span class="comment">// 获取原来的指定元素</span></span><br><span class="line">        E oldValue = get(elements, index);</span><br><span class="line">        <span class="comment">// 如果参数不和老值相同则进行更新</span></span><br><span class="line">        <span class="keyword">if</span> (oldValue != element) &#123;</span><br><span class="line">            <span class="keyword">int</span> len = elements.length;</span><br><span class="line">            <span class="comment">// 复制array</span></span><br><span class="line">            Object[] newElements = Arrays.copyOf(elements, len);</span><br><span class="line">            <span class="comment">// 在新的array中进行更新</span></span><br><span class="line">            newElements[index] = element;</span><br><span class="line">            <span class="comment">// 重新设置array</span></span><br><span class="line">            setArray(newElements);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// Not quite a no-op; ensures volatile write semantics</span></span><br><span class="line">            <span class="comment">// 确保volatile语义还是需要重新设置array</span></span><br><span class="line">            <span class="comment">// 因为对于volatile变量的写操作happens-before对volatile</span></span><br><span class="line">            <span class="comment">// 变量的读操作</span></span><br><span class="line">            setArray(elements);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="删除指定元素"><a href="#删除指定元素" class="headerlink" title="删除指定元素"></a>删除指定元素</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> E <span class="title">remove</span><span class="params">(<span class="keyword">int</span> index)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">this</span>.lock;</span><br><span class="line">    <span class="comment">// 获取锁并加锁</span></span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 进行快照的准备工作</span></span><br><span class="line">        Object[] elements = getArray();</span><br><span class="line">        <span class="keyword">int</span> len = elements.length;</span><br><span class="line">        E oldValue = get(elements, index);</span><br><span class="line">        <span class="keyword">int</span> numMoved = len - index - <span class="number">1</span>;</span><br><span class="line">        <span class="comment">// 如果删除的是最后一个</span></span><br><span class="line">        <span class="keyword">if</span> (numMoved == <span class="number">0</span>)</span><br><span class="line">            <span class="comment">// 直接移除最后一个的空间</span></span><br><span class="line">            setArray(Arrays.copyOf(elements, len - <span class="number">1</span>));</span><br><span class="line">        <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">// 创建一个快照</span></span><br><span class="line">            Object[] newElements = <span class="keyword">new</span> Object[len - <span class="number">1</span>];</span><br><span class="line">            System.arraycopy(elements, <span class="number">0</span>, newElements, <span class="number">0</span>, index);</span><br><span class="line">            System.arraycopy(elements, index + <span class="number">1</span>, newElements, index,</span><br><span class="line">                             numMoved);</span><br><span class="line">            <span class="comment">// 将array设置为快照</span></span><br><span class="line">            setArray(newElements);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> oldValue;</span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">        <span class="comment">// 释放锁</span></span><br><span class="line">        lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="弱一致性的迭代器"><a href="#弱一致性的迭代器" class="headerlink" title="弱一致性的迭代器"></a>弱一致性的迭代器</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">COWIterator</span>&lt;<span class="title">E</span>&gt; <span class="keyword">implements</span> <span class="title">ListIterator</span>&lt;<span class="title">E</span>&gt; </span>&#123;</span><br><span class="line">    <span class="comment">// 数组的快照</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">final</span> Object[] snapshot;</span><br><span class="line">    <span class="comment">/** Index of element to be returned by subsequent call to next.  */</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">int</span> cursor;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="title">COWIterator</span><span class="params">(Object[] elements, <span class="keyword">int</span> initialCursor)</span> </span>&#123;</span><br><span class="line">        cursor = initialCursor;</span><br><span class="line">        snapshot = elements;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断是否还有下一个元素</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasNext</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> cursor &lt; snapshot.length;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">hasPrevious</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> cursor &gt; <span class="number">0</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 遍历的方法</span></span><br><span class="line">    <span class="meta">@SuppressWarnings</span>(<span class="string">"unchecked"</span>)</span><br><span class="line">    <span class="function"><span class="keyword">public</span> E <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span> (! hasNext())</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> NoSuchElementException();</span><br><span class="line">        <span class="keyword">return</span> (E) snapshot[cursor++];</span><br><span class="line">    &#125;</span><br><span class="line">    。。。。。</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  在snapshot中保存着当前array的快照,也就是说迭代器所操作的是array的一个快照，所以当其他线程对array进行更改的时候，迭代器是感知不到的，这又会产生弱一致性问题。</p><p>  我们来看一下实例</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">CopyOnWriteArrayListTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> <span class="keyword">throws</span> InterruptedException </span>&#123;</span><br><span class="line">    CopyOnWriteArrayList&lt;Integer&gt; copyOnWriteArrayList = <span class="keyword">new</span> CopyOnWriteArrayList&lt;&gt;();</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">1</span>);</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">2</span>);</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">3</span>);</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">4</span>);</span><br><span class="line">    copyOnWriteArrayList.add(<span class="number">5</span>);</span><br><span class="line"></span><br><span class="line">    Thread threadOne = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runnable() &#123;</span><br><span class="line">      <span class="meta">@Override</span></span><br><span class="line">      <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        copyOnWriteArrayList.set(<span class="number">1</span>,<span class="number">123</span>);</span><br><span class="line">        copyOnWriteArrayList.remove(<span class="number">2</span>);</span><br><span class="line">        copyOnWriteArrayList.remove(<span class="number">3</span>);</span><br><span class="line">      &#125;</span><br><span class="line">    &#125;);</span><br><span class="line">    <span class="comment">// 保证iterator先获取</span></span><br><span class="line">    Iterator&lt;Integer&gt; integerIterator = copyOnWriteArrayList.iterator();</span><br><span class="line"></span><br><span class="line">    threadOne.start();</span><br><span class="line"></span><br><span class="line">    threadOne.join();</span><br><span class="line"></span><br><span class="line">    <span class="keyword">while</span> (integerIterator.hasNext()) &#123;</span><br><span class="line">      System.out.println(integerIterator.next());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  <img src="/2019/08/16/Java并发编程学习——Java并发编程之美学习笔记五/1.jpg" alt="运行结果"></p><p>  我们可以看到运行结果是1 2 3 4 5</p><p>  我们来看一下创建iterator的源码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> Iterator&lt;E&gt; <span class="title">iterator</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> COWIterator&lt;E&gt;(getArray(), <span class="number">0</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">COWIterator</span><span class="params">(Object[] elements, <span class="keyword">int</span> initialCursor)</span> </span>&#123;</span><br><span class="line">    cursor = initialCursor;</span><br><span class="line">    snapshot = elements;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  其实跟上面讲到的获取和删除的弱一致性一样，因为修改和删除操作时对一个快照进行修改和删除的，并且最终把新的快照地址重新赋值给array，虽然此时的array指向的地址已经变了，但是原来迭代器中获取array的地址并没有改变，所以迭代器操作的还是原来的array，这就是写时复制产生的弱一致性问题。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Java并发包中并发List介绍&quot;&gt;&lt;a href=&quot;#Java并发包中并发List介绍&quot; class=&quot;headerlink&quot; title=&quot;Java并发包中并发List介绍&quot;&gt;&lt;/a&gt;Java并发包中并发List介绍&lt;/h2&gt;&lt;p&gt;  并发包中的List只有 
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美学习笔记四</title>
    <link href="https://francisqiang.github.io/2019/08/14/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E5%AD%A6%E4%B9%A0%E7%AC%94%E8%AE%B0%E5%9B%9B/"/>
    <id>https://francisqiang.github.io/2019/08/14/Java并发编程学习——Java并发编程之美学习笔记四/</id>
    <published>2019-08-14T01:36:40.000Z</published>
    <updated>2019-08-16T06:57:09.127Z</updated>
    
    <content type="html"><![CDATA[<h2 id="原子变量操作类"><a href="#原子变量操作类" class="headerlink" title="原子变量操作类"></a>原子变量操作类</h2><p>  在JUC并发包中有很多原子变量类，比如AtomicInteger，AtomicLong和AtomicBoolean等。他们原理类似，这里讲解AtomicLong类，AtomicLong是原子性递增和递减类，其内部使用Unsafe实现。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">AtomicLong</span> <span class="keyword">extends</span> <span class="title">Number</span> <span class="keyword">implements</span> <span class="title">java</span>.<span class="title">io</span>.<span class="title">Serializable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">1927816293512124184L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取unsafe实例</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> Unsafe unsafe = Unsafe.getUnsafe();</span><br><span class="line">    <span class="comment">// 声明偏移量</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> valueOffset;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 判断JVM是否支持long类型无锁CAS</span></span><br><span class="line">    <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">boolean</span> VM_SUPPORTS_LONG_CAS = VMSupportsCS8();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">native</span> <span class="keyword">boolean</span> <span class="title">VMSupportsCS8</span><span class="params">()</span></span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 获取value在Atomica中的偏移量</span></span><br><span class="line">            valueOffset = unsafe.objectFieldOffset</span><br><span class="line">                (AtomicLong.class.getDeclaredField(<span class="string">"value"</span>));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception ex) &#123; <span class="keyword">throw</span> <span class="keyword">new</span> Error(ex); &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 实际的变量值</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">volatile</span> <span class="keyword">long</span> value;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 初始化</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AtomicLong</span><span class="params">(<span class="keyword">long</span> initialValue)</span> </span>&#123;</span><br><span class="line">        value = initialValue;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">AtomicLong</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">get</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> value;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 设置</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(<span class="keyword">long</span> newValue)</span> </span>&#123;</span><br><span class="line">        value = newValue;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">lazySet</span><span class="params">(<span class="keyword">long</span> newValue)</span> </span>&#123;</span><br><span class="line">        unsafe.putOrderedLong(<span class="keyword">this</span>, valueOffset, newValue);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 获取并设置long值，返回原来long的值</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">getAndSet</span><span class="params">(<span class="keyword">long</span> newValue)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.getAndSetLong(<span class="keyword">this</span>, valueOffset, newValue);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 原子性设置值为被给予的更新值如果当前值与参数中到的expect期望值相等的话</span></span><br><span class="line"><span class="comment">     * Atomically sets the value to the given updated value</span></span><br><span class="line"><span class="comment">     * if the current value &#123;<span class="doctag">@code</span> ==&#125; the expected value.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> expect the expected value</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> update the new value</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> &#123;<span class="doctag">@code</span> true&#125; if successful. False return indicates that</span></span><br><span class="line"><span class="comment">     * the actual value was not equal to the expected value.</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">compareAndSet</span><span class="params">(<span class="keyword">long</span> expect, <span class="keyword">long</span> update)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.compareAndSwapLong(<span class="keyword">this</span>, valueOffset, expect, update);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 和上面方法一样 但是可能会失败而且不提供保证 一般很少代替上面的方法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">weakCompareAndSet</span><span class="params">(<span class="keyword">long</span> expect, <span class="keyword">long</span> update)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.compareAndSwapLong(<span class="keyword">this</span>, valueOffset, expect, update);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 原子性增加1</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">getAndIncrement</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.getAndAddLong(<span class="keyword">this</span>, valueOffset, <span class="number">1L</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 原子性减1</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">getAndDecrement</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.getAndAddLong(<span class="keyword">this</span>, valueOffset, -<span class="number">1L</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 原子性增加指定的值</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">getAndAdd</span><span class="params">(<span class="keyword">long</span> delta)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.getAndAddLong(<span class="keyword">this</span>, valueOffset, delta);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 增加1且返回更新后的值</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">incrementAndGet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.getAndAddLong(<span class="keyword">this</span>, valueOffset, <span class="number">1L</span>) + <span class="number">1L</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 原子性减1且返回更新后的值</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">decrementAndGet</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.getAndAddLong(<span class="keyword">this</span>, valueOffset, -<span class="number">1L</span>) - <span class="number">1L</span>;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">addAndGet</span><span class="params">(<span class="keyword">long</span> delta)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> unsafe.getAndAddLong(<span class="keyword">this</span>, valueOffset, delta) + delta;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 原子性更新循坏使用CAS 导致线程竞争</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">getAndUpdate</span><span class="params">(LongUnaryOperator updateFunction)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> prev, next;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            prev = get();</span><br><span class="line">            next = updateFunction.applyAsLong(prev);</span><br><span class="line">        &#125; <span class="keyword">while</span> (!compareAndSet(prev, next));</span><br><span class="line">        <span class="keyword">return</span> prev;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">updateAndGet</span><span class="params">(LongUnaryOperator updateFunction)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> prev, next;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            prev = get();</span><br><span class="line">            next = updateFunction.applyAsLong(prev);</span><br><span class="line">        &#125; <span class="keyword">while</span> (!compareAndSet(prev, next));</span><br><span class="line">        <span class="keyword">return</span> next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 传入函数 自定义算法</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">getAndAccumulate</span><span class="params">(<span class="keyword">long</span> x,</span></span></span><br><span class="line"><span class="function"><span class="params">                                       LongBinaryOperator accumulatorFunction)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> prev, next;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            prev = get();</span><br><span class="line">            next = accumulatorFunction.applyAsLong(prev, x);</span><br><span class="line">        &#125; <span class="keyword">while</span> (!compareAndSet(prev, next));</span><br><span class="line">        <span class="keyword">return</span> prev;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">final</span> <span class="keyword">long</span> <span class="title">accumulateAndGet</span><span class="params">(<span class="keyword">long</span> x,</span></span></span><br><span class="line"><span class="function"><span class="params">                                       LongBinaryOperator accumulatorFunction)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">long</span> prev, next;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            prev = get();</span><br><span class="line">            next = accumulatorFunction.applyAsLong(prev, x);</span><br><span class="line">        &#125; <span class="keyword">while</span> (!compareAndSet(prev, next));</span><br><span class="line">        <span class="keyword">return</span> next;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Long.toString(get());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">intValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">longValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">floatValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">float</span>)get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">doubleValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">double</span>)get();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  在这里AtomicLong类可以直接获取Unsafe是因为它本身是在rt.jar下面的是通过Bootstrap类加载器加载的。</p><p>  上面的原子操作类都是使用的CAS非阻塞算法，性能更好，但是在高并发的情况下Atomicxxx还存放着性能问题(会导致线程一直竞争CAS，导致大量资源浪费)，在JDK1.8之后提供了高并发下的LongAdder类。</p><h2 id="JDK8新增的原子操作类LongAdder"><a href="#JDK8新增的原子操作类LongAdder" class="headerlink" title="JDK8新增的原子操作类LongAdder"></a>JDK8新增的原子操作类LongAdder</h2><p>  在AtomicLong中，在高并发的情况下大量线程会同时竞争更新同一个原子变量，这样会导致大量线程失败通过无线循环不断进行自旋尝试CAS的操作，这回白白浪费CPU资源。</p><p>  因为AtomicLong是多个线程竞争同一个原子变量，而LongAdder中则将原子变量和线程一一对应，比如设置一个Cell数组，其中的元素对应着每个线程(通过一定的算法实现)，然后最后获取值的时候将base(基础值)和cell数组中的元素值相加。</p><p>  <strong>LongAdder维护了一个延迟初始化的原子性更新数组(默认情况为null)和一个基础值base。由于cell占用的内存相对较大所以是在需要的时候创建，即惰性加载</strong>。</p><p>  因为cell是一个数组，数组中的元素内存地址是连续的，这就很容易导致伪共享的问题，在LongAdder中使用了@Contented注解来避免。</p><h3 id="LongAdder代码分析"><a href="#LongAdder代码分析" class="headerlink" title="LongAdder代码分析"></a>LongAdder代码分析</h3><p>  我们围绕着6个问题去分析源码</p><ol><li><p>LongAdder的结构是怎样的  答：继承了Striped64里面有一个base cell数组和cellBusy自旋操作标志</p></li><li><p>当前线程应该访问Cell数组里面的哪一个Cell元素  答：获取当前线程的探针(作为每个线程对应哪一个cell的算法基础)，根据当前线程的随机数ThreadLocalRandomProbe和cell元素个数计算当前要访问的cell元素的下标，如果发现对应下标的元素为空则新增一个Cell元素到数组中并在之前将cellBusy设置为1防止其他线程竞争</p></li><li><p>如何初始化Cell数组  答：懒加载 但需要操作的时候进行初始化操作</p></li><li><p>Cell数组如何进行扩容  答：当CPU个数大于cell元素进行扩容 这时会多个CPU(线程)争抢一个cell元素产生冲突</p></li><li><p>线程访问分配的Cell元素后有冲突后应该如何处理  答：进行扩容操作</p></li><li><p>如何保证线程操作被分配的Cell元素的原子性  答：使用volatile保证内存可见性，使用cas操作保证操作原子性</p><p><img src="/2019/08/14/Java并发编程学习——Java并发编程之美学习笔记四/1.jpg" alt="LongAdder"></p><p>我们可以看到LongAdder继承Striped64，而Striped64中维持着base，cellBusy，cell三个变量。</p><p>base是用来计算LongAdder的真实值的(base和cell元素相加),cellBusy是用来实现自旋锁的，状态值只有0和1，当创建Cell元素，扩容Cell数组或者初始化Cell数组的时候使用CAS操作该变量来保证同时只有一个线程可以进行其中之一的操作。</p><p>我们来看一下Cell的构造</p></li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">@sun</span>.misc.Contended <span class="keyword">static</span> <span class="keyword">final</span> <span class="class"><span class="keyword">class</span> <span class="title">Cell</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 维持着一个long,保证内存可见性声明为volatile</span></span><br><span class="line">    <span class="keyword">volatile</span> <span class="keyword">long</span> value;</span><br><span class="line">    Cell(<span class="keyword">long</span> x) &#123; value = x; &#125;</span><br><span class="line">    <span class="comment">// 进行cas操作，是更新时原子性的</span></span><br><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">boolean</span> <span class="title">cas</span><span class="params">(<span class="keyword">long</span> cmp, <span class="keyword">long</span> val)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> UNSAFE.compareAndSwapLong(<span class="keyword">this</span>, valueOffset, cmp, val);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Unsafe mechanics</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> sun.misc.Unsafe UNSAFE;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> valueOffset;</span><br><span class="line">    <span class="keyword">static</span> &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            UNSAFE = sun.misc.Unsafe.getUnsafe();</span><br><span class="line">            Class&lt;?&gt; ak = Cell.class;</span><br><span class="line">            valueOffset = UNSAFE.objectFieldOffset</span><br><span class="line">                (ak.getDeclaredField(<span class="string">"value"</span>));</span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> Error(e);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们来看一下LongAdder源码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 继承了Striped64</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">LongAdder</span> <span class="keyword">extends</span> <span class="title">Striped64</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">7249069246863182397L</span>;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">LongAdder</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 增加指定的值 主要操作</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">long</span> x)</span> </span>&#123;</span><br><span class="line">        Cell[] as; <span class="keyword">long</span> b, v; <span class="keyword">int</span> m; Cell a;</span><br><span class="line">        <span class="comment">// 如果数组为空且cas失败会进入</span></span><br><span class="line">        <span class="comment">// 或者数组不为空并且没有cas操作的时候进入</span></span><br><span class="line">        <span class="keyword">if</span> ((as = cells) != <span class="keyword">null</span> || !casBase(b = base, b + x)) &#123;</span><br><span class="line">            <span class="keyword">boolean</span> uncontended = <span class="keyword">true</span>;</span><br><span class="line">            <span class="comment">// 当数组为空直接进入 这里就是数组为空且刚刚cas失败进入</span></span><br><span class="line">            <span class="comment">// 或者c数组不为空但再次cas失败则进入调用longAccumulate</span></span><br><span class="line">            <span class="keyword">if</span> (as == <span class="keyword">null</span> || (m = as.length - <span class="number">1</span>) &lt; <span class="number">0</span> ||</span><br><span class="line">                (a = as[getProbe() &amp; m]) == <span class="keyword">null</span> ||</span><br><span class="line">                !(uncontended = a.cas(v = a.value, v + x)))</span><br><span class="line">                longAccumulate(x, <span class="keyword">null</span>, uncontended);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 增加1</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">increment</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        add(<span class="number">1L</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 减少1</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">decrement</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        add(-<span class="number">1L</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算所有的值即获取真正的value</span></span><br><span class="line">    <span class="comment">// 没有做加锁操作 所以并不是原子性的 会存在并发问题</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">sum</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Cell[] as = cells; Cell a;</span><br><span class="line">        <span class="keyword">long</span> sum = base;</span><br><span class="line">        <span class="keyword">if</span> (as != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; as.length; ++i) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((a = as[i]) != <span class="keyword">null</span>)</span><br><span class="line">                    sum += a.value;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 重置所有元素为0</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">reset</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Cell[] as = cells; Cell a;</span><br><span class="line">        base = <span class="number">0L</span>;</span><br><span class="line">        <span class="keyword">if</span> (as != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; as.length; ++i) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((a = as[i]) != <span class="keyword">null</span>)</span><br><span class="line">                    a.value = <span class="number">0L</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 计算并重置</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">sumThenReset</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Cell[] as = cells; Cell a;</span><br><span class="line">        <span class="keyword">long</span> sum = base;</span><br><span class="line">        base = <span class="number">0L</span>;</span><br><span class="line">        <span class="keyword">if</span> (as != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; as.length; ++i) &#123;</span><br><span class="line">                <span class="keyword">if</span> ((a = as[i]) != <span class="keyword">null</span>) &#123;</span><br><span class="line">                    sum += a.value;</span><br><span class="line">                    a.value = <span class="number">0L</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">return</span> sum;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">toString</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> Long.toString(sum());</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">long</span> <span class="title">longValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> sum();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">intValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">int</span>)sum();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">float</span> <span class="title">floatValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">float</span>)sum();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">doubleValue</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> (<span class="keyword">double</span>)sum();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">static</span> <span class="class"><span class="keyword">class</span> <span class="title">SerializationProxy</span> <span class="keyword">implements</span> <span class="title">Serializable</span> </span>&#123;</span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> serialVersionUID = <span class="number">7249069246863182397L</span>;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * The current value returned by sum().</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@serial</span></span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="keyword">private</span> <span class="keyword">final</span> <span class="keyword">long</span> value;</span><br><span class="line"></span><br><span class="line">        SerializationProxy(LongAdder a) &#123;</span><br><span class="line">            value = a.sum();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * Return a &#123;<span class="doctag">@code</span> LongAdder&#125; object with initial state</span></span><br><span class="line"><span class="comment">         * held by this proxy.</span></span><br><span class="line"><span class="comment">         *</span></span><br><span class="line"><span class="comment">         * <span class="doctag">@return</span> a &#123;<span class="doctag">@code</span> LongAdder&#125; object with initial state</span></span><br><span class="line"><span class="comment">         * held by this proxy.</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">        <span class="function"><span class="keyword">private</span> Object <span class="title">readResolve</span><span class="params">()</span> </span>&#123;</span><br><span class="line">            LongAdder a = <span class="keyword">new</span> LongAdder();</span><br><span class="line">            a.base = value;</span><br><span class="line">            <span class="keyword">return</span> a;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> Object <span class="title">writeReplace</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">new</span> SerializationProxy(<span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">readObject</span><span class="params">(java.io.ObjectInputStream s)</span></span></span><br><span class="line"><span class="function">        <span class="keyword">throws</span> java.io.InvalidObjectException </span>&#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> java.io.InvalidObjectException(<span class="string">"Proxy required"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们来看一下longAccumulate函数源码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br></pre></td><td class="code"><pre><span class="line">    <span class="function"><span class="keyword">final</span> <span class="keyword">void</span> <span class="title">longAccumulate</span><span class="params">(<span class="keyword">long</span> x, LongBinaryOperator fn,</span></span></span><br><span class="line"><span class="function"><span class="params">                              <span class="keyword">boolean</span> wasUncontended)</span> </span>&#123;</span><br><span class="line">        <span class="comment">// 获取当前线程的探针(作为每个线程对应哪一个cell的算法基础)</span></span><br><span class="line">        <span class="keyword">int</span> h;</span><br><span class="line">        <span class="keyword">if</span> ((h = getProbe()) == <span class="number">0</span>) &#123;</span><br><span class="line">            ThreadLocalRandom.current(); <span class="comment">// force initialization</span></span><br><span class="line">            h = getProbe();</span><br><span class="line">            wasUncontended = <span class="keyword">true</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">boolean</span> collide = <span class="keyword">false</span>;                <span class="comment">// True if last slot nonempty</span></span><br><span class="line">        <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">            Cell[] as; Cell a; <span class="keyword">int</span> n; <span class="keyword">long</span> v;</span><br><span class="line">            <span class="comment">// 如果cell不为空</span></span><br><span class="line">            <span class="keyword">if</span> ((as = cells) != <span class="keyword">null</span> &amp;&amp; (n = as.length) &gt; <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// 根据当前线程的随机数ThreadLocalRandomProbe和cell元素个数计算当前要访问的cell元素的下标，如果发现对应下标的元素为空则新增一个Cell元素到数组中并在之前将cellBusy设置为1防止其他线程竞争</span></span><br><span class="line">                <span class="keyword">if</span> ((a = as[(n - <span class="number">1</span>) &amp; h]) == <span class="keyword">null</span>) &#123;</span><br><span class="line">                    <span class="keyword">if</span> (cellsBusy == <span class="number">0</span>) &#123;       <span class="comment">// Try to attach new Cell</span></span><br><span class="line">                        Cell r = <span class="keyword">new</span> Cell(x);   <span class="comment">// Optimistically create</span></span><br><span class="line">                        <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">                            <span class="keyword">boolean</span> created = <span class="keyword">false</span>;</span><br><span class="line">                            <span class="keyword">try</span> &#123;               <span class="comment">// Recheck under lock</span></span><br><span class="line">                                Cell[] rs; <span class="keyword">int</span> m, j;</span><br><span class="line">                                <span class="keyword">if</span> ((rs = cells) != <span class="keyword">null</span> &amp;&amp;</span><br><span class="line">                                    (m = rs.length) &gt; <span class="number">0</span> &amp;&amp;</span><br><span class="line">                                    rs[j = (m - <span class="number">1</span>) &amp; h] == <span class="keyword">null</span>) &#123;</span><br><span class="line">                                    rs[j] = r;</span><br><span class="line">                                    created = <span class="keyword">true</span>;</span><br><span class="line">                                &#125;</span><br><span class="line">                            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                                cellsBusy = <span class="number">0</span>;</span><br><span class="line">                            &#125;</span><br><span class="line">                            <span class="keyword">if</span> (created)</span><br><span class="line">                                <span class="keyword">break</span>;</span><br><span class="line">                            <span class="keyword">continue</span>;           <span class="comment">// Slot is now non-empty</span></span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125;</span><br><span class="line">                    collide = <span class="keyword">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="comment">// 如果cas已经知道失败则重置标志</span></span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (!wasUncontended)       <span class="comment">// CAS already known to fail</span></span><br><span class="line">                    wasUncontended = <span class="keyword">true</span>;      <span class="comment">// Continue after rehash</span></span><br><span class="line">                <span class="comment">// 进行cas操作成功直接返回</span></span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (a.cas(v = a.value, ((fn == <span class="keyword">null</span>) ? v + x :</span><br><span class="line">                                             fn.applyAsLong(v, x))))</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                <span class="comment">// 如果cell元素个数大于CPU个数会产生冲突 冲突则进行扩容操作</span></span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (n &gt;= NCPU || cells != as)</span><br><span class="line">                    collide = <span class="keyword">false</span>;            <span class="comment">// At max size or stale</span></span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (!collide)</span><br><span class="line">                    collide = <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">else</span> <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">                    <span class="keyword">try</span> &#123;</span><br><span class="line">                        <span class="keyword">if</span> (cells == as) &#123;      <span class="comment">// Expand table unless stale</span></span><br><span class="line">                            <span class="comment">// 移位增加两倍</span></span><br><span class="line">                            Cell[] rs = <span class="keyword">new</span> Cell[n &lt;&lt; <span class="number">1</span>];</span><br><span class="line">                            <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; n; ++i)</span><br><span class="line">                                rs[i] = as[i];</span><br><span class="line">                            cells = rs;</span><br><span class="line">                        &#125;</span><br><span class="line">                    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                        <span class="comment">// 扩容的时候进行cas操作避免其他线程进行扩容或者更新操作完成后设置cellBusy为0</span></span><br><span class="line">                        cellsBusy = <span class="number">0</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                    collide = <span class="keyword">false</span>;</span><br><span class="line">                    <span class="keyword">continue</span>;                   <span class="comment">// Retry with expanded table</span></span><br><span class="line">                &#125;</span><br><span class="line">                h = advanceProbe(h);</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="comment">// 如果为空则进行初始化</span></span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (cellsBusy == <span class="number">0</span> &amp;&amp; cells == as &amp;&amp; casCellsBusy()) &#123;</span><br><span class="line">                <span class="keyword">boolean</span> init = <span class="keyword">false</span>;</span><br><span class="line">                <span class="keyword">try</span> &#123;                           <span class="comment">// Initialize table</span></span><br><span class="line">                    <span class="comment">// 初始化</span></span><br><span class="line">                    <span class="keyword">if</span> (cells == as) &#123;</span><br><span class="line">                        Cell[] rs = <span class="keyword">new</span> Cell[<span class="number">2</span>];</span><br><span class="line">                        rs[h &amp; <span class="number">1</span>] = <span class="keyword">new</span> Cell(x);</span><br><span class="line">                        cells = rs;</span><br><span class="line">                        init = <span class="keyword">true</span>;</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">                    cellsBusy = <span class="number">0</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (init)</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">            &#125;</span><br><span class="line">            <span class="keyword">else</span> <span class="keyword">if</span> (casBase(v = base, ((fn == <span class="keyword">null</span>) ? v + x :</span><br><span class="line">                                        fn.applyAsLong(v, x))))</span><br><span class="line">                <span class="keyword">break</span>;                          <span class="comment">// Fall back on using base</span></span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">```  </span><br><span class="line"></span><br><span class="line">## LongAccumulator类原理探究</span><br><span class="line"></span><br><span class="line">  LongAccumulator比LongAdder更加强大，原因在于LongAdder中只有累加操作，而LongAccumulator中是自定义函数来实现的。我们来看一下LongAccumulator的accumulate函数和LongAdder中的add方法的区别</span><br><span class="line"></span><br><span class="line">```java</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">accumulate</span><span class="params">(<span class="keyword">long</span> x)</span> </span>&#123;</span><br><span class="line">        Cell[] as; <span class="keyword">long</span> b, v, r; <span class="keyword">int</span> m; Cell a;</span><br><span class="line">        <span class="keyword">if</span> ((as = cells) != <span class="keyword">null</span> ||</span><br><span class="line">            (r = function.applyAsLong(b = base, x)) != b &amp;&amp; !casBase(b, r)) &#123;</span><br><span class="line">            <span class="keyword">boolean</span> uncontended = <span class="keyword">true</span>;</span><br><span class="line">            <span class="keyword">if</span> (as == <span class="keyword">null</span> || (m = as.length - <span class="number">1</span>) &lt; <span class="number">0</span> ||</span><br><span class="line">                (a = as[getProbe() &amp; m]) == <span class="keyword">null</span> ||</span><br><span class="line">                !(uncontended =</span><br><span class="line">                  (r = function.applyAsLong(v = a.value, x)) == v ||</span><br><span class="line">                  a.cas(v, r)))</span><br><span class="line">                <span class="comment">// 这里传入了计算的值和一个函数</span></span><br><span class="line">                longAccumulate(x, function, uncontended);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">add</span><span class="params">(<span class="keyword">long</span> x)</span> </span>&#123;</span><br><span class="line">        Cell[] as; <span class="keyword">long</span> b, v; <span class="keyword">int</span> m; Cell a;</span><br><span class="line">        <span class="keyword">if</span> ((as = cells) != <span class="keyword">null</span> || !casBase(b = base, b + x)) &#123;</span><br><span class="line">            <span class="keyword">boolean</span> uncontended = <span class="keyword">true</span>;</span><br><span class="line">            <span class="keyword">if</span> (as == <span class="keyword">null</span> || (m = as.length - <span class="number">1</span>) &lt; <span class="number">0</span> ||</span><br><span class="line">                (a = as[getProbe() &amp; m]) == <span class="keyword">null</span> ||</span><br><span class="line">                !(uncontended = a.cas(v = a.value, v + x)))</span><br><span class="line">                <span class="comment">// 传入了值和一个null 我们可以猜测 传入null的时候给我们默认进行增加操作了</span></span><br><span class="line">                longAccumulate(x, <span class="keyword">null</span>, uncontended);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br></pre></td></tr></table></figure><p>  我们继续回顾一下刚刚上面的longAccumulate方法源码中最后一个else if</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 很明显 当fn为null的时候使用了v + x操作 不然调用该fn的applyAsLong</span></span><br><span class="line"><span class="comment">// 我们可以继续猜测这里面进行了自定义</span></span><br><span class="line"> <span class="keyword">else</span> <span class="keyword">if</span> (casBase(v = base, ((fn == <span class="keyword">null</span>) ? v + x :</span><br><span class="line">    fn.applyAsLong(v, x))))</span><br><span class="line">    <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这就是一个函数接口</span></span><br><span class="line"><span class="meta">@FunctionalInterface</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">interface</span> <span class="title">LongBinaryOperator</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * Applies this operator to the given operands.</span></span><br><span class="line"><span class="comment">     *</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> left the first operand</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@param</span> right the second operand</span></span><br><span class="line"><span class="comment">     * <span class="doctag">@return</span> the operator result</span></span><br><span class="line"><span class="comment">     */</span></span><br><span class="line">    <span class="function"><span class="keyword">long</span> <span class="title">applyAsLong</span><span class="params">(<span class="keyword">long</span> left, <span class="keyword">long</span> right)</span></span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;原子变量操作类&quot;&gt;&lt;a href=&quot;#原子变量操作类&quot; class=&quot;headerlink&quot; title=&quot;原子变量操作类&quot;&gt;&lt;/a&gt;原子变量操作类&lt;/h2&gt;&lt;p&gt;  在JUC并发包中有很多原子变量类，比如AtomicInteger，AtomicLong和Atom
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美读书笔记三</title>
    <link href="https://francisqiang.github.io/2019/08/13/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B8%89/"/>
    <id>https://francisqiang.github.io/2019/08/13/Java并发编程学习——Java并发编程之美读书笔记三/</id>
    <published>2019-08-13T06:51:26.000Z</published>
    <updated>2019-08-13T08:06:20.623Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Random类及其局限性"><a href="#Random类及其局限性" class="headerlink" title="Random类及其局限性"></a>Random类及其局限性</h2><p>  在JDK7之前包括现在Random都是使用比较广泛的随机数生成工具。在java.lang.Math中随机数生成也是使用的java.util.Random的实例。</p><p>  下面是Random的一种常见的使用方式。</p><p>  基本步骤就是生成一个Random实例，然后通过这个实例的方法去生成随机数字。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RandomTest</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    Random random = <span class="keyword">new</span> Random();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">5</span>; i++) &#123;</span><br><span class="line">      System.out.println(random.nextInt(<span class="number">10</span>));</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  随机数的生成需要一个默认的<em>种子</em>，这个种子是一个long类型的数字。我们可以查看一下Random的源码。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 无参构造函数其实是通过当前时间生成long类型的种子的</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Random</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 调用的是有参构造函数</span></span><br><span class="line">    <span class="keyword">this</span>(seedUniquifier() ^ System.nanoTime());</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Random</span><span class="params">(<span class="keyword">long</span> seed)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (getClass() == Random.class)</span><br><span class="line">        <span class="keyword">this</span>.seed = <span class="keyword">new</span> AtomicLong(initialScramble(seed));</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="comment">// subclass might have overriden setSeed</span></span><br><span class="line">        <span class="keyword">this</span>.seed = <span class="keyword">new</span> AtomicLong();</span><br><span class="line">        setSeed(seed);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们来看一下nextInt()方法的源码</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 无参nextInt方法调用了next方法</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">nextInt</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> next(<span class="number">32</span>);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">int</span> <span class="title">next</span><span class="params">(<span class="keyword">int</span> bits)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">long</span> oldseed, nextseed;</span><br><span class="line">    <span class="comment">// 获取当前的seed</span></span><br><span class="line">    AtomicLong seed = <span class="keyword">this</span>.seed;</span><br><span class="line">    <span class="keyword">do</span> &#123;</span><br><span class="line">        oldseed = seed.get();</span><br><span class="line">        <span class="comment">// 通过旧种子生成新种子</span></span><br><span class="line">        nextseed = (oldseed * multiplier + addend) &amp; mask;</span><br><span class="line">        <span class="comment">// CAS操作更新种子</span></span><br><span class="line">    &#125; <span class="keyword">while</span> (!seed.compareAndSet(oldseed, nextseed));</span><br><span class="line">    <span class="keyword">return</span> (<span class="keyword">int</span>)(nextseed &gt;&gt;&gt; (<span class="number">48</span> - bits));</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">nextInt</span><span class="params">(<span class="keyword">int</span> bound)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 参数校验</span></span><br><span class="line">    <span class="keyword">if</span> (bound &lt;= <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(BadBound);</span><br><span class="line">    <span class="comment">// 还是调用的next方法</span></span><br><span class="line">    <span class="comment">// 后面通过计算控制范围</span></span><br><span class="line">    <span class="keyword">int</span> r = next(<span class="number">31</span>);</span><br><span class="line">    <span class="keyword">int</span> m = bound - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> ((bound &amp; m) == <span class="number">0</span>)  <span class="comment">// i.e., bound is a power of 2</span></span><br><span class="line">        r = (<span class="keyword">int</span>)((bound * (<span class="keyword">long</span>)r) &gt;&gt; <span class="number">31</span>);</span><br><span class="line">    <span class="keyword">else</span> &#123;</span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> u = r;</span><br><span class="line">             u - (r = u % bound) + m &lt; <span class="number">0</span>;</span><br><span class="line">             u = next(<span class="number">31</span>))</span><br><span class="line">            ;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  看了以上的代码，我们先不管种子是否是原子变量，如果多个线程去调用这个随机方法获得种子然后生成随机数，因为方法里新种子的生成依赖于旧种子，而旧种子是存放在共享变量里的，这里就会导致线程不安全问题，如果线程1生成一个种子，然后线程2和线程3同时调用该方法然后生成新种子，这个时候两个线程调用的旧种子是一样的，又因为旧种子变成新种子的算法是固定的，所以这两个线程得到的是同一个新种子，那么就意味着他们会生成同样的随机数。</p><p>  而我们要注意的是在next方法里seed是被声明成AtomicLong类型的，它是原子变量，所以这样就可以解决线程安全的问题了(同一时刻只有一个线程能对这个原子变量进行操作)，后面原子变量的更新操作使用的是CAS操作，同一时刻只有一个线程能更新成功，这样就会 <strong><em>导致大量线程自旋重试</em></strong> ，这样就极大地降低了并发性能。</p><h2 id="ThreadLocalRandom"><a href="#ThreadLocalRandom" class="headerlink" title="ThreadLocalRandom"></a>ThreadLocalRandom</h2><p>  ThreadLocalRandom很好的解决了Random在高并发场景下的缺陷和不足。与ThreadLocal的原理一样ThreadLocalRandom使用的也是 <strong><em>线程封闭技术</em></strong> 。</p><p>  使用方式和Random差不多。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">RandomTest</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    ThreadLocalRandom threadLocalRandom = ThreadLocalRandom.current();</span><br><span class="line">    <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;</span><br><span class="line">      System.out.println(threadLocalRandom.nextInt(<span class="number">10</span>));</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  因为在Random中种子是共享变量，所以在多线程环境下会出现线程安全问题。而ThreadLocalRandom则是把种子变为线程本地变量。这样每个线程就会通过自己线程里的旧种子去更新种子。</p><h2 id="ThreadLocalRandom源码分析"><a href="#ThreadLocalRandom源码分析" class="headerlink" title="ThreadLocalRandom源码分析"></a>ThreadLocalRandom源码分析</h2><p>  <img src="/2019/08/13/Java并发编程学习——Java并发编程之美读书笔记三/1.jpg" alt="ThreadLocalRandom源码"></p><p>  我们可以发现ThreadLocalRandom是继承了Random类的，<strong>但是需要注意的是ThreadLocalRandom并没有使用Random的seed变量，具体的变量存放在Thread中的ThreadLocalRandomSeed中(存放在线程中)。当调用ThreadLocalRandom的nextInt方法的时候，会获取当前线程的ThreadLocalRandomSeed变量并通过这个种子更新种子然后使用新种子来随机生成数字</strong>。</p><p>  其中ThreadLocalRandom中的seeder和probeGenerator是两个原子性变量，在初始化调用线程的种子和探针的时候会用到他们，<em>每个线程只会调用一次</em>。</p><p>  另外instance是ThreadLocalRandom的一个实例而且是static的，也就是说多个线程获取的是同一个实例，但是因为种子是存放在线程中的，所以不会产生安全问题。</p><h3 id="Unsafe机制"><a href="#Unsafe机制" class="headerlink" title="Unsafe机制"></a>Unsafe机制</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> sun.misc.Unsafe UNSAFE;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> SEED;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> PROBE;</span><br><span class="line"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">long</span> SECONDARY;</span><br><span class="line"><span class="keyword">static</span> &#123;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        <span class="comment">// 获取Unsafe实例</span></span><br><span class="line">        UNSAFE = sun.misc.Unsafe.getUnsafe();</span><br><span class="line">        Class&lt;?&gt; tk = Thread.class;</span><br><span class="line">        <span class="comment">// 获取threadLocalRandomSeed，threadLocalRandomProbe，</span></span><br><span class="line">        <span class="comment">// threadLocalRandomSecondarySeed在Thread中的偏移量</span></span><br><span class="line">        SEED = UNSAFE.objectFieldOffset</span><br><span class="line">            (tk.getDeclaredField(<span class="string">"threadLocalRandomSeed"</span>));</span><br><span class="line">        PROBE = UNSAFE.objectFieldOffset</span><br><span class="line">            (tk.getDeclaredField(<span class="string">"threadLocalRandomProbe"</span>));</span><br><span class="line">        SECONDARY = UNSAFE.objectFieldOffset</span><br><span class="line">            (tk.getDeclaredField(<span class="string">"threadLocalRandomSecondarySeed"</span>));</span><br><span class="line">    &#125; <span class="keyword">catch</span> (Exception e) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> Error(e);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="ThreadLocalRandom-current-方法"><a href="#ThreadLocalRandom-current-方法" class="headerlink" title="ThreadLocalRandom.current()方法"></a>ThreadLocalRandom.current()方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> ThreadLocalRandom <span class="title">current</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 判断是否为第一次调用，为0则为第一次调用，如果是做初始化操作</span></span><br><span class="line">    <span class="keyword">if</span> (UNSAFE.getInt(Thread.currentThread(), PROBE) == <span class="number">0</span>)</span><br><span class="line">        localInit();</span><br><span class="line">    <span class="comment">// 返回统一的static实例</span></span><br><span class="line">    <span class="keyword">return</span> instance;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 初始化当前线程的种子变量</span></span><br><span class="line"><span class="function"><span class="keyword">static</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">localInit</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 初始化探针</span></span><br><span class="line">    <span class="keyword">int</span> p = probeGenerator.addAndGet(PROBE_INCREMENT);</span><br><span class="line">    <span class="keyword">int</span> probe = (p == <span class="number">0</span>) ? <span class="number">1</span> : p; <span class="comment">// skip 0</span></span><br><span class="line">    <span class="comment">// 初始化seed</span></span><br><span class="line">    <span class="keyword">long</span> seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));</span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    UNSAFE.putLong(t, SEED, seed);</span><br><span class="line">    UNSAFE.putInt(t, PROBE, probe);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="int-nextInt-int-bound-方法"><a href="#int-nextInt-int-bound-方法" class="headerlink" title="int nextInt(int bound)方法"></a>int nextInt(int bound)方法</h3><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">nextInt</span><span class="params">(<span class="keyword">int</span> bound)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 参数校验</span></span><br><span class="line">    <span class="keyword">if</span> (bound &lt;= <span class="number">0</span>)</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(BadBound);</span><br><span class="line">    <span class="comment">// 根据当前线程中的种子计算新种子</span></span><br><span class="line">    <span class="keyword">int</span> r = mix32(nextSeed());</span><br><span class="line">    <span class="keyword">int</span> m = bound - <span class="number">1</span>;</span><br><span class="line">    <span class="keyword">if</span> ((bound &amp; m) == <span class="number">0</span>) <span class="comment">// power of two</span></span><br><span class="line">        r &amp;= m;</span><br><span class="line">    <span class="keyword">else</span> &#123; <span class="comment">// reject over-represented candidates</span></span><br><span class="line">        <span class="keyword">for</span> (<span class="keyword">int</span> u = r &gt;&gt;&gt; <span class="number">1</span>;</span><br><span class="line">             u + m - (r = u % bound) &lt; <span class="number">0</span>;</span><br><span class="line">             u = mix32(nextSeed()) &gt;&gt;&gt; <span class="number">1</span>)</span><br><span class="line">            ;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">final</span> <span class="keyword">long</span> <span class="title">nextSeed</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Thread t; <span class="keyword">long</span> r; <span class="comment">// read and update per-thread seed</span></span><br><span class="line">    <span class="comment">// 更新种子</span></span><br><span class="line">    UNSAFE.putLong(t = Thread.currentThread(), SEED,</span><br><span class="line">    <span class="comment">// 这里获取线程种子并进行 + GAMMA操作</span></span><br><span class="line">                   r = UNSAFE.getLong(t, SEED) + GAMMA);</span><br><span class="line">    <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>  因为Random的种子生成随机数的方法，在Random中的种子是共享的所以多线程会出现并发问题，而Random中将种子声明成原子变量并且使用CAS更新会导致在多线程环境下多个线程去竞争资源，从而导致大量线程自旋，浪费资源和降低并发能力。</p><p>  在ThreadLocalRandom中使用了线程封闭技术来解决这个问题，线程封闭即使线程本地化，将共享变量进行本地化，从而避免了线程安全问题和提高了并发能力。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Random类及其局限性&quot;&gt;&lt;a href=&quot;#Random类及其局限性&quot; class=&quot;headerlink&quot; title=&quot;Random类及其局限性&quot;&gt;&lt;/a&gt;Random类及其局限性&lt;/h2&gt;&lt;p&gt;  在JDK7之前包括现在Random都是使用比较广泛的随机
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——隐藏在并发包中的管程</title>
    <link href="https://francisqiang.github.io/2019/08/12/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E9%9A%90%E8%97%8F%E5%9C%A8%E5%B9%B6%E5%8F%91%E5%8C%85%E4%B8%AD%E7%9A%84%E7%AE%A1%E7%A8%8B/"/>
    <id>https://francisqiang.github.io/2019/08/12/Java并发编程学习——隐藏在并发包中的管程/</id>
    <published>2019-08-12T11:35:23.000Z</published>
    <updated>2019-08-12T11:59:11.432Z</updated>
    
    <content type="html"><![CDATA[<h2 id="Java-SDK并发包中的管程"><a href="#Java-SDK并发包中的管程" class="headerlink" title="Java SDK并发包中的管程"></a>Java SDK并发包中的管程</h2><p>  <strong><em>在Java SDK并发包中通过Lock和Condition两个接口来实现管程，其中Lock用于解决互斥问题，Condition用来解决同步问题</em></strong>。</p><h2 id="再造管程的理由"><a href="#再造管程的理由" class="headerlink" title="再造管程的理由"></a>再造管程的理由</h2><p>  在Java 1.5之前synchronized是不如SDK中的Lock的，但1.6版本之后synchronized做了很多优化，将性能追了上来，所以为什么还要重新“造轮子”的原因不是性能问题，而是 <strong><em>死锁问题中的不可抢占条件</em></strong> 。</p><p>  我们知道，synchronized是无法破坏不可抢占条件的，因为当线程申请不到锁的时候会直接阻塞。这个阻塞不会释放资源。</p><p>  而让我们重新去设计一把互斥锁，其实有三种方法去解决。</p><ol><li><p><strong>能够响应中断</strong>。因为申请不到锁之后阻塞也不释放资源，所以我们希望阻塞线程能被通知中断唤醒并且释放曾经拥有过的资源。</p></li><li><p><strong>支持超时</strong>。如果线程在一段时间内没有获取到锁，不是进入阻塞状态而是返回一个错误，这样线程也将右机会释放曾经获得的锁了。</p></li><li><p><strong>非阻塞地获取锁</strong>。获取失败的时候不是进入阻塞状态而是直接返回，这样线程就有机会释放曾经持有的锁了。</p><p>而这三种方案，在Lock接口中都有对应的API</p><p><img src="/2019/08/12/Java并发编程学习——隐藏在并发包中的管程/1.jpg" alt="Lock接口"></p><p>对应的lockInterruptibly()是支持中断的API，tryLock()是支持非阻塞获取锁的API，tryLock(long time,TimeUnit unit)是支持超时的API。</p></li></ol><h2 id="如何保证可见性"><a href="#如何保证可见性" class="headerlink" title="如何保证可见性"></a>如何保证可见性</h2><p>  对于synchronized来说因为happends-before规则可以保证可见性，而在ReentrantLock实现类中内部持有了一个volatile变量state，并且在lock和unlock的时候读写state， <strong><em>通过volatile的happens-before规则</em></strong> 。</p><h2 id="锁的最佳实践"><a href="#锁的最佳实践" class="headerlink" title="锁的最佳实践"></a>锁的最佳实践</h2><ol><li><p>永远只在更新对象的变量时加锁</p></li><li><p>永远只在访问可变的成员变量的时候加锁</p></li><li><p>永远不再调用其他对象的方法的时候加锁(也许其他方法里面有线程sleep()或者IO操作，其他类也可能加锁会导致死锁)</p></li></ol><h2 id="思考"><a href="#思考" class="headerlink" title="思考"></a>思考</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">int</span> balance;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> Lock lock</span><br><span class="line">          = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line">  <span class="comment">// 转账</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">transfer</span><span class="params">(Account tar, <span class="keyword">int</span> amt)</span></span>&#123;</span><br><span class="line">    <span class="keyword">while</span> (<span class="keyword">true</span>) &#123;</span><br><span class="line">      <span class="keyword">if</span>(<span class="keyword">this</span>.lock.tryLock()) &#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">          <span class="keyword">if</span> (tar.lock.tryLock()) &#123;</span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">              <span class="keyword">this</span>.balance -= amt;</span><br><span class="line">              tar.balance += amt;</span><br><span class="line">            &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">              tar.lock.unlock();</span><br><span class="line">            &#125;</span><br><span class="line">          &#125;<span class="comment">//if</span></span><br><span class="line">        &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">          <span class="keyword">this</span>.lock.unlock();</span><br><span class="line">        &#125;</span><br><span class="line">      &#125;<span class="comment">//if</span></span><br><span class="line">    &#125;<span class="comment">//while</span></span><br><span class="line">  &#125;<span class="comment">//transfer</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  上面的代码不会产生死锁，但是可能会产生活锁。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;Java-SDK并发包中的管程&quot;&gt;&lt;a href=&quot;#Java-SDK并发包中的管程&quot; class=&quot;headerlink&quot; title=&quot;Java SDK并发包中的管程&quot;&gt;&lt;/a&gt;Java SDK并发包中的管程&lt;/h2&gt;&lt;p&gt;  &lt;strong&gt;&lt;em&gt;在Jav
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美读书笔记二</title>
    <link href="https://francisqiang.github.io/2019/08/12/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%BA%8C/"/>
    <id>https://francisqiang.github.io/2019/08/12/Java并发编程学习——Java并发编程之美读书笔记二/</id>
    <published>2019-08-12T04:41:22.000Z</published>
    <updated>2019-08-12T06:14:20.284Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是多线程并发编程"><a href="#什么是多线程并发编程" class="headerlink" title="什么是多线程并发编程"></a>什么是多线程并发编程</h2><p>  并发：同一个时间段多个任务同时都在执行。</p><p>  并行：多个任务在单位时间内同时执行。</p><p>  也就是说并发使用cpu在短时间内切换进程造成了多个任务同时执行的假象。而在多线程编程实践中，<em>线程的个数往往大于CPU个数</em>，所以一般都称多线程并发编程而不是多线程并行编程(意味着一个核上存在多个线程，所以是并发)。</p><h2 id="为什么要进行多线程并发编程"><a href="#为什么要进行多线程并发编程" class="headerlink" title="为什么要进行多线程并发编程"></a>为什么要进行多线程并发编程</h2><p>  多核CPU时代打破了单核CPU对多线程效能的限制(频繁切换线程会带来额外开销)，多线程并发编程可以显著提高性能以应对海量数据和请求。</p><h2 id="Java中的线程安全问题"><a href="#Java中的线程安全问题" class="headerlink" title="Java中的线程安全问题"></a>Java中的线程安全问题</h2><p>  多个线程去改变或读取(至少有一个去改变)一个共享资源会产生线程安全问题。</p><h2 id="Java中共享变量的内存可见性问题"><a href="#Java中共享变量的内存可见性问题" class="headerlink" title="Java中共享变量的内存可见性问题"></a>Java中共享变量的内存可见性问题</h2><p>  <img src="/2019/08/12/Java并发编程学习——Java并发编程之美读书笔记二/1.jpg" alt="1.jpg"></p><p>  Java内存规定，将所有变量都存放在主内存， <strong><em>当线程使用变量时，会把主内存里面的变量复制到自己的工作空间或者叫工作内存</em></strong> 。</p><p>  而这个线程的工作内存又是怎样的呢？</p><p>  <img src="/2019/08/12/Java并发编程学习——Java并发编程之美读书笔记二/2.jpg" alt="2.jpg"></p><p>  上图是一个双核CPU系统架构，每个核都有自己的控制器和运算器，其中控制器包含一组寄存器和操作控制器，运算器执行算术逻辑运算。每个核都有自己的一级缓存，在有些架构里面还有一个所有CPU共享的二级缓存。 <strong><em>那么Java内存模型里面的工作内存，就对应着这里的L1或者L2或者CPU的寄存器</em></strong> 。</p><p>  由于cache的存在会导致内存不可见，比如线程A和线程B要对一个共享变量x做增加的操作。</p><p>  线程A首先去获取共享变量x的值，由于两级缓存都没有命中，那么线程A从主存中取出x的值为0并做增加操作，并且放入一级缓存和二级缓存，线程B也去获取共享变量x的值，首先一级缓存没有命中，二级缓存命中(刚刚线程A写入了二级共享缓存)，然后发现二级缓存中的值为1，然后进行增加操作并写入自己的一级缓存和二级缓存，主存。</p><p>  这样看来没什么问题，但是如果线程A继续进行增加操作呢？线程A首先会去获取共享变量x的值，一级缓存命中！并且获取到了x的值为1，然后进行增加操作变为2，问题就出现了。</p><p>  <strong>线程自己的工作内存(自己的缓存)会导致内存不可见性</strong>。</p><h2 id="Java中的synchronized关键字"><a href="#Java中的synchronized关键字" class="headerlink" title="Java中的synchronized关键字"></a>Java中的synchronized关键字</h2><p>  synchronized是Java提供的一种原子性内置锁，由于Java的线程和操作系统中的线程一一对应，所有当阻塞一个线程的时候，需要从用户态切换到内核态执行阻塞操作，这是一个很耗时的操作。而synchronized就会导致上下文切换。</p><h3 id="synchronized的内存语意"><a href="#synchronized的内存语意" class="headerlink" title="synchronized的内存语意"></a>synchronized的内存语意</h3><p>  synchronized代码块中的变量会 <strong><em>从线程的工作内存中清除</em></strong> ，也就是说synchronized可以解决内存的不可见性。</p><h3 id="Java中的volatile关键字"><a href="#Java中的volatile关键字" class="headerlink" title="Java中的volatile关键字"></a>Java中的volatile关键字</h3><p>  使用synchronized的方式可以解决内存不可见，volatile也可以。</p><p>  <strong><em>当变量声明为volatile时，线程在写入变量时不会把值缓存在寄存器或者其他地方，而是把值刷新会主内存</em></strong> 。</p><p>  volatile虽然解决了内存可见性问题，但是并不是原子操作，所以在多线程并发时也会出现异常。而一般在什么时候使用volatile关键字呢？</p><ol><li><p>写入变量不依赖与变量的当前值(加一和赋值操作),因为如果依赖于当前值，那么获取——计算——写入这三步不是原子操作，而不保证volatile的原子性。</p></li><li><p>读写变量时没有加锁。加锁已经保证了内存可见性，所以没必要把变量再声明为volatile了。</p></li></ol><h2 id="java中的原子性操作"><a href="#java中的原子性操作" class="headerlink" title="java中的原子性操作"></a>java中的原子性操作</h2><p>  因为 <strong><em>线程切换是CPU指令级别的</em></strong> ，而Java中的一条语句通常是由很多指令组成的，所以在多线程环境下线程切换会导致很多并发不安全的问题，而synchronized会保证同时只有一个线程执行。</p><h2 id="Java中的CAS操作"><a href="#Java中的CAS操作" class="headerlink" title="Java中的CAS操作"></a>Java中的CAS操作</h2><p>  CAS即Compare and Swap，是JDK提供的<em>非阻塞原子性操作</em>，它通过硬件保证了比较——更新操作的原子性。</p><h3 id="ABA问题"><a href="#ABA问题" class="headerlink" title="ABA问题"></a>ABA问题</h3><p>  当线程1使用CAS修改初始值为A的变量X，那么线程1会首先获取当前变量X的值，然后使用CAS操作尝试修改X的值为B，但是这个时候线程2使用CAS修改变量的值为B然后又通过CAS操作修改变脸的值为A，此时线程1执行CAS的时候X的值虽然是A，然是这个A已经不是线程1获取时的A了。</p><p>  解决：ABA问题就是变量的状态发生了环形转换，可以提供给变量的状态值配置一个时间戳来避免ABA问题产生。</p><h2 id="Unsafe类"><a href="#Unsafe类" class="headerlink" title="Unsafe类"></a>Unsafe类</h2><p>  Unsafe类提供了硬件级别的原子性操作，里面的方法都是native方法，他们使用JNI方式访问本地C++实现库。</p><p>  当我们要使用Unsafe类的时候，在本身getUnsafe()方法中会判断当前类加载是否是Bootstrap类加载器，如果不是抛出异常，而我们启动main函数所在的类是使用AppClassLoader加载的，所以在main函数这里面加载Unsafe类的时候，根据委托机制，会委托给Bootstrap去加载Unsafe类。</p><p>  如果不加以限制，我们可以直接通过Unsafe操作内存，这是不安全的，所以我们需要在rt.jar包下使用Unsafe类，我们也可以通过万能的反射来实现。</p><h2 id="Java指令重排序"><a href="#Java指令重排序" class="headerlink" title="Java指令重排序"></a>Java指令重排序</h2><p>  <strong>Java内存模型允许编译器和处理器对指令重排序以提高运行性能</strong>。而多线程的环境下指令重排序会导致一些并发问题，而如果使用volatile修饰变量可以避免一些重排序和内存可见性问题。</p><p>  写volatile变量的时候，可以确保volatile变量写之前的操作不会被编译器重排序到volatile写之后。读volatile变量时，可以确保对volatile变量读之后的操作不会被编译器重排序到volatile读之前。</p><h2 id="伪共享"><a href="#伪共享" class="headerlink" title="伪共享"></a>伪共享</h2><h3 id="什么是伪共享"><a href="#什么是伪共享" class="headerlink" title="什么是伪共享"></a>什么是伪共享</h3><p>  为了解决内存和CPU之间的速度差异，CPU会添加一个或多个缓存存储器，而缓存在内部是 <strong><em>按行存储</em></strong> 的。因为局部性原理，当一个变量要存入缓存中其实是连带着周围的变量存入缓存的。所以存入Cache行的<strong>是内存而不是单个变量</strong>。</p><p>  比如现在两个CPU，有两个变量x，y放入了缓存行，当线程1对CPU1的缓存行进行修改变量x的值的时候，因为缓存一致性协议，会导致CPU2对应的缓存行失效。所以线程2需要写入x或y的值的时候就要去二级缓存中查找。</p><h3 id="为何出现伪共享"><a href="#为何出现伪共享" class="headerlink" title="为何出现伪共享"></a>为何出现伪共享</h3><p>  因为放入缓存行的是多个数据(是一个内存空间)。</p><h3 id="如何避免伪共享"><a href="#如何避免伪共享" class="headerlink" title="如何避免伪共享"></a>如何避免伪共享</h3><p>  填充缓存行(创建一个对象封装相应的变量使对象空间和缓存行空间一样大)。</p><p>  JDK8后使用sun.misc.Contended注解(会自动补齐缓存行)。</p><h2 id="锁的概述"><a href="#锁的概述" class="headerlink" title="锁的概述"></a>锁的概述</h2><h3 id="乐观锁和悲观锁"><a href="#乐观锁和悲观锁" class="headerlink" title="乐观锁和悲观锁"></a>乐观锁和悲观锁</h3><p>  悲观锁：对数据被外界修改保持保守态度，认为数据很容易就被其他线程修改，在对数据处理之前先加锁。</p><p>  乐观锁：认为数据在一般情况下不会造成冲突，所以在访问记录前不会加排它锁，而在进行数据提交更新的时候，才会正式对数据冲突与否进行检测。</p><h3 id="公平锁与非公平锁"><a href="#公平锁与非公平锁" class="headerlink" title="公平锁与非公平锁"></a>公平锁与非公平锁</h3><p>  公平锁：先来先获得锁。</p><p>  非公平：后来的也可以抢占锁。</p><p>  在一般情况下使用非公平锁，公平锁会带来额外开销。</p><h3 id="独占锁和共享锁"><a href="#独占锁和共享锁" class="headerlink" title="独占锁和共享锁"></a>独占锁和共享锁</h3><p>  独占锁：只能一个线程拥有</p><p>  共享锁：多个线程拥有，比如说ReadWriteLock读写锁允许一个资源被多个线程进行读操作。</p><h3 id="可重入锁"><a href="#可重入锁" class="headerlink" title="可重入锁"></a>可重入锁</h3><p>  可重入锁：已经获得了锁的资源再次申请该锁的时候不会被阻塞，synchronized就是一个可重入锁。</p><h3 id="自旋锁"><a href="#自旋锁" class="headerlink" title="自旋锁"></a>自旋锁</h3><p>  当前线程在获取锁的时候，如果发现锁已经被占用，它不马上阻塞自己，而是在不放弃CPU使用权的情况下，多次(默认情况下是10次)尝试获取。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;什么是多线程并发编程&quot;&gt;&lt;a href=&quot;#什么是多线程并发编程&quot; class=&quot;headerlink&quot; title=&quot;什么是多线程并发编程&quot;&gt;&lt;/a&gt;什么是多线程并发编程&lt;/h2&gt;&lt;p&gt;  并发：同一个时间段多个任务同时都在执行。&lt;/p&gt;
&lt;p&gt;  并行：多个任
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java并发编程之美读书笔记一</title>
    <link href="https://francisqiang.github.io/2019/08/11/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E4%B9%8B%E7%BE%8E%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0%E4%B8%80/"/>
    <id>https://francisqiang.github.io/2019/08/11/Java并发编程学习——Java并发编程之美读书笔记一/</id>
    <published>2019-08-11T06:04:03.000Z</published>
    <updated>2019-08-11T11:36:41.200Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是线程"><a href="#什么是线程" class="headerlink" title="什么是线程"></a>什么是线程</h2><p>  在操作系统中，进程是资源分配的基本单位，线程是资源调度的基本单位，因为<strong>真正占用CPU的是线程</strong>。</p><p>  <img src="/2019/08/11/Java并发编程学习——Java并发编程之美读书笔记一/1.jpg" alt="Java进程模型"></p><p>  对于整个Java应用程序来说是一个进程，里面有很多线程。在操作系统中说，线程不独立拥有资源(进程是拥有资源的基本单位)，但是线程还是会拥有自己独立的一些资源的比如说程序计数器，栈等。</p><p>  程序计数器：<strong>用来记录当前线程要执行的指令地址</strong>。我的理解是，第一为了确定线程要执行的后面的指令地址，第二是为了<strong>确保线程切换的时候，线程能记住它的执行状态(当前执行到哪了)，当下一次再次获得CPU资源的时候，线程能从它私有的程序计数器中获取指令地址继续执行</strong>。但是如果执行的是native方法，那么pc计数器记录的是undefined地址，只有执行Java代码的时候记录的才是下一条指令的地址。</p><p>  栈：对于任何语言来说，线程是需要有栈的，这个栈中存放是就是<strong>栈帧</strong>。因为，对于CPU来说，是没有方法层面的，当高级语言进行方法调用的时候其实对于CPU来说还是执行相应地址中的指令，所以在高级语言中需要有一个<strong>方法调用栈</strong>，每执行一次方法调用的时候将参数，返回地址存入栈帧中并压入方法栈，等到被调用的方法执行完再出栈取出返回地址继续执行指令。而因为<strong>栈帧和方法同生死共命运</strong>，所以局部变量，参数等都是放入栈帧中的。</p><p>  总结：线程中这个栈是方法调用栈，其中基本单位是栈帧，每次该线程进行方法调用的时候创建栈帧并压入方法调用栈，栈帧中有相应的方法参数，返回地址，和局部变量(<strong>Java中局部变量在栈中的原因</strong>)</p><p>  堆：堆是进程中最大的一块内存，堆是被进程中的所有线程所共享的。堆中主要存放的是new操作创建的对象实例(Java对象在堆中)。</p><p>  方法区：用来存放JVM加载的类，常量及静态变量等信息，也是所有线程共享的。</p><h2 id="线程创建与运行"><a href="#线程创建与运行" class="headerlink" title="线程创建与运行"></a>线程创建与运行</h2><p>  线程创建有三种方式</p><ol><li><p>实现Runnable接口并重写run方法</p></li><li><p>继承Thread类并重写run方法</p></li><li><p>使用FutureTask创建</p><p>使用继承的好处是，获取当前线程直接使用this就可以了，但是Java不支持多继承，所以该类继承了Thread类之后不能再继承其他类了(降低了可扩展性)，而且继承Thread类即将任务代码和线程耦合了。</p><p>实现Runnable接口并重写run方法，最后将该实现接口实例作为参数传入Thread构造函数中，这种方法将任务代码和线程之间解耦，并且解决了多继承的问题。</p><p>而对于上面两种创建线程的方法来说，线程是没有返回值的，因为run方法是void方法。要使线程具有返回值可以通过FutureTask创建线程。</p><p>具体的步骤是：</p></li><li><p>创建一个类实现Callable接口并重写call()方法</p></li><li>将该类作为参数传入Thread构造方法并启动线程</li><li>最后通过FutureTask.get()等待任务执行完毕返回结果</li></ol><h2 id="线程等待与通知"><a href="#线程等待与通知" class="headerlink" title="线程等待与通知"></a>线程等待与通知</h2><h3 id="wait-函数"><a href="#wait-函数" class="headerlink" title="wait()函数"></a>wait()函数</h3><p>  它是Object中的一个方法，在并发编程中，它的调用者其实是<strong>共享变量</strong>，因为只有获得了synchronized隐式锁的线程才能使用wait方法，如果没有则会抛出IllegalMonitorStateException。</p><p>  当线程调用这个wait方法的时候会被<strong>阻塞挂起</strong>，只有</p><ol><li>其他线程调用了该共享变量的notify或者notifyAll(也就是说wait方法会释放当前共享变量的锁)</li><li><p>其他线程调用了该线程的interrupt()方法，该线程抛出InterruptedException异常并返回。</p><p>为了防范<em>虚假唤醒</em>，所以wait方法有它的编程范式</p></li></ol><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">synchronized</span> (obj) &#123;</span><br><span class="line">    <span class="keyword">while</span>(条件不满足) &#123;</span><br><span class="line">        obj.wait();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h3 id="wait-long-timeout"><a href="#wait-long-timeout" class="headerlink" title="wait(long timeout)"></a>wait(long timeout)</h3><p>  增加wait时间限制，如果超出时间限制，不管上文提到的两个情况是否满足，该线程<strong>还是会因为超时而返回</strong>。</p><h3 id="wait-long-timeout-int-nanos"><a href="#wait-long-timeout-int-nanos" class="headerlink" title="wait(long timeout, int nanos)"></a>wait(long timeout, int nanos)</h3><p>  差不多，内部其实调用的是wait(long timeout)函数</p><h3 id="notify"><a href="#notify" class="headerlink" title="notify()"></a>notify()</h3><p>  会<strong>随机</strong>唤醒一个在该共享变量下调用wait()方法而阻塞的线程。</p><p>  被唤醒的线程不能直接执行，还需要重新获得共享变量的锁，才能继续执行。</p><h3 id="notifyAll"><a href="#notifyAll" class="headerlink" title="notifyAll()"></a>notifyAll()</h3><p>  通知所有在该共享变量下因为调用wait而阻塞的线程。</p><h2 id="等待线程执行终止的join方法"><a href="#等待线程执行终止的join方法" class="headerlink" title="等待线程执行终止的join方法"></a>等待线程执行终止的join方法</h2><p>  join方法是Thread直接提供的无参且返回值为void的方法。</p><p>  作用是<strong>等待线程执行完毕</strong>。</p><h2 id="让线程睡眠的sleep-方法"><a href="#让线程睡眠的sleep-方法" class="headerlink" title="让线程睡眠的sleep()方法"></a>让线程睡眠的sleep()方法</h2><p>  会让线程暂时让出指定时间的执行权，也就是在这期间不参与<strong>CPU调度</strong>，但是线程持有的监视器资源(比如锁)是<strong>不会释放</strong>的。</p><h2 id="让出CPU执行权的yield-方法"><a href="#让出CPU执行权的yield-方法" class="headerlink" title="让出CPU执行权的yield()方法"></a>让出CPU执行权的yield()方法</h2><p>  当一个线程调用这个方法的时候就是在暗示线程调度器 当前线程请求让出自己的CPU使用，<strong>但是，线程调度器可以无条件忽略这个暗示</strong>。我们知道时间片轮转会让某个线程持有CPU资源一段时间，线程如果还没使用完这个时间就不想使用了，可以调用这个方法来告诉线程调度器可以进行下一轮的线程调度了。</p><h2 id="线程中断"><a href="#线程中断" class="headerlink" title="线程中断"></a>线程中断</h2><p>  <strong>线程中断是线程之间的一种协作模式</strong>，通过设置<em>中断标志</em>不能直接终止线程的执行，而是 <strong><em>线程根据中断状态自己去处理</em></strong> 。</p><p>  下面三个方法是关于线程中断的</p><ol><li><p>void interrupt()方法 中断线程，例如线程A执行时，线程B可以通过调用线程A的interrupt()方法来设置线程A的<strong>中断标志为true，并立即返回</strong>，注意：仅仅是设置中断标志，线程A并没有真正被中断。若线程A调用了wait(),join(),sleep()方法而被阻塞挂起的时候，线程B调用线程A的interrupt()方法会在 <strong><em>调用这些方法的地方抛出InterruptedException而返回(注意：这里是wait()这些方法，而不是interrupt()方法)</em></strong> 。</p></li><li><p>boolean isInterrupted()方法 检测当前线程是否被中断，如果是返回true，否则false。</p></li><li><p>boolean interrupted()方法 检测 <strong><em>当前线程</em></strong> 是否被中断，如果是返回true，否则false。并且它还会<strong>清除中断状态</strong>。它是Thread的静态方法，不管在哪调用返回的都是当前执行的线程的中断状态并清楚中断状态。</p><p>如果某个线程为了等待某些条件发生而阻塞(一般会调用sleep，wait或join函数)，比如这个线程调用了sleep(3000)函数去等待某种条件发生，但是在1秒的时候条件已经满足，这个时候可以调用该线程的interrupt()方法来 <strong><em>强制sleep()抛出InterruptedException而返回，线程恢复到激活状态</em></strong>。</p></li></ol><h2 id="理解线程上下文切换"><a href="#理解线程上下文切换" class="headerlink" title="理解线程上下文切换"></a>理解线程上下文切换</h2><p>  正如一开始所讲的程序计数器，程序技术器中保存了相应的下一个执行指令地址，栈中保存了执行的一些信息。这些就是线程上下文切换所需要的资源。</p><p>  线程上下文切换的时机</p><ol><li><p>当前线程CPU时间使用完处于就绪状态。</p></li><li><p>当前线程被其他线程中断。</p></li></ol><h2 id="线程死锁"><a href="#线程死锁" class="headerlink" title="线程死锁"></a>线程死锁</h2><p>  死锁的四个必要条件：1. 互斥，2. 请求并占有， 3. 不可剥夺， 4. 环路等待</p><p>  避免：破坏一个必要条件。</p><h2 id="守护线程与用户线程"><a href="#守护线程与用户线程" class="headerlink" title="守护线程与用户线程"></a>守护线程与用户线程</h2><p>  Java中的线程分为两类：分别为daemon线程和user线程。JVM在启动时会调用main函数，main函数所在的线程是user线程，其实JVM在启动时还启动了好多daemon线程比如垃圾回收线程。</p><p>  用户线程和守护线程的区别：当最后一个用户线程结束的时候JVM会退出，而守护线程的消亡不会影响到JVM的退出。</p><p>  比如在main函数中启动一个无限循环的用户线程，当主线程执行完毕的时候JVM不会退出，但是如果把这个无限循环的线程改为守护线程，那么在主线程结束后JVM会自动退出。</p><p>  如何设置守护线程？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">deamonThread.setDaemon(<span class="keyword">true</span>);</span><br></pre></td></tr></table></figure><p>  main线程运行结束后，JVM会自动启动一个叫做DestroyJavaVM的线程，该线程会等待所有 <strong><em>用户线程</em></strong> 结束后终止JVM进程。</p><p>  而Tomcat的NIO实现NioEndPoint中会开启一组接受线程来接受用户的连接请求以及一组处理线程负责具体处理用户请求，这些线程线程被设置成了守护线程，即当tomcat收到shutdown命令之后并且没有其他用户线程存在的情况下，tomcat进程会马上消亡而<strong>不会等待处理线程处理完当前的请求</strong>。</p><h2 id="ThreadLocal"><a href="#ThreadLocal" class="headerlink" title="ThreadLocal"></a>ThreadLocal</h2><p>  线程本地化，可以将共享变量复制到线程本地存储空间。这是一种无锁的同步方式。</p><p>  使用方式： 创建ThreadLocal变量，在线程运行方法中设置刚刚的threadLocal变量实例。</p><h3 id="实现原理"><a href="#实现原理" class="headerlink" title="实现原理"></a>实现原理</h3><p>  <img src="/2019/08/11/Java并发编程学习——Java并发编程之美读书笔记一/2.jpg" alt="ThreadLocal原理"></p><p>  当ThreadLocal实例在线程执行的时候第一次调用set或者get方法的时候会在线程中创建相应的threadLocals(这个是一个ThreadLocalMap，一种定制化的HashMap，里面存放了许多线程本地化变量)，key为当前ThreadLocal实例的引用，值为自己设置的值。后面调用就直接会在线程的threadLocals这个Map中进行操作。</p><p>  注意当本地变量不再使用的时候最好使用remove将其删除，避免内存溢出。还要注意的是ThreadLocal不支持继承，也就是说子线程不会拥有父线程的threadLocals变量。</p><p>  但是可以使用InheritableThreadLocal类，可以追溯Thread创建的源码，Thread在创建的过程中会判断父线程的inheritableThreadLocals(上图的Thread中有这个私有变量)是否为空，如果不为空那么将其复制到子线程的inheritableThreadLocals中去。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;什么是线程&quot;&gt;&lt;a href=&quot;#什么是线程&quot; class=&quot;headerlink&quot; title=&quot;什么是线程&quot;&gt;&lt;/a&gt;什么是线程&lt;/h2&gt;&lt;p&gt;  在操作系统中，进程是资源分配的基本单位，线程是资源调度的基本单位，因为&lt;strong&gt;真正占用CPU的是线程&lt;/
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——为什么局部变量是线程安全的</title>
    <link href="https://francisqiang.github.io/2019/08/10/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E4%B8%BA%E4%BB%80%E4%B9%88%E5%B1%80%E9%83%A8%E5%8F%98%E9%87%8F%E6%98%AF%E7%BA%BF%E7%A8%8B%E5%AE%89%E5%85%A8%E7%9A%84/"/>
    <id>https://francisqiang.github.io/2019/08/10/Java并发编程学习——为什么局部变量是线程安全的/</id>
    <published>2019-08-10T09:32:30.000Z</published>
    <updated>2019-08-10T09:58:58.659Z</updated>
    
    <content type="html"><![CDATA[<h2 id="局部变量不存在数据竞争"><a href="#局部变量不存在数据竞争" class="headerlink" title="局部变量不存在数据竞争"></a>局部变量不存在数据竞争</h2><p>  在并发编程领域里，<strong>没有共享就没有伤害</strong>。对于局部变量是不存在数据竞争的，为什么呢？</p><p>  比如，下面代码里的 fibonacci() 这个方法，会根据传入的参数 n ，返回 1 到 n 的斐波那契数列，斐波那契数列类似这样： 1、1、2、3、5、8、13、21、34……第 1 项和第 2 项是 1，从第 3 项开始，每一项都等于前两项之和。在这个方法里面，有个局部变量：数组 r 用来保存数列的结果，每次计算完一项，都会更新数组 r 对应位置中的值。你可以思考这样一个问题，当多个线程调用 fibonacci() 这个方法的时候，数组 r 是否存在数据竞争（Data Race）呢？</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 返回斐波那契数列</span></span><br><span class="line"><span class="keyword">int</span>[] fibonacci(<span class="keyword">int</span> n) &#123;</span><br><span class="line">  <span class="comment">// 创建结果数组</span></span><br><span class="line">  <span class="keyword">int</span>[] r = <span class="keyword">new</span> <span class="keyword">int</span>[n];</span><br><span class="line">  <span class="comment">// 初始化第一、第二个数</span></span><br><span class="line">  r[<span class="number">0</span>] = r[<span class="number">1</span>] = <span class="number">1</span>;  <span class="comment">// ①</span></span><br><span class="line">  <span class="comment">// 计算 2..n</span></span><br><span class="line">  <span class="keyword">for</span>(<span class="keyword">int</span> i = <span class="number">2</span>; i &lt; n; i++) &#123;</span><br><span class="line">      r[i] = r[i-<span class="number">2</span>] + r[i-<span class="number">1</span>];</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> r;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们试想一下貌似多个线程同时执行fibonacci方法的时候对数组r的写入读取会发生数据竞争。</p><h2 id="方法是如何被执行的"><a href="#方法是如何被执行的" class="headerlink" title="方法是如何被执行的"></a>方法是如何被执行的</h2><p>  对于CPU来说是没有方法这一层面的，对于它来说任何操作都是一条条指令，那么CPU如果进行一个方法调用总要进行返回到调用方法的代码片段(地址)去执行，这个CPU是怎么做到的呢？</p><p>  答案就是栈，在我的关于栈的文章中提到过方法栈，CPU就是通过栈来实现返回到原来调用方法的地址的。</p><p>  <img src="/2019/08/10/Java并发编程学习——为什么局部变量是线程安全的/1.jpg" alt="方法栈"></p><p>  在线程执行进入方法的时候，会将这个方法的一些信息作为<strong>栈帧</strong>压入<strong>方法栈</strong>中。一些信息可能有参数，返回地址(这个肯定是必要的，因为出栈的时候需要用到，不然无法返回了)。但栈帧出栈的时候就意味着这个方法执行完了，所以隐含意思就是<strong>一个栈帧对应着一次一个方法的执行，栈帧和一次一个方法的执行是同生死共命运的</strong>。</p><p>  而我们也知道，对于局部变量来说，在方法执行完就会消失，所以局部变量和栈帧就有着同样的性质了，即局部变量，栈帧，一次一个方法的执行这三者是同时消亡的。所以把局部变量放入栈帧中是最合适不过的了，现实也是这么做的。这也解释了<strong>Java中为什么局部变量是存放在栈中的</strong>，想要跨越方法的边界，那么变量就必须放入堆中。</p><p>  <img src="/2019/08/10/Java并发编程学习——为什么局部变量是线程安全的/2.jpg" alt="方法栈"></p><h2 id="线程与方法栈"><a href="#线程与方法栈" class="headerlink" title="线程与方法栈"></a>线程与方法栈</h2><p> <img src="/2019/08/10/Java并发编程学习——为什么局部变量是线程安全的/3.jpg" alt="方法栈"></p><p>  从操作系统层面来说，每个线程不独立拥有资源，但是它还是拥有一些必要的资源比如线程控制块(也不算资源)，用户栈，内核栈。。所以线程是拥有自己的栈的，也就是说每个线程执行过程中都拥有自己的方法调用栈，而栈帧是在这个方法栈中的，栈帧里面拥有着局部变量，所以可以说，每个线程的局部变量根本不是一个地址，所以就不会出现数据竞争了，所以局部变量是线程安全的。</p><h2 id="线程封闭"><a href="#线程封闭" class="headerlink" title="线程封闭"></a>线程封闭</h2><p>  因为局部变量不存在并发问题，现在也成为了一个解决并发问题的重要思路了，叫<strong>线程封闭</strong>。</p><p>  采用线程封闭技术的案例非常多，例如从数据库连接池里获取的连接 Connection，在 JDBC 规范里并没有要求这个 Connection 必须是线程安全的。数据库连接池通过线程封闭技术，保证一个 Connection 一旦被一个线程获取之后，在这个线程关闭 Connection 之前的这段时间里，不会再分配给其他线程，从而保证了 Connection 不会有并发问题。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;局部变量不存在数据竞争&quot;&gt;&lt;a href=&quot;#局部变量不存在数据竞争&quot; class=&quot;headerlink&quot; title=&quot;局部变量不存在数据竞争&quot;&gt;&lt;/a&gt;局部变量不存在数据竞争&lt;/h2&gt;&lt;p&gt;  在并发编程领域里，&lt;strong&gt;没有共享就没有伤害&lt;/stro
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——创建多少线程才是合适的</title>
    <link href="https://francisqiang.github.io/2019/08/10/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E5%88%9B%E5%BB%BA%E5%A4%9A%E5%B0%91%E7%BA%BF%E7%A8%8B%E6%89%8D%E6%98%AF%E5%90%88%E9%80%82%E7%9A%84/"/>
    <id>https://francisqiang.github.io/2019/08/10/Java并发编程学习——创建多少线程才是合适的/</id>
    <published>2019-08-10T08:24:39.000Z</published>
    <updated>2019-08-10T08:55:16.091Z</updated>
    
    <content type="html"><![CDATA[<h2 id="为什么使用多线程"><a href="#为什么使用多线程" class="headerlink" title="为什么使用多线程"></a>为什么使用多线程</h2><p>  减少<strong>延迟</strong>，提高<strong>吞吐量</strong>。</p><p>  延迟指从发出请求到收到响应过程的时间，吞吐量指单位时间内能完成的请求个数。</p><h2 id="多线程应用场景"><a href="#多线程应用场景" class="headerlink" title="多线程应用场景"></a>多线程应用场景</h2><p>  想要降低延迟提高吞吐量主要有两个维度的方法，第一个是优化算法，第二个是最大化硬件的利用率。前者属于算法范畴，后者就和并发编程息息相关了。而对于计算机硬件来说最主要的就是两个硬件，一个是IO一个是CPU。在操作系统层面，操作系统已经为我们对硬件的利用率做了很大的优化，但是还是不够，在CPU与IO配合使用的利用率更加需要我们程序员去优化。也就是说，<strong>我们需要去解决CPU和IO设备综合利用率的问题</strong></p><p>  <img src="/2019/08/10/Java并发编程学习——创建多少线程才是合适的/1.jpg" alt="单线程"></p><p>  例如在单线程中，假设CPU和IO的执行时间相同，那么这个时候CPU和IO设备的利用率都是50%。</p><p>  但在双线程中，CPU和IO设备执行时间都是一样的，这个时候，CPU在等待IO的时候又可以去执行下一个线程的，这个时候CPU和IO的利用率就是100%。</p><p>  <img src="/2019/08/10/Java并发编程学习——创建多少线程才是合适的/2.jpg" alt="双线程"></p><p>  所以，<strong>如果 CPU 和 I/O 设备的利用率都很低，那么可以尝试通过增加线程来提高吞吐量。</strong></p><p>  在单核时代，多线程主要就是用来平衡 CPU 和 I/O 设备的。如果程序只有 CPU 计算，而没有 I/O 操作的话，多线程不但不会提升性能，还会使性能变得更差，原因是<strong>增加了线程切换的成本</strong>。但是在多核时代，这种纯计算型的程序也可以利用多线程来提升性能。为什么呢？因为利用多核可以降低响应时间。</p><p>  例如计算 1+2+… … +100 亿的值，如果在 4 核的 CPU 上利用 4 个线程执行，线程 A 计算 [1，25 亿)，线程 B 计算 [25 亿，50 亿)，线程 C 计算 [50，75 亿)，线程 D 计算 [75 亿，100 亿]，之后汇总，那么理论上应该比一个线程计算 [1，100 亿] 快将近 4 倍，响应时间能够降到 25%。一个线程，对于 4 核的 CPU，CPU 的利用率只有 25%，而 4 个线程，则能够将 CPU 的利用率提高到 100%。</p><p>  <img src="/2019/08/10/Java并发编程学习——创建多少线程才是合适的/3.jpg" alt="双线程"></p><h2 id="创建多少线程合适"><a href="#创建多少线程合适" class="headerlink" title="创建多少线程合适"></a>创建多少线程合适</h2><p>  创建多少线程合适需要考虑应用场景，通常情况下我们的程序都是由IO操作和CPU操作一起执行的，而IO操作相对于CPU操作来说是非常耗时的。所以大部分情况下，I/O 操作执行的时间相对于 CPU 计算来说都非常长，这种场景我们一般都称为 I/O 密集型计算。</p><p>  和 I/O 密集型计算相对的就是 CPU 密集型计算了，CPU 密集型计算大部分场景下都是纯 CPU 计算。I/O 密集型程序和 CPU 密集型程序，计算最佳线程数的方法是不同的。</p><p>  对于 CPU 密集型计算，多线程本质上是提升多核 CPU 的利用率，所以对于一个 4 核的 CPU，每个核一个线程，理论上创建 4 个线程就可以了，再多创建线程也只是增加线程切换的成本。所以，对于 CPU 密集型的计算场景，理论上<strong>线程的数量 =CPU 核数</strong>就是最合适的。不过在工程上，线程的数量一般会设置为<strong>CPU 核数 +1</strong>，这样的话，当线程因为偶尔的内存页失效或其他原因导致阻塞时，这个额外的线程可以顶上，从而保证 CPU 的利用率。</p><p>  对于 I/O 密集型的计算场景，比如前面我们的例子中，如果 CPU 计算和 I/O 操作的耗时是 1:1，那么 2 个线程是最合适的。如果 CPU 计算和 I/O 操作的耗时是 1:2，那多少个线程合适呢？是 3 个线程，如下图所示：CPU 在 A、B、C 三个线程之间切换，对于线程 A，当 CPU 从 B、C 切换回来时，线程 A 正好执行完 I/O 操作。这样 CPU 和 I/O 设备的利用率都达到了 100%。</p><p>  <img src="/2019/08/10/Java并发编程学习——创建多少线程才是合适的/4.jpg" alt="IO密集型计算"></p><p>  最佳线程数 =1 +（I/O 耗时 / CPU 耗时）</p><p>  对于多核来说只需要乘上核数就行：最佳线程数 =CPU 核数 * [ 1 +（I/O 耗时 / CPU 耗时）]</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;为什么使用多线程&quot;&gt;&lt;a href=&quot;#为什么使用多线程&quot; class=&quot;headerlink&quot; title=&quot;为什么使用多线程&quot;&gt;&lt;/a&gt;为什么使用多线程&lt;/h2&gt;&lt;p&gt;  减少&lt;strong&gt;延迟&lt;/strong&gt;，提高&lt;strong&gt;吞吐量&lt;/strong&gt;
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——Java线程的生命周期</title>
    <link href="https://francisqiang.github.io/2019/08/10/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94Java%E7%BA%BF%E7%A8%8B%E7%9A%84%E7%94%9F%E5%91%BD%E5%91%A8%E6%9C%9F/"/>
    <id>https://francisqiang.github.io/2019/08/10/Java并发编程学习——Java线程的生命周期/</id>
    <published>2019-08-10T05:16:09.000Z</published>
    <updated>2019-08-10T06:26:48.018Z</updated>
    
    <content type="html"><![CDATA[<h2 id="通用线程模型"><a href="#通用线程模型" class="headerlink" title="通用线程模型"></a>通用线程模型</h2><p>  在操作系统中线程有五种状态，这也是通用的线程模型。这五种状态分别是<strong>初始状态(创建)</strong>,<strong>可运行状态(就绪)</strong>,<strong>运行状态</strong>,<strong>休眠状态(阻塞)</strong>,<strong>终止状态(消亡)</strong>。</p><p>  <img src="/2019/08/10/Java并发编程学习——Java线程的生命周期/1.jpg" alt="通用线程模型"></p><p>  对于这五种状态，不同的编程语言会对他们进行简化合并或者细化，就比如Java把这五种状态进行了相应的更改。</p><h2 id="Java线程模型"><a href="#Java线程模型" class="headerlink" title="Java线程模型"></a>Java线程模型</h2><p>  Java线程一共有六种状态，分别是：NEW（初始化状态），RUNNABLE（可运行 / 运行状态），BLOCKED（阻塞状态），WAITING（无时限等待），TIMED_WAITING（有时限等待），TERMINATED（终止状态）。</p><p>  <img src="/2019/08/10/Java并发编程学习——Java线程的生命周期/2.jpg" alt="Java线程模型"></p><p>  而对于BLOCKED，WAITTING，TIMED_WATTING这三种状态可以归于通用线程模型中的休眠状态(阻塞)，而对于通用线程中的运行状态和就绪(可运行状态)，在Java中则对他们进行了合并，合并成了Runnable状态。</p><h3 id="Runnable与Blocked状态之间的转换"><a href="#Runnable与Blocked状态之间的转换" class="headerlink" title="Runnable与Blocked状态之间的转换"></a>Runnable与Blocked状态之间的转换</h3><p>  对于Runnable转换成Blocked状态只有一种情况，那就是在获取synchronized隐式锁失败的时候，线程会从可运行(Runnable)状态转换为Blocked阻塞状态。而当线程获取到了synchronized隐式锁的时候又会从Blocked状态转换到Runnable状态。</p><p>  但是<strong>线程调用阻塞式API的时候不会从Runnable状态转换到Blocked状态</strong>，在操作系统层面，线程会进入休眠(阻塞状态)，但是在JVM层面，线程的状态是不会发生变化的，也就是说线程状态还是Runnable。<strong>JVM 层面并不关心操作系统调度相关的状态</strong>，因为在 JVM 看来，等待 CPU 使用权（操作系统层面此时处于可执行状态）与等待 I/O（操作系统层面此时处于休眠状态）没有区别，都是在等待某个资源，所以都归入了 RUNNABLE 状态。</p><h3 id="Runnable和Waitting状态之间的转换"><a href="#Runnable和Waitting状态之间的转换" class="headerlink" title="Runnable和Waitting状态之间的转换"></a>Runnable和Waitting状态之间的转换</h3><ol><li><p>获得synchronized隐式锁的线程调用Object的wait方法</p></li><li><p>调用无参数的Thread.join(),其中的 join() 是一种线程同步方法，例如有一个线程对象 thread A，当调用 A.join() 的时候，执行这条语句的线程会等待 thread A 执行完，而等待中的这个线程，其状态会从 RUNNABLE 转换到 WAITING。当线程 thread A 执行完，原来等待它的线程又会从 WAITING 状态转换到 RUNNABLE。</p></li><li><p>调用 LockSupport.park() 方法。其中的 LockSupport 对象，也许你有点陌生，其实 Java 并发包中的锁，都是基于它实现的。调用 LockSupport.park() 方法，当前线程会阻塞，线程的状态会从 RUNNABLE 转换到 WAITING。调用 LockSupport.unpark(Thread thread) 可唤醒目标线程，目标线程的状态又会从 WAITING 状态转换到 RUNNABLE。</p></li></ol><h3 id="Runnable与Timed-Waitting状态之间的转换"><a href="#Runnable与Timed-Waitting状态之间的转换" class="headerlink" title="Runnable与Timed_Waitting状态之间的转换"></a>Runnable与Timed_Waitting状态之间的转换</h3><ol><li><p>调用带超时参数的 Thread.sleep(long millis) 方法</p></li><li><p>获得 synchronized 隐式锁的线程，调用带超时参数的 Object.wait(long timeout) 方法</p></li><li><p>调用带超时参数的 Thread.join(long millis) 方法</p></li><li><p>调用带超时参数的 LockSupport.parkNanos(Object blocker, long deadline) 方法；</p></li><li><p>调用带超时参数的 LockSupport.parkUntil(long deadline) 方法。</p></li></ol><h3 id="从New到Runnable状态"><a href="#从New到Runnable状态" class="headerlink" title="从New到Runnable状态"></a>从New到Runnable状态</h3><p>  首先当线程创建的时候就是New状态，而创建线程有两种方式。</p><p>  一是继承Thread类并重写run方法。</p><p>  二是实现Runnable接口重写run方法并将该类作为创建Thread的参数。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 自定义线程对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">MyThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 线程需要执行的代码</span></span><br><span class="line">    ......</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 创建线程对象</span></span><br><span class="line">MyThread myThread = <span class="keyword">new</span> MyThread();</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 实现 Runnable 接口</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Runner</span> <span class="keyword">implements</span> <span class="title">Runnable</span> </span>&#123;</span><br><span class="line">  <span class="meta">@Override</span></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 线程需要执行的代码</span></span><br><span class="line">    ......</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 创建线程对象</span></span><br><span class="line">Thread thread = <span class="keyword">new</span> Thread(<span class="keyword">new</span> Runner());</span><br></pre></td></tr></table></figure><p>  而对于从New状态到Runnable状态只需要调用线程的start()方法。</p><h3 id="从Runnable状态到Terminated状态"><a href="#从Runnable状态到Terminated状态" class="headerlink" title="从Runnable状态到Terminated状态"></a>从Runnable状态到Terminated状态</h3><p>  调用stop()或者interrupt()方法，但是对于stop方法官方已经废弃，因为stop方法会直接杀死线程，如果线程已经获得隐式锁，那么这锁也会消失，也就意味着，其他线程再也获取不到这个锁了。类似的方法还有 suspend() 和 resume() 方法，这两个方法同样也都不建议使用了。</p><p>  而 interrupt() 方法就温柔多了，interrupt() 方法仅仅是<strong>通知线程</strong>，线程有机会执行一些后续操作，同时也可以无视这个通知。被 interrupt 的线程，是怎么收到通知的呢？一种是<strong>异常</strong>，另一种是<strong>主动检测</strong>。</p><p>  比如有线程A和线程B，线程A调用了线程B的interrupt()方法，这个意思是指线程A告诉线程B——你现在被设置成了中断状态。但是线程B还可以进行操作(因为它有个isInterrupted来决定被中断之后执行的操作)</p><p>  当线程 A 处于 WAITING、TIMED_WAITING 状态时，如果其他线程调用线程 A 的 interrupt() 方法，会使线程 A 返回到 RUNNABLE 状态，同时线程 A 的代码会触发 InterruptedException 异常。</p><p>  上面我们提到转换到 WAITING、TIMED_WAITING 状态的触发条件，都是调用了类似 wait()、join()、sleep() 这样的方法，我们看这些方法的签名，发现都会 throws InterruptedException 这个异常。这个异常的触发条件就是：其他线程调用了该线程的 interrupt() 方法。</p><p>  <em>当线程 A 处于 RUNNABLE 状态时，并且阻塞在 java.nio.channels.InterruptibleChannel 上时，如果其他线程调用线程 A 的 interrupt() 方法，线程 A 会触发 java.nio.channels.ClosedByInterruptException 这个异常；而阻塞在 java.nio.channels.Selector 上时，如果其他线程调用线程 A 的 interrupt() 方法，线程 A 的 java.nio.channels.Selector 会立即返回</em>。</p><p>  上面这两种情况属于被中断的线程通过异常的方式获得了通知。还有一种是主动检测，如果线程处于 RUNNABLE 状态，并且没有阻塞在某个 I/O 操作上，例如中断计算圆周率的线程 A，这时就得依赖线程 A 主动检测中断状态了。如果其他线程调用线程 A 的 interrupt() 方法，那么线程 A 可以通过 isInterrupted() 方法，检测是不是自己被中断了。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;通用线程模型&quot;&gt;&lt;a href=&quot;#通用线程模型&quot; class=&quot;headerlink&quot; title=&quot;通用线程模型&quot;&gt;&lt;/a&gt;通用线程模型&lt;/h2&gt;&lt;p&gt;  在操作系统中线程有五种状态，这也是通用的线程模型。这五种状态分别是&lt;strong&gt;初始状态(创建)&lt;/s
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——管程</title>
    <link href="https://francisqiang.github.io/2019/08/09/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E7%AE%A1%E7%A8%8B/"/>
    <id>https://francisqiang.github.io/2019/08/09/Java并发编程学习——管程/</id>
    <published>2019-08-09T14:04:37.000Z</published>
    <updated>2019-08-09T14:49:41.839Z</updated>
    
    <content type="html"><![CDATA[<h2 id="什么是管程"><a href="#什么是管程" class="headerlink" title="什么是管程"></a>什么是管程</h2><p>  管程：<strong>管理共享变量和操作共享变量的过程</strong>。为了解决信号量配对的复杂性以及分散在程序中降低了程序的可读性，管程通过一种数据结构对共享变量和对共享变量的操作进行了封装。</p><p>  管程和信号量是等价的，所谓等价指的是用管程能够实现信号量，也能用信号量实现管程。但是管程更容易使用，所以 Java 选择了管程。</p><p>  在Java1.5之前提供的是synchronized，wait，notify和notifyAll，这是java管程实现的一部分。</p><h2 id="MESA模型"><a href="#MESA模型" class="headerlink" title="MESA模型"></a>MESA模型</h2><p>  在管程的发展史上，先后出现过三种不同的管程模型，分别是：Hasen 模型、Hoare 模型和 MESA 模型。其中，现在广泛应用的是 MESA 模型，并且 Java 管程的实现参考的也是 MESA 模型。</p><h3 id="MESA模型实现互斥"><a href="#MESA模型实现互斥" class="headerlink" title="MESA模型实现互斥"></a>MESA模型实现互斥</h3><p>  <img src="/2019/08/09/Java并发编程学习——管程/1.jpg" alt="互斥"></p><p>  这里的共享变量被封装起来了，其中对于共享变量queue的操作只能通过enq和deq，而这两个方法保证互斥性，只允许一个线程进入管程。</p><h3 id="MESA墨香实现同步"><a href="#MESA墨香实现同步" class="headerlink" title="MESA墨香实现同步"></a>MESA墨香实现同步</h3><p>  <img src="/2019/08/09/Java并发编程学习——管程/2.jpg" alt="同步"></p><p>  对于同步则通过增加<strong>条件变量和相应的等待队列</strong>。</p><p>  如果当条件变量不满足的时候则进入相应的等待队列，因为某个操作而导致条件允许的时候则唤醒相应的等待队列中的线程，并且再次获取锁来执行。因为要再次获取锁，所以这个条件只能是曾经满足过，所以当阻塞线程被唤醒再次获取锁需要执行的时候，有可能这个时候的条件又不允许了。</p><h2 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h2><p>对于入队操作，如果队列已满，就需要等待直到队列不满，所以这里用了notFull.await();。</p><p>对于出队操作，如果队列为空，就需要等待直到队列不空，所以就用了notEmpty.await();。</p><p>如果入队成功，那么队列就不空了，就需要通知条件变量：队列不空notEmpty对应的等待队列。</p><p>如果出队成功，那就队列就不满了，就需要通知条件变量：队列不满notFull对应的等待队列。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">BlockedQueue</span>&lt;<span class="title">T</span>&gt;</span>&#123;</span><br><span class="line">  <span class="keyword">final</span> Lock lock =</span><br><span class="line">    <span class="keyword">new</span> ReentrantLock();</span><br><span class="line">  <span class="comment">// 条件变量：队列不满  </span></span><br><span class="line">  <span class="keyword">final</span> Condition notFull =</span><br><span class="line">    lock.newCondition();</span><br><span class="line">  <span class="comment">// 条件变量：队列不空  </span></span><br><span class="line">  <span class="keyword">final</span> Condition notEmpty =</span><br><span class="line">    lock.newCondition();</span><br><span class="line"></span><br><span class="line">  <span class="comment">// 入队</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">enq</span><span class="params">(T x)</span> </span>&#123;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">while</span> (队列已满)&#123;</span><br><span class="line">        <span class="comment">// 等待队列不满</span></span><br><span class="line">        notFull.await();</span><br><span class="line">      &#125;  </span><br><span class="line">      <span class="comment">// 省略入队操作...</span></span><br><span class="line">      <span class="comment">// 入队后, 通知可出队</span></span><br><span class="line">      notEmpty.signal();</span><br><span class="line">    &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">      lock.unlock();</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 出队</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">deq</span><span class="params">()</span></span>&#123;</span><br><span class="line">    lock.lock();</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="keyword">while</span> (队列已空)&#123;</span><br><span class="line">        <span class="comment">// 等待队列不空</span></span><br><span class="line">        notEmpty.await();</span><br><span class="line">      &#125;</span><br><span class="line">      <span class="comment">// 省略出队操作...</span></span><br><span class="line">      <span class="comment">// 出队后，通知可入队</span></span><br><span class="line">      notFull.signal();</span><br><span class="line">    &#125;<span class="keyword">finally</span> &#123;</span><br><span class="line">      lock.unlock();</span><br><span class="line">    &#125;  </span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  这里的await和signal就类似于wait和notify。</p><p>  对于 MESA 管程来说，有一个编程范式，就是需要在一个 while 循环里面调用 wait()。这个是 MESA 管程特有的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">while</span>(条件不满足) &#123;</span><br><span class="line">  wait();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  Hasen 模型、Hoare 模型和 MESA 模型的一个核心区别就是当条件满足后，如何通知相关线程。管程要求同一时刻只允许一个线程执行，那当线程 T2 的操作使线程 T1 等待的条件满足时，T1 和 T2 究竟谁可以执行呢？</p><ol><li><p>Hasen 模型里面，要求 notify() 放在代码的最后，这样 T2 通知完 T1 后，T2 就结束了，然后 T1 再执行，这样就能保证同一时刻只有一个线程执行。</p></li><li><p>Hoare 模型里面，T2 通知完 T1 后，T2 阻塞，T1 马上执行；等 T1 执行完，再唤醒 T2，也能保证同一时刻只有一个线程执行。但是相比 Hasen 模型，T2 多了一次阻塞唤醒操作。</p></li><li><p>MESA 管程里面，T2 通知完 T1 后，T2 还是会接着执行，T1 并不立即执行，仅仅是从条件变量的等待队列进到入口等待队列里面。这样做的好处是 notify() 不用放到代码的最后，T2 也没有多余的阻塞唤醒操作。但是也有个副作用，就是当 T1 再次执行的时候，可能曾经满足的条件，现在已经不满足了，所以需要以循环方式检验条件变量。</p></li></ol><h2 id="notify-何时可以使用"><a href="#notify-何时可以使用" class="headerlink" title="notify() 何时可以使用"></a>notify() 何时可以使用</h2><p>  满足以下三个条件：</p><ol><li><p>所有等待线程拥有相同的等待条件</p></li><li><p>所有等待线程被唤醒之后，执行相同的操作。</p></li><li><p>只需要唤醒一个线程。</p></li></ol>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;什么是管程&quot;&gt;&lt;a href=&quot;#什么是管程&quot; class=&quot;headerlink&quot; title=&quot;什么是管程&quot;&gt;&lt;/a&gt;什么是管程&lt;/h2&gt;&lt;p&gt;  管程：&lt;strong&gt;管理共享变量和操作共享变量的过程&lt;/strong&gt;。为了解决信号量配对的复杂性以及分散在程
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
  <entry>
    <title>Java并发编程学习——安全性，活跃性以及性能问题</title>
    <link href="https://francisqiang.github.io/2019/08/09/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B%E5%AD%A6%E4%B9%A0%E2%80%94%E2%80%94%E5%AE%89%E5%85%A8%E6%80%A7%EF%BC%8C%E6%B4%BB%E8%B7%83%E6%80%A7%E4%BB%A5%E5%8F%8A%E6%80%A7%E8%83%BD%E9%97%AE%E9%A2%98/"/>
    <id>https://francisqiang.github.io/2019/08/09/Java并发编程学习——安全性，活跃性以及性能问题/</id>
    <published>2019-08-09T06:11:27.000Z</published>
    <updated>2019-08-09T06:44:09.982Z</updated>
    
    <content type="html"><![CDATA[<h2 id="安全性问题"><a href="#安全性问题" class="headerlink" title="安全性问题"></a>安全性问题</h2><p>  在并发编程中需要考虑三个基本问题——安全性，活跃性以及性能。</p><p>  所谓安全性就是指程序能否按照我们预期的执行。就比如线程安全这个概念，所谓线程安全和不安全无非就是指程序能否保证它的正确性，而这个正确性就是我们让程序按照我们所期望地执行。</p><p>  而这种情况下只有<strong>存在共享数据并且该数据会发生变化</strong>的时候需要考虑安全性问题，通俗来讲就是是否存在多个线程对这个资源进行读写操作(至少有一个线程进行写操作)。</p><p>  当多个线程同时访问同一数据，并且至少有一个线程会写这个数据的时候，如果我们不采取防护措施，那么就会导致并发 Bug，对此还有一个专业的术语，叫做<strong>数据竞争</strong>（Data Race）。</p><p>  就比如以下的add10k方法，当多个线程调用的时候就会发生数据竞争。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">long</span> count = <span class="number">0</span>;</span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">add10K</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> idx = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(idx++ &lt; <span class="number">10000</span>) &#123;</span><br><span class="line">      count += <span class="number">1</span>;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  那是不是在访问数据的地方，我们加个锁保护一下就能解决所有的并发问题了呢？显然没有这么简单。例如，对于上面示例，我们稍作修改，增加两个被 synchronized 修饰的 get() 和 set() 方法， add10K() 方法里面通过 get() 和 set() 方法来访问 value 变量，修改后的代码如下所示。对于修改后的代码，所有访问共享变量 value 的地方，我们都增加了互斥锁，此时是不存在数据竞争的。但很显然修改后的 add10K() 方法并不是线程安全的。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">long</span> count = <span class="number">0</span>;</span><br><span class="line">  <span class="function"><span class="keyword">synchronized</span> <span class="keyword">long</span> <span class="title">get</span><span class="params">()</span></span>&#123;</span><br><span class="line">    <span class="keyword">return</span> count；</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(<span class="keyword">long</span> v)</span></span>&#123;</span><br><span class="line">    count = v;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">add10K</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">int</span> idx = <span class="number">0</span>;</span><br><span class="line">    <span class="keyword">while</span>(idx++ &lt; <span class="number">10000</span>) &#123;</span><br><span class="line">      set(get()+<span class="number">1</span>)</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  我们来解读一下，这时候同时存在两个线程访问add10k方法然后同时进入while循环，然后同时调用get方法，但是因为get方法是synchronized修饰的所以同时只有一个线程能访问，比如这个时候线程A获得了优先权进行调用，读取到count为0，然后这个时候get方法执行完线程A释放锁，线程B获得了锁进入get方法也读取到count为0，然后可能这个时候线程B有执行了加一操作并且获得了set方法的锁进入set方法将count设置为1，这个时候可能线程A也执行了+1操作并且获得了线程B刚刚释放的锁来进行set操作，这个时候count又会被设置为1，其实本该结果为2(因为进行了两次加一操)，但是因为线程执行顺序的问题而导致程序不是按照我们期望的执行，所以这个是咸亨不安全的。</p><p>  这种问题，有个官方的称呼，叫<strong>竞态条件</strong>（Race Condition）。所谓竞态条件，指的是程序的执行结果依赖线程执行的顺序。例如上面的例子，如果两个线程完全同时执行，那么结果是 1；如果两个线程是前后执行，那么结果就是 2。在并发环境里，线程的执行顺序是不确定的，如果程序存在竞态条件问题，那就意味着程序执行的结果是不确定的，而执行结果不确定这可是个大 Bug。</p><p>  下面再结合一个例子来说明下竞态条件，就是前面文章中提到的转账操作。转账操作里面有个判断条件——转出金额不能大于账户余额，但在并发环境里面，如果不加控制，当多个线程同时对一个账号执行转出操作时，就有可能出现超额转出问题。假设账户 A 有余额 200，线程 1 和线程 2 都要从账户 A 转出 150，在下面的代码里，有可能线程 1 和线程 2 同时执行到第 6 行，这样线程 1 和线程 2 都会发现转出金额 150 小于账户余额 200，于是就会发生超额转出的情况。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">int</span> balance;</span><br><span class="line">  <span class="comment">// 转账</span></span><br><span class="line">  <span class="function"><span class="keyword">void</span> <span class="title">transfer</span><span class="params">(</span></span></span><br><span class="line"><span class="function"><span class="params">      Account target, <span class="keyword">int</span> amt)</span></span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.balance &gt; amt) &#123;</span><br><span class="line">      <span class="keyword">this</span>.balance -= amt;</span><br><span class="line">      target.balance += amt;</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>  那面对数据竞争和竞态条件问题，又该如何保证线程的安全性呢？其实这两类问题，都可以用<strong>互斥</strong>这个技术方案，而实现互斥的方案有很多，CPU 提供了相关的互斥指令，操作系统、编程语言也会提供相关的 API。从逻辑上来看，我们可以统一归为：<strong>锁</strong>。</p><h2 id="活跃性问题"><a href="#活跃性问题" class="headerlink" title="活跃性问题"></a>活跃性问题</h2><p>  所谓活跃性问题，指的是某个操作无法执行下去。我们常见的“死锁”就是一种典型的活跃性问题，当然除了死锁外，还有两种情况，分别是<strong>活锁</strong>和<strong>饥饿</strong>。</p><p>  但有时线程虽然<strong>没有发生阻塞</strong>，但仍然会存在执行不下去的情况，这就是所谓的“活锁”。</p><p>  可以类比现实世界里的例子，路人甲从左手边出门，路人乙从右手边进门，两人为了不相撞，互相谦让，路人甲让路走右手边，路人乙也让路走左手边，结果是两人又相撞了。这种情况，基本上谦让几次就解决了，因为人会交流啊。可是如果这种情况发生在编程世界了，就有可能会一直没完没了地“谦让”下去，成为没有发生阻塞但依然执行不下去的“活锁”。</p><p>  解决“活锁”的方案很简单，谦让时，尝试等待一个随机的时间就可以了。例如上面的那个例子，路人甲走左手边发现前面有人，并不是立刻换到右手边，而是等待一个随机的时间后，再换到右手边；同样，路人乙也不是立刻切换路线，也是等待一个随机的时间再切换。由于路人甲和路人乙等待的时间是随机的，所以同时相撞后再次相撞的概率就很低了。“等待一个随机时间”的方案虽然很简单，却非常有效，Raft 这样知名的分布式一致性算法中也用到了它。</p><p>  那“饥饿”该怎么去理解呢？所谓“饥饿”指的是线程因无法访问所需资源而无法执行下去的情况。“不患寡，而患不均”，如果线程优先级“不均”，在 CPU 繁忙的情况下，优先级低的线程得到执行的机会很小，就可能发生线程“饥饿”；持有锁的线程，如果执行的时间过长，也可能导致“饥饿”问题。</p><p>  解决“饥饿”问题的方案很简单，有三种方案：一是保证资源充足，二是公平地分配资源，三就是避免持有锁的线程长时间执行。这三个方案中，方案一和方案三的适用场景比较有限，因为很多场景下，资源的稀缺性是没办法解决的，持有锁的线程执行的时间也很难缩短。倒是方案二的适用场景相对来说更多一些。</p><p>  那如何公平地分配资源呢？在并发编程里，主要是使用公平锁。所谓公平锁，是一种先来后到的方案，线程的等待是有顺序的，排在等待队列前面的线程会优先获得资源。</p><h2 id="性能问题"><a href="#性能问题" class="headerlink" title="性能问题"></a>性能问题</h2><p>  使用“锁”要非常小心，但是如果小心过度，也可能出“性能问题”。“锁”的过度使用可能导致串行化的范围过大，这样就不能够发挥多线程的优势了，而我们之所以使用多线程搞并发程序，为的就是提升性能。</p><p>  所以我们要尽量减少串行，那串行对性能的影响是怎么样的呢？假设串行百分比是 5%，我们用多核多线程相比单核单线程能提速多少呢？</p><p>  有个阿姆达尔（Amdahl）定律，代表了处理器并行运算之后效率提升的能力，它正好可以解决这个问题，具体公式如下：</p><p>  <img src="/2019/08/09/Java并发编程学习——安全性，活跃性以及性能问题/2.jpg" alt="Amdahl"></p><p>  公式里的 n 可以理解为 CPU 的核数，p 可以理解为并行百分比，那（1-p）就是串行百分比了，也就是我们假设的 5%。我们再假设 CPU 的核数（也就是 n）无穷大，那加速比 S 的极限就是 20。也就是说，如果我们的串行率是 5%，那么我们无论采用什么技术，最高也就只能提高 20 倍的性能。</p><p>  所以使用锁的时候一定要关注对性能的影响。 那怎么才能避免锁带来的性能问题呢？这个问题很复杂，Java SDK 并发包里之所以有那么多东西，有很大一部分原因就是要提升在某个特定领域的性能。</p><p>  不过从方案层面，我们可以这样来解决这个问题。</p><p>  第一，既然使用锁会带来性能问题，那最好的方案自然就是使用无锁的算法和数据结构了。在这方面有很多相关的技术，例如线程本地存储 (Thread Local Storage, TLS)、写入时复制 (Copy-on-write)、乐观锁等；Java 并发包里面的原子类也是一种无锁的数据结构；Disruptor 则是一个无锁的内存队列，性能都非常好……</p><p>  第二，减少锁持有的时间。互斥锁本质上是将并行的程序串行化，所以要增加并行度，一定要减少持有锁的时间。这个方案具体的实现技术也有很多，例如使用细粒度的锁，一个典型的例子就是 Java 并发包里的 ConcurrentHashMap，它使用了所谓分段锁的技术（这个技术后面我们会详细介绍）；还可以使用读写锁，也就是读是无锁的，只有写的时候才会互斥。</p><p>  性能方面的度量指标有很多，我觉得有三个指标非常重要，就是：吞吐量、延迟和并发量。</p><ol><li>吞吐量：指的是单位时间内能处理的请求数量。吞吐量越高，说明性能越好。</li><li>延迟：指的是从发出请求到收到响应的时间。延迟越小，说明性能越好。</li><li>并发量：指的是能同时处理的请求数量，一般来说随着并发量的增加、延迟也会增加。所以延迟这个指标，一般都会是基于并发量来说的。例如并发量是 1000 的时候，延迟是 50 毫秒。</li></ol><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>  总之并发编程从微观上来讲就是要关注原子性，可见性，顺序性的问题。从宏观上来讲就要关注安全性，活跃性，性能的问题。而微观上三个问题的目的都是为了解决安全性的问题，但是解决问题的同时还可能产生新的问题，这就是活跃性的问题，而如何利用三个微观问题去解决安全问题随之带来了性能问题。</p>]]></content>
    
    <summary type="html">
    
      
      
        &lt;h2 id=&quot;安全性问题&quot;&gt;&lt;a href=&quot;#安全性问题&quot; class=&quot;headerlink&quot; title=&quot;安全性问题&quot;&gt;&lt;/a&gt;安全性问题&lt;/h2&gt;&lt;p&gt;  在并发编程中需要考虑三个基本问题——安全性，活跃性以及性能。&lt;/p&gt;
&lt;p&gt;  所谓安全性就是指程序能否按照我们
      
    
    </summary>
    
      <category term="Java并发编程" scheme="https://francisqiang.github.io/categories/Java%E5%B9%B6%E5%8F%91%E7%BC%96%E7%A8%8B/"/>
    
    
  </entry>
  
</feed>
