Evan's Blog

Java, software development and others.

  BlogJava :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  28 随笔 :: 0 文章 :: 73 评论 :: 0 Trackbacks

#

     摘要: 推荐两款纯Java的SVN Web Client软件。其安装使用均比ViewVC要好!  阅读全文
posted @ 2006-04-06 00:30 Evan 阅读(19243) | 评论 (21)编辑 收藏

     摘要: 前几天好奇,也刚好得了点空闲,然后就想看看ViewVC对Subversion的支持程度,于是就想装个玩玩。好死不死的,在我的VMWare Workstation上刚好有个Windows Server 2003,心想,就它吧,可就这么一偷懒,折腾了我好几天,最终还是只能算将就着把它给装上了。  阅读全文
posted @ 2006-04-05 00:28 Evan 阅读(7686) | 评论 (5)编辑 收藏

     摘要: SVN会取代CVS吗?这个虽然不是我们这种小程序员能决定的大事,但学学总无妨吧,这里有一些我搜集的资料。  阅读全文
posted @ 2006-03-23 23:55 Evan 阅读(3185) | 评论 (0)编辑 收藏

     摘要: 你是不是为了高的测试覆盖度而在为每个函数添加多个测试方法,甚至连getX()和setX()都不放过呢?或者,你一看到覆盖度达到100%的代码,景仰之心就开始“有如滔滔江水绵绵不绝,又有如黄河泛滥,一发不可收拾”了呢?那么,你应该读读Andrew Glover在最近的developerWorks上发表的这篇文章。  阅读全文
posted @ 2006-03-18 23:23 Evan 阅读(1199) | 评论 (0)编辑 收藏

     摘要: 《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中一个比较重要的多线程工具。  阅读全文
posted @ 2006-03-11 23:11 Evan 阅读(1651) | 评论 (0)编辑 收藏

     摘要: 快来看“洋本山”怎样忽悠一个只想买一把锤子的人最后买了一个工具工厂的建造工厂的通用建造工厂。很别扭是吧,但如果你是个开发Web应用的Java程序员,你也许已经或者正在被忽悠。  阅读全文
posted @ 2006-03-11 17:04 Evan 阅读(5753) | 评论 (23)编辑 收藏

看到《Java Threads》第5章,介绍了JDK 1.5新加的一些所谓原子类(Atomic Classes),总感觉有点为原子而原子,实际操作中,又有多少人会为了少许的性能提升而刻意去用这些别扭的操作而放弃直观的synchronize关键字或者Lock类呢?不过,这里不是想讨论这个,而是当其用Atomic Classes来改造它的打字程序后,解释用原子类只是保证类似递增、递减、赋值等操作的原子性,而不能保证其所在的方法一定是线程安全的,然后说,有可能按键事件的处理可能需要等待resetScore()处理完才能执行,而这会导致错误的评分(被当成多敲了键)。由于前几章的内容相对比较简单易懂,所以也没有很仔细的运行那些例子。这里为了验证一下,就运行了一下第4章的例子,然后发现,基本上第一次的评分总是错的。这就引起了我的注意,因为,一般情况下,如果是race condition导致的错误是很难重现的,这么明显的错误很可能是程序逻辑上的错误。仔细看了一下代码,发现在start按钮的事件处理方法里,有下面这样一段代码:
startButton.addActionListener(new ActionListener() {
        public void actionPerformed(ActionEvent evt) {
                displayCanvas.setDone(false);
                producer.setDone(false);
                startButton.setEnabled(false);
                stopButton.setEnabled(true);
                feedbackCanvas.setEnabled(true);
                feedbackCanvas.requestFocus();
                score.resetScore();
            }
        });
