《Java编程思想》整理的一些学习笔记,有不对的地方,欢迎指出。
1 .资源冲突,如果两个线程确实是在修改同一个对象,共享资源的冲突将变得更糟糕,因为这有可能把对象设置成不正确的状态。通过简单的“信号量”概念引入,把它看作是在两个线程之间进行通信的标志对象。如果信号量的值是零,则它监控的资源是可用的,但如果这个值是非零的,则被监控的资源不可用,所以线程必须等待。当资源可用的时候,线程增加信号量的值,然后继续执行这个被监控的资源。把增加和减少信号量的操作定义为原子操作,这样就可保证两个线程同时访问同一资源的时候不至于冲突。
定义一个简化的信号量:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-class" style="box-sizing: border-box;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Semaphore</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">implements</span> <span class="hljs-title" style="box-sizing: border-box; color: rgb(102, 0, 102);">Invariant</span>{</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">volatile</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> semaphore = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">boolean</span> <span class="hljs-title" style="box-sizing: border-box;">available</span>(){<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> semaphore==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>;}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">acquire</span>(){ ++semaphore; }
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">release</span>(){ --semaphore; }
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> InvariantSate <span class="hljs-title" style="box-sizing: border-box;">invariant</span>(){
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">int</span> val = semaphore;
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span>( val==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>||val==<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">1</span> )
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> InvariantOk();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">else</span>
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">return</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> InvariantFailure(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Integer(val));
}
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li></ul>
(其中Invariant接口在博客:线程测试框架已给出)将semaphore字段设置为volatile ,以确保编译器不会对任何读取此值的操作进行优化。
2.解决共享资源竞争,之前说过,可以通过yield()和setPriority()来给线程调度机制提供建议,但这些建议未必会有多大的效果,这取决与你的具体平台和JVM实现。Java以提供关键字 synchronized 的形式,为防止资源冲突提供了内置支持。共享资源一般是以对象的形式存在的内存判断,但也可以是文件,输入/输出端口,或者是打印机。要控制对共享资源的访问,得先把它包装进一个对象。然后把所有要访问这个资源的方法标记为synchronized。即一旦某个线程处于一个标记为synchronized的方法中,那么在这个线程从该方法返回之前,其他所有要调用类中任何标记为synchronized方法的线程都会被阻塞。
每个对象都含有单一的锁(也称为监视器),这个锁本身就是对象的一部分(不用写任何特殊代码)。当在对象上调用其任意synchronized方法的时候,此对象都被加锁,这时该对象上的其他synchronized方法也只能等到前一个方法调用完并释放了锁之后才能被调用。
针对每一个类也有一个锁(作为类的Class对象的一部分),所以synchronized static 方法可以在类的范围内防止对static数据的并发访问。
3.原子操作,即不能被线程调度机制中断的操作;一旦操作开始,那么它一定可以在可能发生的“上下文切换”之前(切换到其他线程执行)执行完毕。如果问题中的变量类型是除long或double以外的基本类型,对这种变量进行简单的赋值或返回值操作的时候,才算是原子操作。然而,只要给long或double加上volatile,操作就是原子的了。注意,在JVM中的自增加操作并不是原子操作,它牵涉到一次读和一次写,所以即使在这样的简单操作中,也为线程出问题提供了空间。线程工作时,每个线程都可能拥有一个本地栈来维护一些变量的复本,如果把一个变量定义成volatile的,就等于告诉编译器不要做任何优化,直接在主存操作变量。
4.保证上述问题解决,做安全的做法就是使用下面的方法:
1)如果要对类中的某个方法进行同步控制,最好同步所有方法。如果忽略了其中一个,通常很难确定这么做是否会有负面影响。
2)当去除方法的同步控制时,要非常小心。通常这么做是基于性能方面的考虑,但在JDK1.3和JDK1.4中,同步控制所需的负担已经大大的减少。此外,只应在使用性能评价工具证实了同步控制确实是性能瓶颈的时候,才这么做。
5.如果只是希望防止多个线程同时访问方法内部的部分代码而不是防止整个方法,可以使用synchronized关键字来分离代码段,这种方式被称为“临界区”,此时,synchronized被用来指定某个对象,此对象的锁被用来对花括号内的代码进行同步控制:
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span>(syncObject){
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">// This code can be accessed</span>
<span class="hljs-comment" style="color: rgb(136, 0, 0); box-sizing: border-box;">//by only one thread at a time</span>
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li></ul>
使用同步控制块,而不是对整个方法进行同步控制,可以使多个线程访问对象的时间性能得到显著的提高。要注意的是,当对象中的方法在不同的锁上同步的时候,两个线程可以访问同一个对象:
<code class="hljs cs has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;">class DualSynch {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">private</span> Object syncObject = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Object();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> synchronized <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">f</span>() {
System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Inside f()"</span>);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
Thread.sleep(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">500</span>);
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InterruptedException e) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(e);
}
System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"leaving f()"</span>);
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">g</span>() {
synchronized (syncObject) {
System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Inside g()"</span>);
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">try</span> {
Thread.sleep(<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">500</span>);
} <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">catch</span> (InterruptedException e) {
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">throw</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> RuntimeException(e);
}
System.<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">out</span>.println(<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"leaving g()"</span>);
}
}
}
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">class</span> SyncObject{
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">static</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">main</span>(String[] args){
final DualSynch ds = <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> DualSynch();
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">new</span> Thread(){
<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">public</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span> <span class="hljs-title" style="box-sizing: border-box;">run</span>(){
ds.f();
}
}.start();;
ds.g();
}
}</code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right; background-color: rgb(238, 238, 238);"><li style="box-sizing: border-box; padding: 0px 5px;">1</li><li style="box-sizing: border-box; padding: 0px 5px;">2</li><li style="box-sizing: border-box; padding: 0px 5px;">3</li><li style="box-sizing: border-box; padding: 0px 5px;">4</li><li style="box-sizing: border-box; padding: 0px 5px;">5</li><li style="box-sizing: border-box; padding: 0px 5px;">6</li><li style="box-sizing: border-box; padding: 0px 5px;">7</li><li style="box-sizing: border-box; padding: 0px 5px;">8</li><li style="box-sizing: border-box; padding: 0px 5px;">9</li><li style="box-sizing: border-box; padding: 0px 5px;">10</li><li style="box-sizing: border-box; padding: 0px 5px;">11</li><li style="box-sizing: border-box; padding: 0px 5px;">12</li><li style="box-sizing: border-box; padding: 0px 5px;">13</li><li style="box-sizing: border-box; padding: 0px 5px;">14</li><li style="box-sizing: border-box; padding: 0px 5px;">15</li><li style="box-sizing: border-box; padding: 0px 5px;">16</li><li style="box-sizing: border-box; padding: 0px 5px;">17</li><li style="box-sizing: border-box; padding: 0px 5px;">18</li><li style="box-sizing: border-box; padding: 0px 5px;">19</li><li style="box-sizing: border-box; padding: 0px 5px;">20</li><li style="box-sizing: border-box; padding: 0px 5px;">21</li><li style="box-sizing: border-box; padding: 0px 5px;">22</li><li style="box-sizing: border-box; padding: 0px 5px;">23</li><li style="box-sizing: border-box; padding: 0px 5px;">24</li><li style="box-sizing: border-box; padding: 0px 5px;">25</li><li style="box-sizing: border-box; padding: 0px 5px;">26</li><li style="box-sizing: border-box; padding: 0px 5px;">27</li><li style="box-sizing: border-box; padding: 0px 5px;">28</li><li style="box-sizing: border-box; padding: 0px 5px;">29</li><li style="box-sizing: border-box; padding: 0px 5px;">30</li><li style="box-sizing: border-box; padding: 0px 5px;">31</li><li style="box-sizing: border-box; padding: 0px 5px;">32</li><li style="box-sizing: border-box; padding: 0px 5px;">33</li><li style="box-sizing: border-box; padding: 0px 5px;">34</li><li style="box-sizing: border-box; padding: 0px 5px;">35</li><li style="box-sizing: border-box; padding: 0px 5px;">36</li><li style="box-sizing: border-box; padding: 0px 5px;">37</li><li style="box-sizing: border-box; padding: 0px 5px;">38</li><li style="box-sizing: border-box; padding: 0px 5px;">39</li><li style="box-sizing: border-box; padding: 0px 5px;">40</li><li style="box-sizing: border-box; padding: 0px 5px;">41</li><li style="box-sizing: border-box; padding: 0px 5px;">42</li></ul>
DualSync对象的f()方法在this上同步(通过在整个方法同步),g()的同步控制块在syncObject对象上同步,因此,两个同步控制相互独立,两个方法同时鱼腥,所以它们没有在对象的同步控制上阻塞。因此,必须把访问共享资源的代码段包装进一个合适的同步控制块。
6.线程有四个状态:新建、就绪、死亡、阻塞(程序能够运行,但有某个条件阻止它运行)。进入阻塞状态的原因:
1)通过调用sleep(miliseconds)使线程进入休眠状态,在指定的时间内不运行。
2)调用wait()使线程挂起,直到线程得道了notify()或notifyAll()消息,线程才会进入就绪状态。
3)线程在等待某个输入/输出完成。
4)线程在某个对象上调用其同步方法,但是对象锁不可用。
7.线程之间为避免冲突,通过“握手机制”来进行的,这种握手可以通过Object的方法wait()和notify()来安全的实现。注意,调用sleep()的时候锁并没有被释放,而调用wait()方法的确释放了锁,这就意味着,再调用wait()期间,可以调用线程对象中的其他同步控制方法,当一个线程在方法里遇到了对wait()的调用的时候,线程的执行被挂起,对象上的锁被释放。
wait()有两种形式,一种与sleep()一样接受毫秒数,不同之处:
1)在wait()期间对象锁是释放的。
2)可以通过notify()、notifyAll(),或者指令时间到期,从wait()中回复执行。
另一种是不带参数的,wait()将无限等下去,知道接收到notify()或notifyAll()的消息。
8.wait()、notify()、notifyAll()这些方法是基类Object的一部分,而不是像sleep()那样属于Thread的一部分。因为这些功能要用到的锁也是所有对象的一部分,所以,你可以把wait()方法放在任何同步控制方法里,不用考虑这个类是否继承Thread或者实现Runnable接口。只能在同步控制方法或同步控制块中调用wait()、notify()、notifyAll()的线程在调用这些方法前必须“拥有”(获取)对象的锁。(sleep不用操作锁,所以可以在非同步控制方法里调用)。
<code class="hljs java has-numbering" style="display: block; padding: 0px; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace;font-size:undefined; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal; background: transparent;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">synchronized</span>(x){
x.notify();
}</code>