-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathsearch.xml
50 lines (47 loc) · 20 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>hello</title>
<url>/blog/3610a686.html</url>
<content><![CDATA[<p>hello 111</p>
]]></content>
</entry>
<entry>
<title>synchronized</title>
<url>/blog/edea11bc.html</url>
<content><![CDATA[<h2 id="常见错误"><a href="#常见错误" class="headerlink" title="常见错误"></a>常见错误</h2><p>  最近比较深入的研究了一下synchronized,发现网上百分之90的博客都是错的,下边我首先来总结一下一些常见的错误:</p>
<ul>
<li><p><font color="red">无锁->偏向->轻量->重量</font> </p>
<p> 认为锁对象的状态转换是这么个流程的同学请刷个1,这里边存在两个问题</p>
<ol>
<li><p><strong>并不存在无锁到偏向</strong>这么一个过程,偏向锁只能从可偏向状态或者重偏向状态获得.<br>偏向锁模式存在偏向锁延迟机制:</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line">HotSpot 虚拟机在启动后有个 4s 的延迟才会对每个新建的对象开启偏向锁模式。</span><br><span class="line">JVM启动时会进行一系列的复杂活动,比如装载配置,系统类初始化等等。在这个过程中会使用大量</span><br><span class="line"><span class="keyword">synchronized</span>关键字对对象加锁,且这些锁大多数都不是偏向锁。为了减少初始化时间,</span><br><span class="line">JVM默认延时加载偏向锁。</span><br></pre></td></tr></table></figure>
<p> 在过了这个延迟偏向时间后创建出来的新对象都是可偏向状态,并不是无锁状态(除非触发了批量撤销),重偏向是触发了批量重偏向后的逻辑,只有两种获得偏向锁的途径,并不存在无锁->偏向锁 </p>
</li>
<li><p>偏向->轻量<br> 这个也不是一定的,轻量锁会出现在线程<strong>交替执行</strong>的时候.<br> 假如一把锁先偏向了线程A,此时A的同步代码块里的内容已执行完,并且A已释放锁(释放偏向锁并不会做什么),但是线程A还存活(如果不存活可能触发jvm层面的线程复用,直接想线程B与Jvm的线程绑定),现在线程B尝试去获取锁,此时线程B就会触发偏向锁撤销,然后将偏向锁升级为一把轻量锁. </p>
<p> 很多同学认为升级重量锁必须要激烈并发,其实不然,比如上边那个场景,如果线程B去获取锁的时候,线程A还没有释放锁,线程B会尝试获取轻量锁,但是轻量锁获取失败,线程B会直接开始膨胀,创建Monitor对象,升级为重量锁,所以升级重量锁和竞争激烈与否并没有关系,<strong>只要有竞争,就会升级成重量锁</strong>;</p>
</li>
</ol>
<p> 总结下获取锁的过程:首先看锁对象是否是可偏向,或者是否是可重偏向状态,如果是直接获取偏向锁,如果不是尝试获取轻量锁,轻量锁获取失败,直接开始膨胀为重量锁;</p>
</li>
<li><p><font color="red">轻量锁自旋</font><br> 轻量锁和偏向锁都不存在自旋,只是会尝试获取,CAS获取失败直接走下一步;</p>
</li>
</ul>
<h2 id="阅读源码与实验的一些发现"><a href="#阅读源码与实验的一些发现" class="headerlink" title="阅读源码与实验的一些发现"></a>阅读源码与实验的一些发现</h2><ul>
<li>批量重偏向修改epoch修改的是正在被锁定的对象<strong>和可偏向对象的epoch</strong>,网上很多博客说的都是修改正在被锁定对象的epoch</li>
<li>批量重偏向和批量撤销共用一个计数器 都会在25秒(默认值))后归零 </li>
</ul>
<figure class="highlight c++"><figcaption><span>JVM源码</span></figcaption><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">static</span> HeuristicsResult <span class="title">update_heuristics</span><span class="params">(oop o, <span class="keyword">bool</span> allow_rebias)</span> </span>{</span><br><span class="line"> markOop mark = o-><span class="built_in">mark</span>();</span><br><span class="line"> <span class="comment">//如果不是偏向模式直接返回</span></span><br><span class="line"> <span class="keyword">if</span> (!mark-><span class="built_in">has_bias_pattern</span>()) {</span><br><span class="line"> <span class="keyword">return</span> HR_NOT_BIASED;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 锁对象的类</span></span><br><span class="line"> Klass* k = o-><span class="built_in">klass</span>();</span><br><span class="line"> <span class="comment">// 当前时间</span></span><br><span class="line"> jlong cur_time = os::<span class="built_in">javaTimeMillis</span>();</span><br><span class="line"> <span class="comment">// 该类上一次批量撤销的时间</span></span><br><span class="line"> jlong last_bulk_revocation_time = k-><span class="built_in">last_biased_lock_bulk_revocation_time</span>();</span><br><span class="line"> <span class="comment">// 该类偏向锁撤销的次数</span></span><br><span class="line"> <span class="keyword">int</span> revocation_count = k-><span class="built_in">biased_lock_revocation_count</span>();</span><br><span class="line"> <span class="comment">// BiasedLockingBulkRebiasThreshold是重偏向阈值(默认20),BiasedLockingBulkRevokeThreshold是批量撤销阈值(默认40),BiasedLockingDecayTime是开启一次新的批量重偏向距离上次批量重偏向的后的延迟时间,默认25000。也就是开启批量重偏向后,经过了一段较长的时间(>=BiasedLockingDecayTime),撤销计数器才超过阈值,那我们会重置计数器。</span></span><br><span class="line"> <span class="keyword">if</span> ((revocation_count >= BiasedLockingBulkRebiasThreshold) &&</span><br><span class="line"> (revocation_count < BiasedLockingBulkRevokeThreshold) &&</span><br><span class="line"> (last_bulk_revocation_time != <span class="number">0</span>) &&</span><br><span class="line"> (cur_time - last_bulk_revocation_time >= BiasedLockingDecayTime)) {</span><br><span class="line"> <span class="comment">// This is the first revocation we've seen in a while of an</span></span><br><span class="line"> <span class="comment">// object of this type since the last time we performed a bulk</span></span><br><span class="line"> <span class="comment">// rebiasing operation. The application is allocating objects in</span></span><br><span class="line"> <span class="comment">// bulk which are biased toward a thread and then handing them</span></span><br><span class="line"> <span class="comment">// off to another thread. We can cope with this allocation</span></span><br><span class="line"> <span class="comment">// pattern via the bulk rebiasing mechanism so we reset the</span></span><br><span class="line"> <span class="comment">// klass's revocation count rather than allow it to increase</span></span><br><span class="line"> <span class="comment">// monotonically. If we see the need to perform another bulk</span></span><br><span class="line"> <span class="comment">// rebias operation later, we will, and if subsequently we see</span></span><br><span class="line"> <span class="comment">// many more revocation operations in a short period of time we</span></span><br><span class="line"> <span class="comment">// will completely disable biasing for this type.</span></span><br><span class="line"> k-><span class="built_in">set_biased_lock_revocation_count</span>(<span class="number">0</span>);</span><br><span class="line"> revocation_count = <span class="number">0</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">if</span> (revocation_count <= BiasedLockingBulkRevokeThreshold) {</span><br><span class="line"> revocation_count = k-><span class="built_in">atomic_incr_biased_lock_revocation_count</span>();</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果达到批量撤销阈值则返回HR_BULK_REVOKE</span></span><br><span class="line"> <span class="keyword">if</span> (revocation_count == BiasedLockingBulkRevokeThreshold) {</span><br><span class="line"> <span class="keyword">return</span> HR_BULK_REVOKE;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果达到批量重偏向阈值则返回HR_BULK_REBIAS</span></span><br><span class="line"> <span class="keyword">if</span> (revocation_count == BiasedLockingBulkRebiasThreshold) {</span><br><span class="line"> <span class="keyword">return</span> HR_BULK_REBIAS;</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 没有达到阈值则撤销单个对象的锁</span></span><br><span class="line"> <span class="keyword">return</span> HR_SINGLE_REVOKE;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<h3 id="验证"><a href="#验证" class="headerlink" title="验证"></a>验证</h3><figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="meta">@Slf4j</span></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Test</span> </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>{</span><br><span class="line"> <span class="comment">//延时产生可偏向对象</span></span><br><span class="line"> Thread.sleep(<span class="number">5000</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建一个list,来存放锁对象</span></span><br><span class="line"> List<Test> locks = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 线程1</span></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">30</span>; i++) {</span><br><span class="line"> <span class="comment">// 新建锁对象</span></span><br><span class="line"> Test lock = <span class="keyword">new</span> Test();</span><br><span class="line"> <span class="keyword">synchronized</span> (lock) {</span><br><span class="line"> locks.add(lock);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">//为了防止JVM线程复用,在创建完对象后,保持线程thead1状态为存活</span></span><br><span class="line"> Thread.sleep(<span class="number">100000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"thead1"</span>).start();</span><br><span class="line"></span><br><span class="line"> <span class="comment">//睡眠3s钟保证线程thead1创建对象完成</span></span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> <span class="comment">// 线程2</span></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">30</span>; i++) {</span><br><span class="line"> Test obj = locks.get(i);</span><br><span class="line"> <span class="keyword">synchronized</span> (obj) {</span><br><span class="line"> <span class="keyword">if</span>(i>=<span class="number">15</span>&&i<=<span class="number">21</span>||i>=<span class="number">38</span>){</span><br><span class="line"> log.debug(Thread.currentThread().getName()+<span class="string">"-第"</span> + (i + <span class="number">1</span>) + <span class="string">"次加锁执行中\t"</span>+</span><br><span class="line"> ClassLayout.parseInstance(obj).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(i==<span class="number">17</span>||i==<span class="number">19</span>){</span><br><span class="line"> log.debug(Thread.currentThread().getName()+<span class="string">"-第"</span> + (i + <span class="number">1</span>) + <span class="string">"次释放锁\t"</span>+</span><br><span class="line"> ClassLayout.parseInstance(obj).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">100000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"thead2"</span>).start();</span><br><span class="line"> <span class="comment">/*如果下边的20-30个对象任然可以重偏向,则说明批量重偏向和批量撤销共用一个计数器 都会在25秒(默认值)后归零,</span></span><br><span class="line"><span class="comment"> 而且最后new的新对象任然是可偏向状态101,如果注释掉的话这行代码则创建的新对象是无锁状态(批量撤销,关闭该Class的偏向锁功能),</span></span><br><span class="line"><span class="comment"> 而且下边的20-30个对象会变成轻量锁*/</span></span><br><span class="line"> Thread.sleep(<span class="number">26000</span>);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建一个list,来存放锁对象</span></span><br><span class="line"> List<Test> locks1 = <span class="keyword">new</span> ArrayList<>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 线程1</span></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">30</span>; i++) {</span><br><span class="line"> <span class="comment">// 新建锁对象</span></span><br><span class="line"> Test lock = <span class="keyword">new</span> Test();</span><br><span class="line"> <span class="keyword">synchronized</span> (lock) {</span><br><span class="line"> locks1.add(lock);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">//为了防止JVM线程复用,在创建完对象后,保持线程thead1状态为存活</span></span><br><span class="line"> Thread.sleep(<span class="number">100000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"thead3"</span>).start();</span><br><span class="line"></span><br><span class="line"> <span class="comment">//睡眠3s钟保证线程thead1创建对象完成</span></span><br><span class="line"> Thread.sleep(<span class="number">1000</span>);</span><br><span class="line"> <span class="comment">// 线程2</span></span><br><span class="line"> <span class="keyword">new</span> Thread(() -> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < <span class="number">30</span>; i++) {</span><br><span class="line"> Test obj = locks1.get(i);</span><br><span class="line"> <span class="keyword">synchronized</span> (obj) {</span><br><span class="line"> <span class="keyword">if</span>(i>=<span class="number">15</span>&&i<=<span class="number">21</span>||i>=<span class="number">38</span>){</span><br><span class="line"> log.debug(Thread.currentThread().getName()+<span class="string">"-第"</span> + (i + <span class="number">1</span>) + <span class="string">"次加锁执行中\t"</span>+</span><br><span class="line"> ClassLayout.parseInstance(obj).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(i==<span class="number">17</span>||i==<span class="number">19</span>){</span><br><span class="line"> log.debug(Thread.currentThread().getName()+<span class="string">"-第"</span> + (i + <span class="number">1</span>) + <span class="string">"次释放锁\t"</span>+</span><br><span class="line"> ClassLayout.parseInstance(obj).toPrintable());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> Thread.sleep(<span class="number">100000</span>);</span><br><span class="line"> } <span class="keyword">catch</span> (InterruptedException e) {</span><br><span class="line"> e.printStackTrace();</span><br><span class="line"> }</span><br><span class="line"> }, <span class="string">"thead4"</span>).start();</span><br><span class="line"> Thread.sleep(<span class="number">3000</span>);</span><br><span class="line"> Test test = <span class="keyword">new</span> Test();</span><br><span class="line"> log.debug(<span class="string">"新对象:"</span> + (ClassLayout.parseInstance(test).toPrintable()));</span><br><span class="line"> LockSupport.park();</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>其他介绍请参考<a href="https://github.com/farmerjohngit/myblog/issues/12">github</a>,这是一篇非常好的博客,写的很详细,我也就不重复总结了. </p>
]]></content>
<categories>
<category>Java</category>
<category>并发</category>
</categories>
<tags>
<tag>Java</tag>
<tag>并发</tag>
</tags>
</entry>
</search>