注意重置成绩的调用放在了最后,此时,随机生成字符的线程应该被唤醒并产生了第一个字符,然后,resetScore()将需要输入的字符又设成了-1,所以,当你第一次输入字符时,总是被认为是多击了一次键而扣1分:(。既然这样,那停止然后再启动也应该会发生这个错误啊。而事实上的确是这样。我想,这不应该看做是race condition吧,有什么样的同步技术能够避免这个问题呢?除非另外弄个标志,当成绩没有被重置前,不能产生第一个字符。当然,这是不需要的,只要将score.resetScore()放到第一句就可以了。

然后又运行了第3章的例子,发现基本上没有这个问题。难道第3章的代码是正确的?打开源代码一看,重置成绩的方法还是放在最后,那这里为什么又是正确的呢?我想,大约是第3章的例子中,每次点击start按钮,都重新创建一个线程对象的原因吧。由于创建对象和初始化线程需要一定的时间,刚好给了主线程重置成绩的机会。

不知道作者有意为之呢,还是疏忽,不过,这样的错误不能算是race condition的例子。
posted @ 2006-03-09 22:11 Evan 阅读(845) | 评论 (0)编辑 收藏

第3章主要介绍了数据的同步(Data Synchronization),这一章则主要介绍线程之间的同步方法(Thread Notification),同样包括传统的wait-and-notify方法和JDK 1.5新推出的Condition Variable。在多线程编程中,数据同步和线程同步是两个最基本也是最关键的部分。
《Java Threads》一书中通过考察打字程序中当按下start和stop按钮后,每次都创建两个新的线程的效率问题来引入线程同步的概念,当然不是线程同步的主要用处。不过,教科书归教科书,实际运用则又是另一回事。所以,通过书本学习语法,通过实践来获得运用经验。

4.1 Wait and Notify

1. 等待/唤醒类似于Solaris或POSIX中的条件变量(conditon variables),或者Windows中的事件变量(evant variable)或者信号量(signal),用于某个/多个线程暂停等待某个条件的满足,而该条件将由其它线程来设置的情况。
2. 在Java中,就像每个对象有一个锁之外,任何对象都可以提供等待/唤醒的机制。就像Java中的synchronized总是表示获得某个具体对象的锁一样,wait和notify也总是等待某个具体的对象,并由该对象唤醒;同样,获得某个对象上的锁不一定是该对象需要同步一样,等待和唤醒的条件也不一定是与之绑定的对象。
3. Java中wait-and-notify的几个方法:
void wait(): 使当前线程处于等待状态,直到其它线程调用了nofity()或者notifyAll()方法为止。
void wait(long timeout): 使当前线程处于等待状态,直到其它线程调用了nofity()或者notifyAll()方法,或者超过了指定的时间(单位为ms)为止
void wait(long timeout, int nanos):与wait(long)一样,只是在某些JVM中可以精确到奈秒。
void notify(): 唤醒一个正在等待该对象的线程。
void notifyAll(): 唤醒所有正在等待该对象的线程。
注意:任何等待和唤醒方法都必须在与之对应的对象的同步方法或同步块里调用。即:wait-and-notify必须和与之对应的synchronized关键词一起使用的。
4. wait()和sleep()的主要区别:
  1) sleep()可以在任何地方调用,而wait()需要在同步方法或同步块中调用;
  2) 进入wait()函数时,JVM会自动释放锁,而当从wait()返回即被唤醒时,又会自动获得锁;而sleep()没有这个功能,因此如果在wait()的地方用sleep()代替,则会导致相应的nofity()方法在等待时不可能被触发,因为notify()必须在相应的同步方法或同步块中,而此时这个锁却被sleep()所在的方法占用。也就是说,wait-and-notify不可能与sleep()同时使用。

4.1.1 The Wait-and-Notify Mechanism and Synchronization
1. 这一节详细的讲解了wait-and-notify机制和synchronized的关系,主要是两点:1)wait-and-notify必须和synchronized同时使用;2)wait()会自动释放和获取锁;
2. 这一节中举了一个例子用来解释可能存在当条件被不满足时也有可能被唤醒的情况:
  1) 线程T1调用一个同步方法;
  2) T1检测状态变量,发现其不满足条件;
  3) T1调用wait(),并释放锁;
  4) 线程T2调用另外一个同步方法,获得锁;
  5) 线程T3调用另外一个同步方法,由于T2获得了锁,所以处于等待状态;
  6) T2修改状态变量,使其满足条件,并调用notify()方法;
  7) T3获得锁,然后处理数据,并将状态变量又设置为不满足条件的状态;
  8) T3处理完毕返回;
  9) T1被唤醒,但实际上此时条件并不满足。
这个例子刚好印证了《Effective Java》中"Item 50: Never invoke wait outside a loop"和《Practical Java》中"实践54:针对wait()和notifyAll()使用旋锁(spin locks)"。即总是用下面这种方式来调用wait():
    synchronized(obj) {
while(<condition does not hold>)
wait();

... // Perform action appropriate to condition }
或者象《Practical Java》中一样:
    synchronized(obj) {
while(<condition does not hold>) {
try {
wait();
} catch (InterruptedException e) {}
}

... // Perform action appropriate to condition }
3. 调用wait()的线程T可能在以下几种情况被唤醒:
  1) 其它线程调用了notify(),而刚好线程T得到了通知;
  2) 其它线程调用了notifyAll();
  3) 其它线程中断了线程T;
  4) 由于JVM的原因,导致了spurious wakeup。

