-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathatom.xml
177 lines (86 loc) · 56.6 KB
/
atom.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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Nesive</title>
<link href="https://blog.nesive.com/atom.xml" rel="self"/>
<link href="https://blog.nesive.com/"/>
<updated>2022-04-09T11:15:10.560Z</updated>
<id>https://blog.nesive.com/</id>
<author>
<name>Nesive</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>Java并发实现原理-多线程基础-JMM与happen-before(6)</title>
<link href="https://blog.nesive.com/b25d002f.html"/>
<id>https://blog.nesive.com/b25d002f.html</id>
<published>2022-04-09T09:23:03.000Z</published>
<updated>2022-04-09T11:15:10.560Z</updated>
<content type="html"><![CDATA[<h2 id="1-为什么会存在“内存可见性”问题?"><a href="#1-为什么会存在“内存可见性”问题?" class="headerlink" title="1.为什么会存在“内存可见性”问题?"></a>1.为什么会存在“内存可见性”问题?</h2><p>要解释这个问题,就涉及现代CPU的架构。如下是x86架构下CPU缓存的布局,即在一个CPU4核心下,L1、L2、L3三级缓存与主内存的布局。每个核上面有L1、L2缓存,L3为所有核共用。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/x86_cpu_cache_layout.png" alt="Java并发原理-x86架构下CPU缓存布局"></p><p>因为存在CPU缓存一致性协议,例如MESI,多个CPU之间的缓存不会出现不同步的问题,不会有“内存可见性”问题。</p><p>但是,缓存一致性协议对性能有很大损耗,为了解决这个问题,CPU的设计者们在这个基础上有进行了各种优化。例如,在计算单算和L1之间家林Store Buffer、Load Buffer(还有其他各种Buffer),如下图所示。</p><p>L1、L2、L3和主内存之间是同步的,有缓存一致性协议的保证,但是Store Buffer、Load Buffer和L1之间确实异步的。也就是说,往内存中写入一个变量,这个变量会保存在Store Buffer里面,稍后才异步地写入L1中,同时同步写入主内存中。</p><p>注意这里只是简要花了x86的CPU缓存体系,还没有进一步讨论SMP架构和NUMA的区别,还有其他CPU架构体系,例如PowerPC、MIPS、ARM等,不同CPU的缓存体系会有各种差异。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/x86_with_buffer.png" alt="Java并发原理-x86加了Store Buffer和Load Buffer的CPU缓存体系"></p><p>但站在操作系统内核的角度,可以统一看待这件事,也就是如下所示的操作系统内核视角下的CPU缓存模型。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/os_cpu_cache_model.png" alt="Java并发原理-操作系统内核视角下的CPU缓存模型"></p><p>多CPU、每隔CPU多核心,每隔核心上面可能还有多个硬件线程,对于操作系统来将,就相当于一个个的逻辑CPU。每隔逻辑CPU都有自己的缓存,这些缓存和主内存之间不是完全同步的。</p><p>对应到Java里,就是JVM抽象内存模型,如下图所示。</p><p>到此为止,介绍了不同CPU架构下复杂的缓存体系,也就回答了为什么会出现“内存可见性”问题。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/jvm_memory_model.png" alt="Java并发原理-操作系统内核视角下的CPU缓存模型"></p><h2 id="2-重排序和内存可见性的关系"><a href="#2-重排序和内存可见性的关系" class="headerlink" title="2.重排序和内存可见性的关系"></a>2.重排序和内存可见性的关系</h2><p>Store Buffer的延迟写入是重排序的一种,成为内存重排序(Memory Ordering)。除此以外,还有编译器和CPU的指令重排序。下面对重排序做了一个分类:</p><p>1.编译器重排序。对于没有先后依赖关系的语句,编译器可以重新调整语句的执行顺序。</p><p>2.CPU指令重排序。在指令级别,让没有依赖关系的多条指令并行。</p><p>3.CPU内存重排序。CPU有自己的缓存,指令执行顺序和写入主内存的顺序不完全一致。</p><p>在三种重排序中,第三类就是造成“内存可见性”问题的主要原因,下面再举例来进一步说明这个问题,如下所示:</p><pre class="line-numbers language-none"><code class="language-none">线程1:X = 1a = Y线程2:Y = 1b = X<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>假设X、Y是两个全局变量,初始的时候,X=0,Y=0。请问,这两个线程执行完毕之后,a和b的正确结果应该是什么?</p><p>很显然,线程1和线程2的执行先后顺序是不确定的,可能顺序执行,也可能交叉执行,最终正确的结果可能是:</p><pre class="line-numbers language-none"><code class="language-none">(1) a = 0,b = 1(2) a = 1,b = 0(3) a = 1,b = 1<span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span></span></code></pre><p>也就是不管谁先谁后,执行结果应该是这三种场景中的一种。但实际可能是 a = 0, b = 0。</p>]]></content>
<summary type="html"><h2 id="1-为什么会存在“内存可见性”问题?"><a href="#1-为什么会存在“内存可见性”问题?" class="headerlink" title="1.为什么会存在“内存可见性”问题?"></a>1.为什么会存在“内存可见性”问题?</h2><p>要解释这个问</summary>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/categories/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java多线程" scheme="https://blog.nesive.com/tags/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Java并发实现原理-多线程基础-volatile关键字(5)</title>
<link href="https://blog.nesive.com/56ec45e8.html"/>
<id>https://blog.nesive.com/56ec45e8.html</id>
<published>2022-04-09T08:43:59.000Z</published>
<updated>2022-04-10T14:30:29.173Z</updated>
<content type="html"><![CDATA[<blockquote><p>volatile这个关键字很不起眼,其使用场景和语义不像synchronized、wait()和notify()那么明显。正因为其隐晦,volatile关键字可能是多线程编程中被误解最多的一个。而关键字越隐晦,背后隐含的往往越复杂、越深刻。接下来将进一步由浅入深地从使用场景讨论到其底层实现。</p></blockquote><h2 id="1-64位写入的原子性(Half-Write)"><a href="#1-64位写入的原子性(Half-Write)" class="headerlink" title="1. 64位写入的原子性(Half Write)"></a>1. 64位写入的原子性(Half Write)</h2><p>举一个简答的例子,对于一个long类型变量的赋值和取值操作而言,在多线程场景下,线程A调用set(100),线程调用B调用get(),在某些场景下,返回值可能不是100.</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Example1</span><span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">long</span> a <span class="token operator">=</span> <span class="token number">0</span><span class="token punctuation">;</span> <span class="token comment">// 线程A调用set(100)</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">set</span><span class="token punctuation">(</span><span class="token keyword">long</span> a<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>a <span class="token operator">=</span> a<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 线程B调用get(),返回值是不是一定为100?</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">get</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">return</span> <span class="token keyword">this</span><span class="token punctuation">.</span>a<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>这有点反直觉,如此简单的一个赋值和取值操作,在多线程下面为什么会不对呢?这是因为JVM的规范并没有要求64位的long或者double的写入是原子的。在32位的机器上,一个64位变量的写入可能被拆分成两个32位的写操作来执行。这样一来,读取的线程就可能读到“一般的值”。解决办法也很简单,在long前面加上volatile关键字。</p><h2 id="2-内存的可见性"><a href="#2-内存的可见性" class="headerlink" title="2.内存的可见性"></a>2.内存的可见性</h2><p>不仅64位,32位或者位数更小的赋值和取值操作,其实也有问题。在之前讲过,线程关闭的标志位stopped为例,它是一个boolean类型的数组,也可能出现主线程把它设置成true,而工作线程读到的却还是false的情形,这就更反直觉了。</p><p>注意,这里并不是说永远读到的都是false,而是说一个线程写完之后,另外一个线程立即去读,读到的是false,但之后能读到true,也就是“最终一致性”,不是“强一致性”。这种特性,对于之前的例子没有太大影响,但是如果想实现无锁算法,例如实现一把自旋锁,就会出现一个线程把状态设置为true,另外一个线程读到的却还是false,然后两个线程都会拿到这把锁的问题。</p><p>所以,我们所说的“内存可见性”,指的是“写完之后立即对其他线程可见”,它的反面不是“不可见”,而是“稍后才能可见”。解决这个问题很容易,给变量加上volatile关键字即可。</p><p>“内存可见性”问题的出现,跟现代CPU的架构密切相关,后面会详细探讨。</p><h2 id="3-重排序-DCL问题"><a href="#3-重排序-DCL问题" class="headerlink" title="3. 重排序:DCL问题"></a>3. 重排序:DCL问题</h2><p>单例模式的线程安全写法不止一种,常用写法为DCL(Double Checking Locking),如下所示:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">class</span> <span class="token class-name">Singleton</span><span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">static</span> <span class="token class-name">Singleton</span> instance<span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token class-name">SIngleton</span> <span class="token function">getInstance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// 为了性能,延迟使用synchronized</span> <span class="token keyword">synchronized</span><span class="token punctuation">(</span><span class="token class-name">Singleton</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">if</span><span class="token punctuation">(</span>instance <span class="token operator">==</span> <span class="token keyword">null</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// 有问题的代码!!!</span> instance <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Instance</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> instance<span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上述的**instance = new Instance()**代码会有问题:其底层会分为三个操作:</p><p>1.分配一块内存</p><p>2.在内存上初始化成员变量</p><p>3.把instance引用指向内存</p><p>在这三个操作中,操作2和操作3可能重排序,即先把instance指向内存,再初始化成员变量,因为二者并没有先后的依赖关系。此时,另外一个线程可能拿到一个未完成初始化的对象。这时,直接访问里面的成员变量,就可能出错。这就是典型的”构造函数溢出“问题。解决办法很简单,就是为instance变量加上volatile修饰。</p><p>通过上面的例子,可以总结出volatile的三重功效:64位写入的原子性、内存可见性和进制重排序。接下来,我们进入volatile原理的探究。</p>]]></content>
<summary type="html"><blockquote>
<p>volatile这个关键字很不起眼,其使用场景和语义不像synchronized、wait()和notify()那么明显。正因为其隐晦,volatile关键字可能是多线程编程中被误解最多的一个。而关键字越隐晦,背后隐含的往往越复杂、越深刻。接下来将</summary>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/categories/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java多线程" scheme="https://blog.nesive.com/tags/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Java并发实现原理-多线程基础-wait()与notify()(4)</title>
<link href="https://blog.nesive.com/143e883d.html"/>
<id>https://blog.nesive.com/143e883d.html</id>
<published>2022-04-09T07:15:48.000Z</published>
<updated>2022-04-09T08:55:28.377Z</updated>
<content type="html"><![CDATA[<h2 id="1-生产者-消费者模型"><a href="#1-生产者-消费者模型" class="headerlink" title="1.生产者-消费者模型"></a>1.生产者-消费者模型</h2><p>生产者-消费者模型是一个常见的多线程编程模型,如图所示。</p><p>一个内存队列,多个生产者线程往内存队列中放数据;多个消费者线程从内存中取数据。要实现一个这样的编程模型,需要做以下几件事。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/thread_producer_consumer.png" alt="Java并发原理-线程的生产者-消费者模型"></p><p>1.内存队列本身要加锁,才能实现线程安全。</p><p>2.阻塞。当内存队列满了,生产者放不进去时,会被阻塞;当内存队列是空的时候,消费者无事可做,会被阻塞。</p><p>3.双向通知。消费者被阻塞之后,生产者放入新数据,要notify()消费者;反之,生产者被阻塞之后,消费者消费了数据,要notity()生产者。</p><p>第1件事必须做,第2和第3件事不一定要做。eg,可以采取一个简单的办法,生产者放不进去时,睡眠几百毫秒再重试,消费者取不到数据时,睡眠几百毫秒再重试。但这个办法效率低下,也不实时。所以,我们只讨论如何阻塞、如何通知的问题。</p><h3 id="1-1-如何阻塞?"><a href="#1-1-如何阻塞?" class="headerlink" title="1-1.如何阻塞?"></a>1-1.如何阻塞?</h3><p>方法1:线程自己阻塞自己,也就是生产者、消费者各自调用wait()和notify()。</p><p>方法2:用一个阻塞队列,当取不到或者放不进去数据的时候,入队/出队函数本身就是阻塞的。这也就是BlockingQueue的实现,后面详细讲述。</p><h3 id="1-2-如何双向通知?"><a href="#1-2-如何双向通知?" class="headerlink" title="1-2.如何双向通知?"></a>1-2.如何双向通知?</h3><p>方法1:wait()与notify()机制。</p><p>方法2:Condition机制。</p><p>此处先讲wait()与notify()机制,后面会专门讲Condition机制与BlockingQueue机制。</p><h2 id="2-为什么必须和synchronized一起使用?"><a href="#2-为什么必须和synchronized一起使用?" class="headerlink" title="2.为什么必须和synchronized一起使用?"></a>2.为什么必须和synchronized一起使用?</h2><p>在Java里面,wait()和notify()是Object的成员函数,是基础中的基础。为什么Java要把wait()和notify()放在如此基础的类里面呢?而不是作为向Thread一类的成员函数,或者其他的成员函数呢?</p><p>回答这个问题之前,先回答为什么wait()和notify()必须和synchronized一起使用?先看以下代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token class-name">Object</span> obj1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Object</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">synchronized</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> obj1<span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">synchronized</span><span class="token punctuation">(</span>obj1<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> obj1<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>或者下面的代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token keyword">this</span><span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>然后,开两个线程。线程A调用f1(),线程B调用f2()。答案已经很明显:两个线程之间要通讯,对于同一个对象来说,一个线程调用该对象的wait(),另一个线程调用该对象的notify(),该对象本身就需要同步!所以,在调用wait()、notify()之前,要先通过synchronized关键字同步给对象,也就是给该对象加锁。</p><p>前面讲了,synchronized关键字可以加在任何对象的成员函数上面,任何对象都可以成为锁。那么,wait()和notify()要同样如此普及,就放在Object里面了。</p><h2 id="3-为什么wait-的时候必须释放锁?"><a href="#3-为什么wait-的时候必须释放锁?" class="headerlink" title="3.为什么wait()的时候必须释放锁?"></a>3.为什么wait()的时候必须释放锁?</h2><p>当线程A进入synchronized(obj1)中之后,也就是对obj1上了锁。此时,调用wait()进入阻塞状态,一直不能退出synchronized代码块;那么,线程B永远无法进入synchronized(obj1)同步块里,永远没有机会调用notify(),岂不是死锁了?</p><p>这就涉及一个关键问题:在wait()的内部,会先释放obj1,然后进入阻塞状态,之后,它被另外一个线程用notify()唤醒,去重新拿锁!其次,wait()调用完成后,执行后面的业务逻辑代码,然后退出synchornized同步块,再次释放锁。<br>wait()内部的伪代码如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token comment">// 释放锁</span> <span class="token comment">// 阻塞,等待其他线程被notify</span> <span class="token comment">// 重新拿锁</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>只有如此才能避免上面所说的死锁问题。后面将Condition实现的时候,会再详细讨论这个问题。</p><h2 id="4-wait-与notify-问题"><a href="#4-wait-与notify-问题" class="headerlink" title="4.wait()与notify()问题"></a>4.wait()与notify()问题</h2><p>以上述的生产者-消费者模型来看,其伪代码大致如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">enqueue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">synchronized</span><span class="token punctuation">(</span>queue<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">while</span><span class="token punctuation">(</span>queue<span class="token punctuation">.</span><span class="token function">full</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> queue<span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 入队列</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token comment">// 通知消费者,队列中有数据了</span> queue<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">dequeue</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">synchronized</span><span class="token punctuation">(</span>queue<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">while</span><span class="token punctuation">(</span>queue<span class="token punctuation">,</span><span class="token function">empty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">{</span> queue<span class="token punctuation">.</span><span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 出队列</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token comment">// 通知生产者,队列中有空位了,可以继续放数据</span> queue<span class="token punctuation">.</span><span class="token function">notify</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>生产者本来只想通知消费者,但它把其他的生产者也通知了;消费者本来只想通知生产者,但它被其他的消费者通知了。原因就是wait()和notify()所作用的对象和synchronized所作用的对象是同一个,只能有一个对象,无法区分队列空和队列满两个条件。这正是Condition要解决的问题。</p>]]></content>
<summary type="html"><h2 id="1-生产者-消费者模型"><a href="#1-生产者-消费者模型" class="headerlink" title="1.生产者-消费者模型"></a>1.生产者-消费者模型</h2><p>生产者-消费者模型是一个常见的多线程编程模型,如图所示。</p>
<</summary>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/categories/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java多线程" scheme="https://blog.nesive.com/tags/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Java并发实现原理-多线程基础-synchronized关键字(3)</title>
<link href="https://blog.nesive.com/cd0cf4b7.html"/>
<id>https://blog.nesive.com/cd0cf4b7.html</id>
<published>2022-04-08T16:25:15.000Z</published>
<updated>2022-04-09T07:16:20.412Z</updated>
<content type="html"><![CDATA[<h2 id="1-锁的对象是什么?"><a href="#1-锁的对象是什么?" class="headerlink" title="1.锁的对象是什么?"></a>1.锁的对象是什么?</h2><p>对于不熟悉多线程原理的人来说,很容易误解synchronized关键字:它通常加在所有的静态成员函数和非静态成员函数之前,表面看好像是“函数之间的互斥”,其实不是。synchronized关键字其实是“给某个对象加了把锁”,这个锁究竟加在了什么对象上面?如下所示,给f1()、f2()加上synchronized关键字。</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">fl</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token keyword">synchronized</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span></span></code></pre><p>等价于如下代码:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">A</span><span class="token punctuation">{</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">synchronized</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">synchronized</span><span class="token punctuation">(</span><span class="token class-name">A</span><span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token class-name">A</span> a <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">A</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token punctuation">.</span><span class="token function">f1</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span>a<span class="token punctuation">.</span><span class="token function">f2</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>对于非静态成员函数,锁其实是加载对象a上面的;<br>对于静态成员函数,锁是加载A.class上面的。 当然,class本身也是对象。</p><p>这回答了一个关于synchronized的常见问题:一个静态成员函数和一个非静态成员函数,都加了synchronized关键字,分别被两个线程调用,他们是否互斥?很显然,因为是两把不同的锁,所以不会互斥。</p><h2 id="2-锁的本质是什么?"><a href="#2-锁的本质是什么?" class="headerlink" title="2.锁的本质是什么?"></a>2.锁的本质是什么?</h2><p>无论使用什么语言,只要是多线程的,就一定会涉及锁。既然锁如此常见,那么锁的本质是什么呢?</p><p>如图所示,多个线程访问同一个资源。线程就是一段运行中的代码;资源访问就是一个变量、一个对象或一个文件等;而锁就是要实现线程对资源的访问控制,保证同一个时间只能有一个线程去访问某一个资源。比如,线程就是一个游客,资源就是一个待参观的房子。这个房子同一时间值允许一个游客进去参观,当一个人出来后下一个人才能进去。而锁,就是这个房子门口的守卫。如果同一个时间允许多个游客参观,锁就会变成信号量,这点后面讨论。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/thread_lock_resource.png" alt="Java并发原理-线程的状态迁移过程"></p><p>从程序角度来看,锁就是一个“对象”,这个对象要完成以下几件事情:</p><p>1.这个对象内部得由一个标志位(state变量),记录自己有没有被某个线程占用(也就是记录当前有没有游客已经进入了房子)。最简单的情况是这个state有0和1两个取值,0标识没有线程占用,1标识某个线程占用了这个锁。</p><p>2.如果这个对象被某个线程占用,它得记录这个线程的thread ID,得知道自己是被那个线程占用了(也就是记录现在是谁在房子里面)。</p><p>3.这个对象还得维护一个thread id list,记录其他所有阻塞的、等待拿这个锁的线程(也就是记录所有在外面的等待的游客)。在当前线程释放锁之后(也就是把state从1变为0),从这个thread id list 里面去一个线程唤醒。</p><p>既然锁是一个“对象”,要访问的共享资源本身也是一个对象,例如前面的对象a,这两个对象可以合成一个对象。代码就变成synchronized(this){…},我们要访问的共享资源是对象a,锁也加在a上面的。当然,也可以另外新建一个对象,代码变成synchronized(obj1){…}。这时访问的共享资源对象是a,而锁是加载新建的对象obj1上面的。</p><p>资源和锁合二为一,使得在Java里面,synchronized关键字可以加在任何对象的成员变量上。这意味着,这个对象既是共享资源,同时也具备“锁”的功能。下面来看Java是如何做到让任何一个对象都具备“锁”的功能的,这也是synchronized的实现原理。</p><h2 id="3-synchronized实现原理"><a href="#3-synchronized实现原理" class="headerlink" title="3.synchronized实现原理"></a>3.synchronized实现原理</h2><p>答案在Java的对象头里。在对象头里,有一个块数据较Mark Word。在64位机器上,<strong>Mark Word</strong>是8个字节(64位)的,这64位中有2个重要字段:锁标志位和占用该锁的thread ID。因为不同版本JVM的实现,对象头的数据结构会有差异,这里不再论述。</p><p>此处值说明锁的实现思路,后面将ReentrantLock详细实现时,也基于类似的思路。在这个思路上,synchronized还会有偏向、自旋等优化策略,ReentrantLock同样也会用到这些优化策略。</p>]]></content>
<summary type="html"><h2 id="1-锁的对象是什么?"><a href="#1-锁的对象是什么?" class="headerlink" title="1.锁的对象是什么?"></a>1.锁的对象是什么?</h2><p>对于不熟悉多线程原理的人来说,很容易误解synchronized关键字:它通</summary>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/categories/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java多线程" scheme="https://blog.nesive.com/tags/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Java并发实现原理-多线程基础-InterruptedException函数与interrupt函数(2)</title>
<link href="https://blog.nesive.com/fafa8499.html"/>
<id>https://blog.nesive.com/fafa8499.html</id>
<published>2022-04-07T08:26:18.000Z</published>
<updated>2022-04-08T17:14:42.433Z</updated>
<content type="html"><![CDATA[<h2 id="1-什么情况下会考出InterruptedException"><a href="#1-什么情况下会考出InterruptedException" class="headerlink" title="1.什么情况下会考出InterruptedException?"></a>1.什么情况下会考出InterruptedException?</h2><p>Interrupt从字面意思看貌似是说,当一个线程运行到一半,将它中断,然后会抛出InterruptedException异常,然而并不是这样。</p><p>只有声明了会抛出InterruptedException的函数才会抛出异常。如下:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">native</span> <span class="token keyword">void</span> <span class="token function">sleep</span><span class="token punctuation">(</span><span class="token keyword">long</span> millis<span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">wait</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">final</span> <span class="token keyword">void</span> <span class="token function">join</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token keyword">throws</span> <span class="token class-name">InterruptedException</span><span class="token punctuation">{</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span></span></code></pre><h2 id="2-轻量级阻塞与重量级阻塞"><a href="#2-轻量级阻塞与重量级阻塞" class="headerlink" title="2.轻量级阻塞与重量级阻塞"></a>2.轻量级阻塞与重量级阻塞</h2><p>轻量级阻塞:能够被中断的阻塞,对应的线程状态是WAITTING或者TIMED_WAITTING。</p><p>重量级阻塞:不能被中断的阻塞,对应状态是BLOCKED。像synchronized这种。</p><p>如下图,是调用不同函数之后,一个线程完整的状态迁移过程。</p><p><img src="https://cdn.jsdelivr.net/gh/Nesive/nesive-butterfly-cdn/image/java_concurrency_principle/thread_state_transfer.png" alt="Java并发原理-线程的状态迁移过程"></p><p>初始线程处于NEW状态,调用start()之后开始执行,进入RUNNING或者READY状态。如果没有调用任何阻塞函数,线程只会在RUNNING和READY状态切换,即系统的时间片调度。这两种状态切换是由操作系统完成的,开发者基本没有机会介入,除了调用yield()函数,放弃对CPU的占用。</p><p>一旦调用了图中的任何函数,线程就会进入WAITTING或者TIMED_WAITTING状态,两者区别只是前者为无限期阻塞,后者传入了一个时间参数,阻塞了一个有限的时间。如果使用了synchronized关键字或者synchronized块,则会进入BLOCKED状态。</p><p>除了常见的阻塞/唤醒函数,还有一对不太常见的阻塞/唤醒函数,LockSupport.park() 和 LockSupport.unpark()。这对函数非常重要,Concurrent包中的Lock的实现即依赖这一对儿操作原语。</p><p>故而t.interrupted()函数的精确含义是<strong>“唤醒轻量阻塞”</strong>,而不是“中断一个线程”。</p><h2 id="3-t-interrupted-和Thread-interrupted-的区别"><a href="#3-t-interrupted-和Thread-interrupted-的区别" class="headerlink" title="3. t.interrupted()和Thread.interrupted()的区别"></a>3. t.interrupted()和Thread.interrupted()的区别</h2><p>因为t.interrupted()相当于给线程发送了一个唤醒的信号,所以如果线程此时恰好处于WAITTING或者TIMED_WAITTING状态,就会抛出一个InterrutedException,并且线程被唤醒。而如果线程此时没有被阻塞,则线程什么都不会做。但在后续,线程可以判断自己是否收到过其它线程发来的中断信号,然后做一些对应的处理。</p><p>这两个函数都是线程用于判断自己是否收到过中断信号的,前者是非静态函数,后者是静态函数。二者的区别在于,前者只是读取中断状态,不修改状态;后者不仅读取中断状态,还会重置中断标志位。</p>]]></content>
<summary type="html"><h2 id="1-什么情况下会考出InterruptedException"><a href="#1-什么情况下会考出InterruptedException" class="headerlink" title="1.什么情况下会考出InterruptedException?"</summary>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/categories/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java多线程" scheme="https://blog.nesive.com/tags/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
<entry>
<title>Java并发实现原理-多线程基础-线程的优雅关闭(1)</title>
<link href="https://blog.nesive.com/7f185260.html"/>
<id>https://blog.nesive.com/7f185260.html</id>
<published>2022-04-07T07:07:36.000Z</published>
<updated>2022-04-08T07:05:20.396Z</updated>
<content type="html"><![CDATA[<h2 id="1-stop-与destory-函数"><a href="#1-stop-与destory-函数" class="headerlink" title="1.stop()与destory()函数"></a>1.stop()与destory()函数</h2><p>线程是“一种运行中的代码或者函数”。既然是运行中就会涉及到是一个问题,在运行中的线程是否可以强制结束/关闭?</p><p>答案是肯定不可以的。在Java中,stop()和destory()之类的函数是官方明确不建议使用的。原因是如果强制杀死线程,那么线程执行所占用的资源,eg:文件描述符、网络连接不能正常关闭。</p><p>因此,一个线程一旦运行,就不要强制去打断,合理的关闭办法是让它自然运行完(函数执行结束),干净的释放掉所有资源,然后退出。如果是一个不断循环的运行的线程,就需要用到线程的通讯机制,让主线程通知它退出。</p><h2 id="2-守护线程"><a href="#2-守护线程" class="headerlink" title="2.守护线程"></a>2.守护线程</h2><p>在下面代码中:在main()函数中开启一个线程,不断的循环打印。请问:当main()函数退出之后,线程是否会被强制退出?整个进程是否会强制退出?</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"main start..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">Thread</span> thread1 <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">Thread</span><span class="token punctuation">(</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token operator">-></span><span class="token punctuation">{</span> <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">try</span><span class="token punctuation">{</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"thread1 executing.."</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token keyword">catch</span><span class="token punctuation">(</span><span class="token class-name">InterruptedExcetpion</span> ex<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span><span class="token punctuation">)</span><span class="token punctuation">;</span> thread1<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token class-name">System</span><span class="token punctuation">.</span>out<span class="token punctuation">.</span><span class="token function">println</span><span class="token punctuation">(</span><span class="token string">"main end..."</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>答案是不会。在C语言中,main()函数退出后,整个程序就退出了,但在Java中并非如此。</p><p>对于如上程序,在thread1.start()前面加一行代码:thread1.setDeamon(true)。当main()函数执行结束后,线程thread1就会退出,整个进程也会退出。</p><p>当在JVM进程里面开启多个线程时,这些线程被分为两类:守护线程与非守护线程。默认开启的是非守护线程。在Java中规定:当所有非守护线程退出后,整个JVM进程就会退出。意思就是守护线程不影响整个JVM退出。eg:垃圾回收线程就是守护线程,他们在后台默默工作,当开发者的所有前台线程(非守护线程)都退出之后,整个JVM进程就退出了。</p><h2 id="3-设置关闭的标志位"><a href="#3-设置关闭的标志位" class="headerlink" title="3.设置关闭的标志位"></a>3.设置关闭的标志位</h2><p>在上面代码中,线程是一个死循环。但在实际工作中,开发人员通常不会这么写,而是通过标志位来实现。如下所示:</p><pre class="line-numbers language-java" data-language="java"><code class="language-java"><span class="token keyword">class</span> <span class="token class-name">MyThread</span> <span class="token keyword">extends</span> <span class="token class-name">Thread</span><span class="token punctuation">{</span> <span class="token keyword">private</span> <span class="token keyword">boolean</span> stopped <span class="token operator">=</span> <span class="token boolean">false</span><span class="token punctuation">;</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">run</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">while</span><span class="token punctuation">(</span><span class="token operator">!</span>stopped<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token punctuation">.</span><span class="token punctuation">.</span><span class="token punctuation">.</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">public</span> <span class="token keyword">void</span> <span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token keyword">this</span><span class="token punctuation">.</span>stopped <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span><span class="token punctuation">}</span><span class="token keyword">public</span> <span class="token keyword">static</span> <span class="token keyword">void</span> <span class="token function">main</span><span class="token punctuation">(</span><span class="token class-name">String</span><span class="token punctuation">[</span><span class="token punctuation">]</span> args<span class="token punctuation">)</span><span class="token punctuation">{</span> <span class="token class-name">MyThread</span> t <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">MyThread</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> t<span class="token punctuation">.</span><span class="token function">start</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 通知线程t关闭</span> t<span class="token punctuation">.</span><span class="token function">stop</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 等待线程t退出while循环,自行退出</span> t<span class="token punctuation">.</span><span class="token function">join</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span><span class="token punctuation">}</span><span aria-hidden="true" class="line-numbers-rows"><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span><span></span></span></code></pre><p>上面的代码有一个问题:如过MyThread t在循环中阻塞在某个地方了,例如里面调用了object.wait()函数,那它可能永远没有机会再执行while(!stopped)的代码了,也就一直无法退出循环。</p><p>此时就要用到如下所讲的(<a href="/fafa8499.html" title="Java并发实现原理-多线程基础-InterruptedException函数与interrupt函数-2">Java并发实现原理-多线程基础-InterruptedException函数与interrupt函数-2</a>).</p>]]></content>
<summary type="html"><h2 id="1-stop-与destory-函数"><a href="#1-stop-与destory-函数" class="headerlink" title="1.stop()与destory()函数"></a>1.stop()与destory()函数</h2><p>线程</summary>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/categories/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java并发实现原理" scheme="https://blog.nesive.com/tags/Java%E5%B9%B6%E5%8F%91%E5%AE%9E%E7%8E%B0%E5%8E%9F%E7%90%86/"/>
<category term="Java多线程" scheme="https://blog.nesive.com/tags/Java%E5%A4%9A%E7%BA%BF%E7%A8%8B/"/>
</entry>
</feed>