﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-Evan's Blog-随笔分类-Java笔记</title><link>http://www.blogjava.net/evanwhj/category/8117.html</link><description>Java, software development and others.</description><language>zh-cn</language><lastBuildDate>Tue, 27 Feb 2007 21:49:12 GMT</lastBuildDate><pubDate>Tue, 27 Feb 2007 21:49:12 GMT</pubDate><ttl>60</ttl><item><title>多线程(7): JT第5章笔记</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/11/34879.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Sat, 11 Mar 2006 15:11:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/11/34879.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/34879.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/11/34879.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/34879.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/34879.html</trackback:ping><description><![CDATA[&nbsp;&nbsp;&nbsp;&nbsp; 摘要: 《Java Threads》的第5章“Minimal Synchronization Techniques”，是这本书中到现在我认为最差的一章了，当然主要是我不喜欢JDK 1.5新推出的Atomic Class，而这一章却花了不少篇章来介绍，且牵强地改造打字程序，又语焉不详地指出这种改造的困难之处和可能带来的副作用，但却又不能从代码的实际运行中看到这种副作用，很有误导初学者的嫌疑。不过，我想，没有哪个初学者会冒风险为了用Atomic Class而将原本简单明了的算法改造得如此晦涩难懂，并且还有潜在的出错风险。所以，对于Atomic Class，我建议跳过不读，绝对没有什么损失。不过对于其中“5.1.3 Double-Checked Locking”和“5.3 Thread Local Variables”这两节倒要着重读一读，尤其是Thread Local，应该说是Java中一个比较重要的多线程工具。&nbsp;&nbsp;<a href='http://www.blogjava.net/evanwhj/archive/2006/03/11/34879.html'>阅读全文</a><img src ="http://www.blogjava.net/evanwhj/aggbug/34879.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-11 23:11 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/11/34879.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程(6): Java Threads例子的一个问题</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/09/34556.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Thu, 09 Mar 2006 14:11:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/09/34556.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/34556.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/09/34556.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/34556.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/34556.html</trackback:ping><description><![CDATA[看到《Java Threads》第5章，介绍了JDK 1.5新加的一些所谓原子类(Atomic Classes)，总感觉有点为原子而原子，实际操作中，又有多少人会为了少许的性能提升而刻意去用这些别扭的操作而放弃直观的synchronize关键字或者Lock类呢？不过，这里不是想讨论这个，而是当其用Atomic Classes来改造它的打字程序后，解释用原子类只是保证类似递增、递减、赋值等操作的原子性，而不能保证其所在的方法一定是线程安全的，然后说，有可能按键事件的处理可能需要等待resetScore()处理完才能执行，而这会导致错误的评分（被当成多敲了键）。由于前几章的内容相对比较简单易懂，所以也没有很仔细的运行那些例子。这里为了验证一下，就运行了一下第4章的例子，然后发现，基本上第一次的评分总是错的。这就引起了我的注意，因为，一般情况下，如果是race condition导致的错误是很难重现的，这么明显的错误很可能是程序逻辑上的错误。仔细看了一下代码，发现在start按钮的事件处理方法里，有下面这样一段代码：<br />
<div class="mycode">startButton.addActionListener(new ActionListener() {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; public void actionPerformed(ActionEvent evt) {<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; displayCanvas.setDone(false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; producer.setDone(false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; startButton.setEnabled(false);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; stopButton.setEnabled(true);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; feedbackCanvas.setEnabled(true);<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; feedbackCanvas.requestFocus();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; score.resetScore();<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; }<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; });</div>
注意重置成绩的调用放在了最后，此时，随机生成字符的线程应该被唤醒并产生了第一个字符，然后，resetScore()将需要输入的字符又设成了-1，所以，当你第一次输入字符时，总是被认为是多击了一次键而扣1分:(。既然这样，那停止然后再启动也应该会发生这个错误啊。而事实上的确是这样。我想，这不应该看做是race condition吧，有什么样的同步技术能够避免这个问题呢？除非另外弄个标志，当成绩没有被重置前，不能产生第一个字符。当然，这是不需要的，只要将score.resetScore()放到第一句就可以了。<br /><br />然后又运行了第3章的例子，发现基本上没有这个问题。难道第3章的代码是正确的？打开源代码一看，重置成绩的方法还是放在最后，那这里为什么又是正确的呢？我想，大约是第3章的例子中，每次点击start按钮，都重新创建一个线程对象的原因吧。由于创建对象和初始化线程需要一定的时间，刚好给了主线程重置成绩的机会。<br /><br />不知道作者有意为之呢，还是疏忽，不过，这样的错误不能算是race condition的例子。<img src ="http://www.blogjava.net/evanwhj/aggbug/34556.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-09 22:11 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/09/34556.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程(5): JT第4章笔记</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/06/33938.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Mon, 06 Mar 2006 14:21:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/06/33938.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/33938.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/06/33938.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/33938.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/33938.html</trackback:ping><description><![CDATA[<a href="http://www.blogjava.net/evanwhj/archive/2006/03/05/33768.html">第3章</a>主要介绍了数据的同步(Data Synchronization)，这一章则主要介绍线程之间的同步方法(Thread Notification)，同样包括传统的wait-and-notify方法和JDK 1.5新推出的Condition Variable。在多线程编程中，数据同步和线程同步是两个最基本也是最关键的部分。<br />《Java Threads》一书中通过考察打字程序中当按下start和stop按钮后，每次都创建两个新的线程的效率问题来引入线程同步的概念，当然不是线程同步的主要用处。不过，教科书归教科书，实际运用则又是另一回事。所以，通过书本学习语法，通过实践来获得运用经验。<br /><br />
<h4>4.1 Wait and Notify</h4>
1. 等待/唤醒类似于Solaris或POSIX中的条件变量(conditon variables)，或者Windows中的事件变量(evant variable)或者信号量(signal)，用于某个/多个线程暂停等待某个条件的满足，而该条件将由其它线程来设置的情况。<br />2. 在Java中，就像每个对象有一个锁之外，任何对象都可以提供等待/唤醒的机制。就像Java中的synchronized总是表示获得某个具体对象的锁一样，wait和notify也总是等待某个具体的对象，并由该对象唤醒；同样，获得某个对象上的锁不一定是该对象需要同步一样，等待和唤醒的条件也不一定是与之绑定的对象。<br />3. Java中wait-and-notify的几个方法：
<div class="mycode">void wait(): 使当前线程处于等待状态，直到其它线程调用了nofity()或者notifyAll()方法为止。<br />void wait(long timeout): 使当前线程处于等待状态，直到其它线程调用了nofity()或者notifyAll()方法，或者超过了指定的时间(单位为ms)为止<br />void wait(long timeout, int nanos)：与wait(long)一样，只是在某些JVM中可以精确到奈秒。<br />void notify(): 唤醒<strong><font color="#ff0000">一个</font></strong>正在等待该对象的线程。<br />void notifyAll(): 唤醒<strong><font color="#ff0000">所有</font></strong>正在等待该对象的线程。 </div>
注意：<strong>任何等待和唤醒方法都必须在与之对应的对象的同步方法或同步块里调用。</strong>即：wait-and-notify必须和与之对应的synchronized关键词一起使用的。<br />4. wait()和sleep()的主要区别：<br />　　1) sleep()可以在任何地方调用，而wait()需要在同步方法或同步块中调用；<br />　　2) 进入wait()函数时，JVM会自动释放锁，而当从wait()返回即被唤醒时，又会自动获得锁；而sleep()没有这个功能，因此如果在wait()的地方用sleep()代替，则会导致相应的nofity()方法在等待时不可能被触发，因为notify()必须在相应的同步方法或同步块中，而此时这个锁却被sleep()所在的方法占用。也就是说，wait-and-notify不可能与sleep()同时使用。<br /><br /><strong>4.1.1 The Wait-and-Notify Mechanism and Synchronization</strong><br />1. 这一节详细的讲解了wait-and-notify机制和synchronized的关系，主要是两点：1)wait-and-notify必须和synchronized同时使用；2)wait()会自动释放和获取锁；<br />2. 这一节中举了一个例子用来解释可能存在当条件被不满足时也有可能被唤醒的情况：
<div class="mycode">　　1) 线程T1调用一个同步方法；<br />　　2) T1检测状态变量，发现其不满足条件；<br />　　3) T1调用wait()，并释放锁；<br />　　4) 线程T2调用另外一个同步方法，获得锁；<br />　　5) 线程T3调用另外一个同步方法，由于T2获得了锁，所以处于等待状态；<br />　　6) T2修改状态变量，使其满足条件，并调用notify()方法；<br />　　7) T3获得锁，然后处理数据，并将状态变量又设置为不满足条件的状态；<br />　　8) T3处理完毕返回；<br />　　9) T1被唤醒，但实际上此时条件并不满足。 </div>
这个例子刚好印证了《Effective Java》中&quot;Item 50: Never invoke wait outside a loop&quot;和《Practical Java》中&quot;实践54：针对wait()和notifyAll()使用旋锁(spin locks)&quot;。即总是用下面这种方式来调用wait():
<div class="mycode">
<pre>    synchronized(obj) {
while(&lt;condition does not hold&gt;)
wait();
<br />
... // Perform action appropriate to condition
}</pre>
</div>
或者象《Practical Java》中一样：
<div class="mycode">
<pre>    synchronized(obj) {
while(&lt;condition does not hold&gt;) {
try {
wait();
} catch (InterruptedException e) {}
}<br /><br />
... // Perform action appropriate to condition
}</pre>
</div>
3. 调用wait()的线程T可能在以下几种情况被唤醒：<br />　　1) 其它线程调用了notify()，而刚好线程T得到了通知；<br />　　2) 其它线程调用了notifyAll()；<br />　　3) 其它线程中断了线程T；<br />　　4) 由于JVM的原因，导致了spurious wakeup。<br /><br /><strong>4.1.2 wait(), notify(), and notifyAll()</strong><br />1. 正像多个线程等待同一对象上的锁，当锁释放时，无法确定哪个线程会得到那个锁一样；当有多个线程在wait()时，当另外一个线程调用nofity()的时候，也不能确定哪个线程会被唤醒； 2. 因此在《Practical Java》的&quot;实践53：优先使用notifyAll()而非notify()&quot;建议的一样，结合实践54，可以比较好的解决线程唤醒的问题。<br /><br /><strong>4.1.3 Wait-and-Notify Mechanism with Synchronized blocks</strong><br />再次强调必须在同一个对象的synchronized方法或块内调用该对象上的wait和notify方法。<br /><br />
<h3>4.2 Condition Variables</h3>
1. 就像上面反复强调的一样，wait-and-notify机制是与特定对象及其上的锁是绑定在一起的，锁和唤醒对象不能分开，这在某些情况下不是很方便；<br />2. JDK 1.5提供Condition接口来提供与其它系统几乎一致的condition variables机制；<br />3. Condition对象由Lock对象的newCondition()方法生成，从而允许一个锁产生多个条件变量，可以根据实际情况来等待不同条件；<br />4. 该书的例子没有什么特别的实际意义，但JDK 1.5文档中提供了一个例子，能充分说明使用Condition Variables使得程序更加清晰易读，也更有效率：
<div class="mycode">
<pre>class BoundedBuffer {<br />    final Lock lock = new ReentrantLock();<br />    final Condition notFull  = lock.newCondition(); <br />    final Condition notEmpty = lock.newCondition();
final Object[] items = new Object[100];
int putptr, takeptr, count;<br /><br />
public void put(Object x) throws InterruptedException {<br />        lock.lock();<br />        try {<br />            while (count == items.length) <br />            notFull.await();<br />            items[putptr] = x; <br />            if (++putptr == items.length) putptr = 0;<br />            ++count;<br />            notEmpty.signal();<br />        } finally {<br />            lock.unlock();<br />        }<br />    }<br />
public Object take() throws InterruptedException {<br />        lock.lock();<br />        try {<br />            while (count == 0) <br />                notEmpty.await();<br />            Object x = items[takeptr]; <br />            if (++takeptr == items.length) takeptr = 0;<br />            --count;<br />            notFull.signal();<br />            return x;<br />        } finally {<br />            lock.unlock();<br />        }<br />    } <br />}</pre>
</div>
具体的说明请参考JDK 1.5的文档。<br />5. 除了用lock和await-and-signal来代替synchronized和wait-and-notify外，其语义和机制基本一样。await()在进入前也会自动释放锁，然后再返回前重新获得锁；<br />6. 使用Condition Variables的原因：<br />　　1) 如果使用Lock对象，则必须使用condition variables；<br />　　2) 每个Lock对象可以创建多个condition variable.<img src ="http://www.blogjava.net/evanwhj/aggbug/33938.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-06 22:21 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/06/33938.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程(4)：JT第3章笔记</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/05/33768.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Sun, 05 Mar 2006 15:25:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/05/33768.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/33768.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/05/33768.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/33768.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/33768.html</trackback:ping><description><![CDATA[<h3>Chapter 3. Data synchronization</h3>
在<a href="http://blog.csdn.net/2006/02/3jt2.html">第二章</a>中介绍了如何创建线程对象、启动和终止线程。但多线程编程的关键在于多个线程之间数据的共享和同步，从这一章开始，将详细介绍线程之间数据的共享和同步的各种方法。<br /><br />  <strong>3.1 The Synchronized Keywor</strong> <br />1. synchronized是Java中最基本也最常用的用来编写多线程安全代码的关键字，用以保护对多线程共享的数据的操作总是完整的；<br /> 2. <strong>Atomic</strong>: 当一个操作被定义成原子操作时，意味着该操作在执行过程中不会被打断；原子操作可以由硬件保证或者通过软件来模拟；<br /> 3. <strong>Mutex Lock</strong>: 在很多多线程系统，通过互斥锁来保护的共享数据。Java中的任何一个对象都有一个与之相关的锁，当一个方法被声明成synchronized时，就表示当线程进入该方法前，必须获得相应对象的锁，在执行完毕再释放这个锁。从而保证同一时刻只有一个线程调用该对象上的被声明为synchronized的方法。注意：Java的互斥锁只能加在对象级上，获得某个对象的锁，并不能保证该对象的属性和其它非synchronized的方法是线程安全的；也不能保证受保护的方法里调用的其它对象是多线程安全的，除非任何调用这些没有被保护的方法或者对象只通过受保护的方法进行调用。所以，编写线程安全的代码关键就在于规划方法和对象之间的调用关系，并尽量采用相同对象的锁来进行同步控制。<br /> <br /> <strong>3.2 The Volatile Keyword</strong> <br />1. Scope of a Lock: 锁的作用范围即获得和释放锁之间的那段时间。<br /> 2. Java标准虽然声明存取一个非long和double变量的操作是原子操作，但由于不同虚拟机实现的差异，在多线程环境下每个线程可能会保留自己的工作拷贝，而导致变量的值产生冲突。为了避免这种情况的发生，可以有两种方法：<br /> 　　1) 为变量创建声明为synchronized的setter和getter方法，然后任何调用（包括在类类部）该变量的地方都通过setter和getter方法；<br /> 　　2) 采用volatile声明，确保每次存取这些属性时都从主内存中读入或者写入主内存中；<br /> 　　3) volatile仅仅用于解决Java内存模式导致的问题，只能运用在对该变量只做一个单一装载或写入操作且该方法的其它操作并不依赖该变量的变化。如：在一个循环体中作为递增或递减变量时就不能使用volatile来解决线程同步的问题。<br /> 3. volatile的使用是有限的，一般而言，仅仅将其作为强制虚拟机总是从主内存读写变量的一个手段，或者某些需要其参数声明为volatile的函数。 <br /><br /><strong>3.3 More on Race Conditions</strong> <br />本节以打字游戏中显示成绩的例子来解释了可能存在的race condition。关键要注意以下几点：<br /> 1. 操作系统会随机的切换多个线程的运行，因此当多个线程调用同一个方法时，可能在<em style="color: rgb(255, 0, 0);"><strong>任何地方</strong></em>被暂停而将控制权交给另外的线程；<br /> 2. 为了减少被误用的可能，总是假设方法有可能被多个线程调用；<br /> 3. 锁仅仅与某个特定实例相关，而与任何方法和类都无关，这一点当需要存取类属性或方法时要特别注意；<br /> 4. 任何时候只有一个线程能够运行某个类中一个被声明为synchronized的静态方法；一个线程只能运行某个特定实例中一个被声明为synchronized的非静态方法。<br /> <br /> <strong>3.4 Explicit Locking</strong> <br />1. 学过Win32下编写多线程的朋友刚开始可能会被Java的Synchronized关键词搞糊涂。因为Java中的任何一个对象都有一个与之相关的锁，而不象在Win32下要先定义一个互斥量，然后再调用一个函数进入或者离开互斥区域。在JDK 1.5以后也开始提供这种显示声明的锁。JDK 1.5中定义了一个Lock接口和一些类，允许程序员显示的使用锁对象。<br />2. 在Lock接口里有两个方法：lock()和unlock()用来获取和释放锁对象，从而保证受保护的代码区域是线程安全的。<br />3. 使用锁对象的一个好处在于可以被保存、传递和抛弃，以在比较复杂的多线程应用中使用统一的锁。<br /> 在使用锁对象时，总是将lock()和unlock()调用包含在try/finally块中，以防止运行时异常的抛出而导致死锁的情况。<br /> <br /> <strong>3.5 Lock Scope</strong> <br />利用lock()和unlock()方法，我们可以在任何地方使用它们，从一行代码到跨越多个方法和对象，这样就能根据程序设计需要来定义锁的作用(scope of lock)范围，而不是象以前局限在对象的层次上。 <strong><br /><br />3.5.1 Synchronized Blocks</strong><br /> 1. 利用synchronized关键字也能建立同步块，而不一定非得同步整个方法。<br /> 2. 同步一段代码时，需要明确的指定获取哪个对象的锁(这就类似于锁对象了)，这样，就可以在多个方法或对象中共享这个锁对象。<br /> <br /> <strong>3.6 Choosing a Locking Mechanism</strong> <br />使用锁对象还是同步关键字(synchronized)，这个取决于开发员自己。使用synchronized比较简单，但相对而言，比较隐晦，在比较复杂的情况下(如要同时同步静态和非静态方法时)，没有锁对象来得直观和统一。一般而言，synchronized简单，易于使用；但同时相对而言效率较低，且不能跨越多个方法。<br /><br /> <strong>3.6.1 The Lock Interface</strong><br />
<div class="mycode">
<pre>public interface Lock {<br />     void lock();<br />     void lockInterruptibly() throws InterruptedException;<br />     boolean tryLock();<br />     boolean tryLock(long time, TimeUnit unit)<br />         throws InterruptedException;<br />     void unlock();<br />     Condition newCondition();  <br />}</pre>
</div>
其中的tryLock()和tryLock(long, TimeUnit)将尝试获取锁对象，如果不能获取或在指定时间内不能获取，将立即返回。调用者可以根据返回值来判断是否获得了锁对象以做进一步的操作。<br /> <br /> <strong>3.7 Nested Locks</strong> <br />1. 在一个同步方法内调用同一个对象上的另外的同步方法的情况，称之为嵌套锁(nested locking)。系统将自动执行嵌套的同步方法，而无须等待锁的释放。因此，有的时候，即使一些私有方法仅仅被已同步的方法调用，我们也给其加上synchronized关键字，以减少后续维护时可能产生的误导。<br /> 2. ReenterantLock类也支持嵌套锁。在ReenterantLock类维持一个计数器，只有当这个计数器为0时，才会释放锁。注意：<strong>这个特性是ReenterantLock类的特性，而不是所有实现Lock接口的类的特性。</strong><br /> 3. 需要支持嵌套锁的一个原因是方法之间交叉调用(cross-calling)。设想对象a的方法M1调用对象b的方法N1，然后N1再调用对象a的方法M2，而M1和M2都是同步方法，如果不支持嵌套锁，则N1将在调用M2时等待M1释放锁，而M1则由于N1没有返回永远也不会释放锁，这样就产生了死锁。<br /> 4. synchronized和Lock接口并没有提供锁对象被嵌套获取的次数，但ReentrantLock则提供了这样一种机制：<br />
<div class="mycode">
<pre>public class ReentrantLock implements Lock {<br />     public int getHoldCount();<br />     public boolean isLocked();<br />     public boolean isHeldByCurrentThread();<br />     public int getQueueLength();<br />     ...  <br />}</pre>
</div>
其中：<br /> &nbsp;&nbsp;&nbsp; 1) getHoldCount()返回当前线程的获取次数，返回0并不表示该锁是可获取的，有可能没有被当前线程获得；<br /> &nbsp;&nbsp;&nbsp;&nbsp;2) isLocked()判断该锁对象是否被任何线程获得；<br /> &nbsp;&nbsp;&nbsp;&nbsp;3) isHeldByCurrentThread()判断是否由当前线程获得；<br /> &nbsp;&nbsp;&nbsp;&nbsp;4) getQueueLength()用来估计当前有多少线程在等待获取这个锁对象。<br /> <br /> <strong>3.8 Deadlock</strong> <br />介绍了死锁的概念，并修改例子代码来演示deadlock的产生。死锁一般产生在多个线程在多个锁上同步时产生的；当然，多个线程在判断多个条件的时候，也有可能产生死锁。<br /> <br /> <strong>3.9 Lock Fairness</strong> <br />1. Java里锁的获取机制依赖于底层多线程系统的实现，并不保证一个特定的顺序；<br /> 2. ReentrantLock类提供一个先进先出(first-in-first-out)的获取锁的选项(在创建锁对象时传入true值)。<img width="1" height="1" src="http://blog.csdn.net/evanwhj/aggbug/616068.aspx" alt="" /><br />文章来源:<a href="http://blog.csdn.net/evanwhj/archive/2006/03/05/616068.aspx">http://blog.csdn.net/evanwhj/archive/2006/03/05/616068.aspx</a><img src ="http://www.blogjava.net/evanwhj/aggbug/33768.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-05 23:25 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/05/33768.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程(3)：JT第2章笔记</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/05/33769.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Sun, 05 Mar 2006 15:25:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/05/33769.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/33769.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/05/33769.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/33769.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/33769.html</trackback:ping><description><![CDATA[<p><span style="font-size: 120%;"><strong>Chapter2: Thread Creation and Management</strong></span></p>
<p><strong>2.1 What Is a Thread? </strong></p>
<p>介绍了什么是线程，以及线程(thread, multithread)与进程(process, mutltitask)的区别。其中的一个重要区别是对共享数据的访问。进程可以共享操作系统级别的某些数据区域，如剪贴板；而线程是对程序自有的数据进行共享。进程之间对共享数据的存取方法是特殊的，因此自然能够得到程序员的注意；而线程之间对共享数据的存取与对线程自己的数据的存取方法是一样的，所以常常比较隐蔽，而被程序员忽略。其实，这也是多线程开发比较难的地方。所以，这一节最后说：&quot;<em style="color: rgb(255, 0, 0);"><strong>A thread, then, is a discrete task that operates on data shared with other threads.</strong></em>（线程就是一个在与其它线程共享的数据上进行操作的单独任务。）&quot;<br /><br /> <strong>2.2 Creating a Thread</strong> </p>
<p>1. 有两种方法创建线程：使用Thread类或者Runnable接口 <br />2. 示例程序竟然是编一个打字游戏，所以，这本书的门槛还是有点高的:(。当然可以从该书的站点直接下载<a href="http://examples.oreilly.com/jthreads3/javathreads3.examples.zip">代码</a>。 </p>
3. Thread class<br />
<div style="border: 1px dotted blue; padding: 10px; width: 100%; background-color: rgb(192, 192, 192); font-size: 80%; overflow-x: auto;">
<pre><font size="2">package java.lang;<br />public class Thread implements Runnable {<br />     public Thread( );<br />     public Thread(Runnable target);<br />     public Thread(ThreadGroup group, Runnable target);<br />     public Thread(String name);<br />     public Thread(ThreadGroup group, String name);<br />     public Thread(Runnable target, String name);<br />     public Thread(ThreadGroup group, Runnable target, String name);<br />     public Thread(ThreadGroup group, Runnable target, String name,<br />                   long stackSize);<br />     public void start( );<br />     public void run( ); <br />}</font></pre>
</div>
<strong>Thread name</strong>: 线程名，用以打印线程信息时用，缺省为Thread-N。 <br /><strong>Runnable target</strong>：可运行的目标。一个可运行的对象就是一串可由线程执行的指令。缺省就是在run()方法中编写的内容。 <br /><strong>Thread group</strong>：线程组。对于大多数应用来说没什么意义，缺省在同一组。 <br /><strong>Stack size</strong>：线程堆栈大小，与平台相关。为了使程序更具可移植性，并不推荐使用。<br /><br /><strong>2.3 The Lifecycle of a Thread </strong>
<p>&nbsp;</p>
<p dir="ltr">1. Creating a Thread：创建一个线程对象，并没有开始执行一个线程 <br />2. Starting a Thread：在调用线程对象的start()方法前，一直处于等待状态。可以通过调用isAlive()方法来获取线程是否正在运行； <br />3. Terminating a Thread：线程在以下情况下结束　　<br />　　1)执行完run()中的语句；<br />　　2)抛出一个异常，包括其没有捕获的异常；<br />　　3)其所在的程序（或者容器）调用System.exit()。 <br /><span style="font-weight: bold; color: red;">注意：一个线程结束后，就不能再次启动，该线程就变成一个普通的对象，如果试图再次调用start()方法，将抛出java.lang.IllegalThreadStateException异常。如果想多次运行，要么创建新的对象；要么就是不要结束该线程。</span><br /> 4. Java并没有提供可靠的方法来暂停、挂起或者重启一个线程的方法，线程本身能通过sleep()函数来暂停执行。目前依然只能确保若干毫秒级别的精度。 <br />5. Thread Cleanup：线程终止后，线程对象依然存在。可以通过join()方法以阻塞方式(blocked)来等待某个线程终止。<br /><br /><strong>2.4 Two Approches to Stop a Thread</strong> </p>
<p dir="ltr">1. <strong>Setting a Flag</strong>：在线程的执行过程中判断其它线程是否将标志置位。其缺点是有时间延迟，尤其是当进入了某些被阻塞的函数如：sleep(), wait()等； <br />2. <strong>调用线程的interrupt()方法</strong>，线程的执行过程中改为判断isInterrupted()的返回值。可以解决线程进入阻塞导致的延迟，但依然解决不了超长时间计算而导致的延迟。interrupt()有两个作用：1)终止sleep()和wait()，抛出InterruptedException；2)使isInterrupted()返回true。下面这段代码将演示interrupt()的作用：</p>
<div style="border: 1px dotted rgb(0, 0, 255); overflow-x: auto; width: 100%; background-color: rgb(192, 192, 192);">
<pre><font size="2">public class TestInterrupted {<br />     public static void main(String args[]) throws InterruptedException{<br />         Foo foo = new Foo();<br />         foo.start();<br />         Thread.currentThread().sleep(100);  //注释掉这句有什么影响呢？<br />         System.out.println(&quot;before interrupt&quot;);<br />         foo.interrupt();<br />         System.out.println(&quot;after interrupt&quot;);<br />     } }  <br /><br />class Foo extends Thread {<br />     public void run() {<br />         try{<br />             while(!isInterrupted()) {<br />                 System.out.println(&quot;start calculating...&quot;);<br />                 double pi = 3.1415926;<br />                 for(int i=0; i&lt;5; i++) {<br />                     for(long j=0; j&lt;1000000; j++) {<br />                         pi *= pi;<br />                     }<br />                     System.out.println(&quot;i=&quot;+i);<br />                 }<br />                 System.out.println(&quot;before sleep&quot;);<br />                 sleep(5000);    //注释掉这句及相关的try...catch语句，又怎样呢？<br />                 System.out.println(&quot;after sleep&quot;);<br />             }<br />         } catch(InterruptedException ex) {<br />             ex.printStackTrace(System.out);<br />         }<br />     }<br />}</font></pre>
</div>
<p dir="ltr"><strong></strong>&nbsp;</p>
<p dir="ltr"><strong>2.5 The Runnable Interface </strong></p>
<p dir="ltr">为什么有了Thread类，还要有Runnable接口呢？最主要的原因是为了解决Java不能多重继承的问题。线程继承自Thread，还是仅仅实现Runnable接口，取决于这个线程的作用。不过，如果仅仅实现Runnable接口，则在线程里不能使用Thread类的方法，比如interrupt()和isInterrupted()。当然，这个还是要取决于实际情况。<br /><br /><strong>2.6 Threads and Objects</strong> </p>
<p dir="ltr">主要讲解线程与对象的关系。线程根本上还是个对象，其可以存取任何对象包括其它线程对象，当然也能被其它对象存取，只要没有因为争夺公共资源而被锁定，线程对象中的任何属性和方法都有可能同时执行。并且Java中的锁只针对对象本身，并不包括对象下的属性；而对方法同步，则等同于对对象同步。更进一步的说明还可以参考《Practical Java》中的&quot;实践46：面对instance函数，synchronized锁定的是对象(object)而非函数(methods)或代码（code）&quot;。下面用两段代码来说明一下：<br /><strong>代码1:</strong></p>
<div style="border: 1px dotted rgb(0, 0, 255); overflow-x: auto; width: 100%; background-color: rgb(192, 192, 192);">
<pre>public class TestLock {<br />     public static void main(String args[])<br />           throws InterruptedException{<br />         Foo foo = new Foo();<br />         foo.start();<br />         Thread t = Thread.currentThread();<br />         for(int i=0; i&lt;10; i++) {<br />             foo.setInt2(&quot;Main&quot;, i, i+20);<br />         }<br />     }<br />}<br /><br />class Foo extends Thread{<br />     protected int arrI[] = new int[10];<br /><br />     public void run() {<br />         try {<br />             for(int i=0; i&lt;10; i++) {    <br />                 setInt(&quot;Foo&quot;, i, i);   <br />             }    <br />         } catch(InterruptedException ex) {}<br />     } <br /><br />     public synchronized void setInt(String from, int pos, int val)<br />               throws InterruptedException{<br />         arrI[pos] = val;<br />         sleep((long)(Math.random()*5000));<br />         System.out.println(from+&quot;:arrI[&quot;+pos+&quot;]=&quot;+arrI[pos]);<br />     }<br /><br />      public void setInt2(String from, int pos, int val)<br />               throws InterruptedException {<br />         synchronized(arrI){<br />             arrI[pos] = val;<br />             sleep((long)(Math.random()*5000));<br />             System.out.println(from+&quot;:arrI[&quot;+pos+&quot;]=&quot;+arrI[pos]);<br />         }<br />     } <br />}</pre>
</div>
结果：非线程安全，setInt()在对象上加锁，而setInt2()在属性arrI上加锁，不同的锁不能保证线程安全。可能的结果如下：<br />
<div style="border: 1px dotted rgb(128, 128, 128); overflow-x: auto; width: 100%; color: rgb(255, 255, 255); background-color: rgb(0, 0, 0);">
<pre>Foo:arrI[0]=0 <br />Main:arrI[0]=0 <br />Main:arrI[1]=21 <br />Main:arrI[2]=22 <br />Foo:arrI[1]=21 <br />Main:arrI[3]=23 <br />Main:arrI[4]=24 <br />Main:arrI[5]=25 <br />Foo:arrI[2]=2 <br />Main:arrI[6]=26 <br />Main:arrI[7]=27 <br />Foo:arrI[3]=3 <br />Foo:arrI[4]=4 <br />Main:arrI[8]=28 <br />Main:arrI[9]=29 <br />Foo:arrI[5]=5 <br />Foo:arrI[6]=6 <br />Foo:arrI[7]=7 <br />Foo:arrI[8]=8 <br />Foo:arrI[9]=9</pre>
</div>
<strong><br />代码2:</strong>
<div style="border: 1px dotted rgb(0, 0, 255); overflow-x: auto; width: 100%; background-color: rgb(192, 192, 192);">
<pre>public class TestLock1 {<br />     public static void main(String args[])<br />           throws InterruptedException{<br />         Foo1 foo = new Foo1();<br />         foo.start();<br />         Thread t = Thread.currentThread();<br />         for(int i=0; i&lt;10; i++) {<br />             foo.setInt2(&quot;Main&quot;, i, i+20);<br />         }<br />     } <br />}  <br /><br />class Foo1 extends Thread{<br />     protected int arrI[] = new int[10];<br /><br />     public void run() {<br />         try{    <br />             for(int i=0; i&lt;10; i++) {    <br />                 setInt(&quot;Foo&quot;, i, i);    <br />             }    <br />         }catch(InterruptedException ex){} <br />     }<br /><br />     public synchronized void setInt(String from, int pos, int val)<br />           throws InterruptedException{<br />         arrI[pos] = val;<br />         sleep((long)(Math.random()*5000));<br />         System.out.println(from+&quot;:arrI[&quot;+pos+&quot;]=&quot;+arrI[pos]);<br />     }<br /><br />     public synchronized void setInt2(String from, int pos, int val)<br />           throws InterruptedException{<br />         arrI[pos] = val;<br />         sleep((long)(Math.random()*5000));<br />         System.out.println(from+&quot;:arrI[&quot;+pos+&quot;]=&quot;+arrI[pos]);<br />&nbsp;    }<br />}</pre>
</div>
结果：线程安全<img width="1" height="1" src="http://blog.csdn.net/evanwhj/aggbug/616046.aspx" alt="" /><a href="http://blog.csdn.net/evanwhj/archive/2006/03/05/616046.aspx"><br /></a><img src ="http://www.blogjava.net/evanwhj/aggbug/33769.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-05 23:25 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/05/33769.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程(2)：J2SE 5.0多线程增强简介</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/05/33770.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Sun, 05 Mar 2006 15:25:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/05/33770.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/33770.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/05/33770.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/33770.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/33770.html</trackback:ping><description><![CDATA[J2SE 5.0在多线程上做了很大的改进，提供了更多的API，包括：<br /> <strong>Atomic variables:</strong> A set of classes that provide threadsafe operations without synchronization<br /> <strong>Explicit locks: </strong>Synchronization locks that can be acquired and released programmatically<br /> <strong>Condition variables:</strong> Variables that can be the subject of a targeted notification when certain conditions exist<br /> <strong>Queues:</strong> Collection classes that are thread-aware <strong>Synchronization primitives:</strong> New classes that perform complex types of synchronization<br /> <strong>Thread pools:</strong> Classes that can manage a pool of threads to run certain tasks<br /> <strong>Thread schedulers:</strong> Classes that can execute tasks at a particular point in time<br /> <br /> 在《Java Threads》一书中将其归纳为三类：<br /> 1. 对现有功能的新实现；<br /> 2. 提供了重要的多线程工具，如线程池(pool)和计划(schedule)；<br /> 3. 最小化同步工具(Minimal synchronization utilities)。<br /> <br />这些功能的妙处我现在自然是无法体会得到，但对于JDK 5.0中提供的这些多线程工具，会不会也遭遇JDK 1.4提供的Log API的命运，因敌不过第三方工具而成为摆设呢（至少目前我还在用Log4J，且其也没有停止开发的迹象）？<img width="1" height="1" src="http://blog.csdn.net/evanwhj/aggbug/616043.aspx" alt="" /><a href="http://blog.csdn.net/evanwhj/archive/2006/03/05/616043.aspx"><br /></a><img src ="http://www.blogjava.net/evanwhj/aggbug/33770.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-05 23:25 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/05/33770.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>多线程(1): 选书</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/05/33771.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Sun, 05 Mar 2006 15:25:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/05/33771.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/33771.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/05/33771.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/33771.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/33771.html</trackback:ping><description><![CDATA[<p>任何一门支持多线程的语言中，多线程是都是一个让人又爱又恨的东西。Java的多线程相对而言比其它语言要简单一点，如果不是开发框架类或者系统级的程序，也许很少会碰到要明确碰到Java的多线程API，但事实上不等于你不用注意多线程安全的问题，尤其当你在开发Web程序的时候，在类中使用了静态属性（static fields）而不仅仅是对象属性（instance fields）的时候，如果在压力测试或者提交给用户使用的时候，发生了一些不可重现的错误或者数据混乱的时候，那往往要查查这些使用了静态属性的类是否是多线程安全的了。当然，如果你专注于开发Web应用，并且很少涉及框架或核心模块的开发，那也就基本上知道synchronized的关键字的应用就可以了。这也许就是Java多线程相对其它语言中多线程要简单一点的原因。</p>
<span class="fullpost">当然，这次我打算比较深入地来了解了解一下Java多线程开发的其它一些内容，那么找一本好的书是一个比 较好的开始。关于Java多线程开发的专著比较有名的大约是《Java Threads, 3rd Edition》和《Java Thread Programming》了，前者基于JDK 1.5(这个版本对多线程进行了很大的改进)进行介绍，并且指出了与以前版本的区别；而后者出版于1999年，是基于JDK 1.2进行讲解的。所以呢，基本上采用第一本为主。同时也参考一下《Practical Java》和《Effective Java》的相关条目。<br /><br /></span>这几本书的封面如下，相关书的介绍可去<a href="http://www.amazon.com/" target="_blank">Amazon</a>查看一下：<span class="fullpost"><br />
<p><img alt="" src="http://blog.csdn.net/images/blog_csdn_net/evanwhj/Java.threads.jpg" /><img alt="" src="http://blog.csdn.net/images/blog_csdn_net/evanwhj/Java.thread.jpg" /></p>
<p><img alt="" src="http://blog.csdn.net/images/blog_csdn_net/evanwhj/Practical.Java1.jpg" /><img alt="" src="http://blog.csdn.net/images/blog_csdn_net/evanwhj/Effective.Java1.jpg" /></p>
</span><img width="1" height="1" src="http://blog.csdn.net/evanwhj/aggbug/616041.aspx" alt="" /><a href="http://blog.csdn.net/evanwhj/archive/2006/03/05/616041.aspx"><br /></a><img src ="http://www.blogjava.net/evanwhj/aggbug/33771.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-05 23:25 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/05/33771.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item><item><title>Java学习笔记：序言</title><link>http://www.blogjava.net/evanwhj/archive/2006/03/05/33772.html</link><dc:creator>Evan</dc:creator><author>Evan</author><pubDate>Sun, 05 Mar 2006 15:25:00 GMT</pubDate><guid>http://www.blogjava.net/evanwhj/archive/2006/03/05/33772.html</guid><wfw:comment>http://www.blogjava.net/evanwhj/comments/33772.html</wfw:comment><comments>http://www.blogjava.net/evanwhj/archive/2006/03/05/33772.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/evanwhj/comments/commentRss/33772.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/evanwhj/services/trackbacks/33772.html</trackback:ping><description><![CDATA[除了C是在大学中系统学的外，C++、Java、Design Pattern等等等等都是在工作中赶鸭子上架的时候学的，虽然有边做边学的乐趣；但也有为了赶时间抓进度，只要解决了问题就好而浅尝辄止的遗憾，也时有 遗忘的现象发生。近来得了点空闲，准备好好读读几本或算经典或算时髦的书。所以，就有了这样一个Blog，希望能将学习和工作中的一些小心得给记录下来， 聊以敝帚自珍之需。<img width="1" height="1" alt="" src="http://blog.csdn.net/evanwhj/aggbug/616036.aspx" /><a href="http://blog.csdn.net/evanwhj/archive/2006/03/05/616036.aspx"><br /></a><img src ="http://www.blogjava.net/evanwhj/aggbug/33772.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/evanwhj/" target="_blank">Evan</a> 2006-03-05 23:25 <a href="http://www.blogjava.net/evanwhj/archive/2006/03/05/33772.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>