4.1.2 wait(), notify(), and notifyAll()
1. 正像多个线程等待同一对象上的锁,当锁释放时,无法确定哪个线程会得到那个锁一样;当有多个线程在wait()时,当另外一个线程调用nofity()的时候,也不能确定哪个线程会被唤醒; 2. 因此在《Practical Java》的"实践53:优先使用notifyAll()而非notify()"建议的一样,结合实践54,可以比较好的解决线程唤醒的问题。

4.1.3 Wait-and-Notify Mechanism with Synchronized blocks
再次强调必须在同一个对象的synchronized方法或块内调用该对象上的wait和notify方法。

4.2 Condition Variables

1. 就像上面反复强调的一样,wait-and-notify机制是与特定对象及其上的锁是绑定在一起的,锁和唤醒对象不能分开,这在某些情况下不是很方便;
2. JDK 1.5提供Condition接口来提供与其它系统几乎一致的condition variables机制;
3. Condition对象由Lock对象的newCondition()方法生成,从而允许一个锁产生多个条件变量,可以根据实际情况来等待不同条件;
4. 该书的例子没有什么特别的实际意义,但JDK 1.5文档中提供了一个例子,能充分说明使用Condition Variables使得程序更加清晰易读,也更有效率:
class BoundedBuffer {
final Lock lock = new ReentrantLock();
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100]; int putptr, takeptr, count;

public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await();
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal();
} finally {
lock.unlock();
}
}
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await();
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal();
return x;
} finally {
lock.unlock();
}
}
}
具体的说明请参考JDK 1.5的文档。
5. 除了用lock和await-and-signal来代替synchronized和wait-and-notify外,其语义和机制基本一样。await()在进入前也会自动释放锁,然后再返回前重新获得锁;
6. 使用Condition Variables的原因:
  1) 如果使用Lock对象,则必须使用condition variables;
  2) 每个Lock对象可以创建多个condition variable.
posted @ 2006-03-06 22:21 Evan 阅读(823) | 评论 (0)编辑 收藏

除了C是在大学中系统学的外,C++、Java、Design Pattern等等等等都是在工作中赶鸭子上架的时候学的,虽然有边做边学的乐趣;但也有为了赶时间抓进度,只要解决了问题就好而浅尝辄止的遗憾,也时有 遗忘的现象发生。近来得了点空闲,准备好好读读几本或算经典或算时髦的书。所以,就有了这样一个Blog,希望能将学习和工作中的一些小心得给记录下来, 聊以敝帚自珍之需。
posted @ 2006-03-05 23:25 Evan 阅读(328) | 评论 (0)编辑 收藏

任何一门支持多线程的语言中,多线程是都是一个让人又爱又恨的东西。Java的多线程相对而言比其它语言要简单一点,如果不是开发框架类或者系统级的程序,也许很少会碰到要明确碰到Java的多线程API,但事实上不等于你不用注意多线程安全的问题,尤其当你在开发Web程序的时候,在类中使用了静态属性(static fields)而不仅仅是对象属性(instance fields)的时候,如果在压力测试或者提交给用户使用的时候,发生了一些不可重现的错误或者数据混乱的时候,那往往要查查这些使用了静态属性的类是否是多线程安全的了。当然,如果你专注于开发Web应用,并且很少涉及框架或核心模块的开发,那也就基本上知道synchronized的关键字的应用就可以了。这也许就是Java多线程相对其它语言中多线程要简单一点的原因。

当然,这次我打算比较深入地来了解了解一下Java多线程开发的其它一些内容,那么找一本好的书是一个比 较好的开始。关于Java多线程开发的专著比较有名的大约是《Java Threads, 3rd Edition》和《Java Thread Programming》了,前者基于JDK 1.5(这个版本对多线程进行了很大的改进)进行介绍,并且指出了与以前版本的区别;而后者出版于1999年,是基于JDK 1.2进行讲解的。所以呢,基本上采用第一本为主。同时也参考一下《Practical Java》和《Effective Java》的相关条目。

这几本书的封面如下,相关书的介绍可去Amazon查看一下:


posted @ 2006-03-05 23:25 Evan 阅读(578) | 评论 (1)编辑 收藏

仅列出标题
共3页: 上一页 1 2 3 下一页