编写多线程Java应用程序常见问题
时间:2007-12-27 来源:不详 作者:林子
尽管信号量并未直接被 Java 语言所支持,却很轻易在给对象上锁的基础上实现。一个简单的实现方法如下所示:
class Semaphore {
private int count;
public Semaphore(int n) {
this.count = n;
}
public synchronized void acquire() {
while(count == 0) {
try {
wait();
} catch (InterruptedException e) {
// keep trying
}
}
count--;
}
public synchronized void release() {
count ;
notify(); // alert a thread that's blocking on this semaphore
}
}
常见的上锁问题
不幸的是,使用上锁会带来其他问题。让我们来看一些常见问题以及相应的解决方法:
死锁。死锁是一个经典的多线程问题,因为不同的线程都在等待那些根本不可能被释放的锁,从而导致所有的工作都无法完成。假设有两个线程,分别代表两个饥饿的人,他们必须共享刀叉并轮流吃饭。他们都需要获得两个锁:共享刀和共享叉的锁。假如线程 "A" 获得了刀,而线程 "B" 获得了叉。线程 A 就会进入阻塞状态来等待获得叉,而线程 B 则阻塞来等待 A 所拥有的刀。这只是人为设计的例子,但尽管在运行时很难探测到,这类情况却时常发生。虽然要探测或推敲各种情况是非常困难的,但只要按照下面几条规则去设计系统,就能够避免死锁问题:
dedecms.com
让所有的线程按照同样的顺序获得一组锁。这种方法消除了 X 和 Y 的拥有者分别等待对方的资源的问题。
将多个锁组成一组并放到同一个锁下。前面死锁的例子中,可以创建一个银器对象的锁。于是在获得刀或叉之前都必须获得这个银器的锁。
将那些不会阻塞的可获得资源用变量标志出来。当某个线程获得银器对象的锁时,就可以通过检查变量来判定是否整个银器集合中的对象锁都可获得。假如是,它就可以获得相关的锁,否则,就要释放掉银器这个锁并稍后再尝试。
最重要的是,在编写代码前认真仔细地设计整个系统。多线程是困难的,在开始编程之前具体设计系统能够帮助你避免难以发现死锁的问题。
Volatile 变量. volatile 要害字是 Java 语言为优化编译器设计的。以下面的代码为例:
class VolatileTest {
public void foo() {
boolean flag = false;
if(flag) {
// this could happen
}
}
}
一个优化的编译器可能会判定出 if 部分的语句永远不会被执行,就根本不会编译这部分的代码。假如这个类被多线程访问,flag 被前面某个线程设置之后,在它被 if 语句测试之前,可以被其他线程重新设置。用 volatile 要害字来声明变量,就可以告诉编译器在编译的时候,不需要通过猜测变量值来优化这部分的代码。 内容来自dedecms
无法访问的线程 有时候虽然获取对象锁没有问题,线程依然有可能进入阻塞状态。在 Java 编程中 IO 就是这类问题最好的例子。当线程因为对象内的 IO 调用而阻塞时,此对象应当仍能被其他线程访问。该对象通常有责任取消这个阻塞的 IO 操作。造成阻塞调用的线程经常会令同步任务失败。假如该对象的其他方法也是同步的,当线程被阻塞时,此对象也就相当于被冷冻住了。其他的线程由于不能获得对象的锁,就不能给此对象发消息(例如,取消 IO 操作)。必须确保不在同步代码中包含那些阻塞调用,或确认在一个用同步阻塞代码的对象中存在非同步方法。尽管这种方法需要花费一些注重力来保证结果代码安全运行,但它答应在拥有对象的线程发生阻塞后,该对象仍能够响应其他线程。
上一篇:Java泛型编程快速入门 下一篇:JAVA - IO包的学习引导文章
文章评论
共有位Admini5网友发表了评论 查看完整内容