活跃指的是一些最终以有利方面结束。当一个应用到达的状态是为不可能继续向下执行时,那么就是非活跃的。在单一线程应用中,死循环就是一个例子。在多线程的应用中,活跃是死锁、活锁和饥饿:
Deadlock:线程1等待的资源是线程2所持有的,和线程2等待资源是线程1所持有的。两个线程会同时在等待。
LiveLock:线程x保持着尝试一个操作,而这个总是失败。这个的情况是不能继续执行的。
Starvation:线程x继续延迟(通过调度程序列表),它需要资源让自己的程序继续下去。在低优先权线程之前可能存在高优先权调度程序执行,而这里总是有高优先权的程序。饥饿也通常指无限延期(indefinite postponement)。
思考死锁。之个问题出现是因为有太多同步通过同步锁。如果你不注意,你可能遇到这样的问题,锁被多个线程请求,没有线程持有它自己的锁,但是持有的锁需要其它线程的资源,没有线程可以在这个临界区进入或退出去释放自己持有的锁,因为其它线程在这个临界区持有这个锁。下面例子证明了这个问题:
Listing 2-1 死锁例子。
package com.owen.thread.chapter2;
public class DeadlockDemo
{
private final Object lock1 = new Object();
private final Object lock2 = new Object();
public void instanceMethod1()
{
synchronized (lock1)
{
synchronized (lock2)
{
System.out.println("first thread in instanceMethod1");
// critical section guarded first by
// lock1 and then by lock2
}
}
}
public void instanceMethod2()
{
synchronized (lock2)
{
synchronized (lock1)
{
System.out.println("second thread in instanceMethod2");
// critical section guarded first by
// lock2 and then by lock1
}
}
}
public static void main(String[] args)
{
final DeadlockDemo dld = new DeadlockDemo();
Runnable r1 = new Runnable()
{
@Override
public void run()
{
while (true)
{
dld.instanceMethod1();
try
{
Thread.sleep(50);
} catch (InterruptedException ie)
{
}
}
}
};
Thread thdA = new Thread(r1);
Runnable r2 = new Runnable()
{
@Override
public void run()
{
while (true)
{
dld.instanceMethod2();
try
{
Thread.sleep(50);
} catch (InterruptedException ie)
{
}
}
}
};
Thread thdB = new Thread(r2);
thdA.start();
thdB.start();
}
}
Listing2-1线程A和线程B分别请求instanceMethod1()和instanceMethod2(),在不同的时间。思考下面的执行顺序:
线程A请求instanceMehtod1(),包含一个锁注册lock1引用对象,和进入它外部的临界区。(但是不能请求注册lock2引用对象。)
线程B请求instanceMehtod2(),包含一个锁注册lock2引用对象,和进入它外部的临界区。(但是不能请求注册lock1引用对象。)
线程A试图去请求锁连接lock2,但是java虚拟机强迫线程在临界外部等待,因为线程B持有这个锁。
线程B试图去请求锁连接lock1,但是java虚拟机强迫线程在临界外部等待,因为线程A持有这个锁。
没有线程可以继续执行,因为另一个线程持有该线程需要的锁。你拥有了死锁的状态,和这个项目(至少在上下文存在两线程)被“冻结”。
你可以观察第一个线程在instanceMethod1和第二个线程在instanceMethod2的信息正常输出,直到应用被“冻结”,因为死锁。
尽管前面的例子明确了一个死锁状态的应用,但是在实际应用中并不是那么容易发现死锁。例如,你的代码可能包含下面多个类的循环关系(在向几个资源文件):
类A同步方法请求类B的同步方法。
类B同步方法请求类C的同步方法。
类C同步方法请求类A的同步方法。
如果线程A请求类A的同步方法和线程B请求类C的同步方法,当线程B试图去请求类A的同步方法时,它将会阻塞,而线程A问题在这个方法内部,线程A继续执行直到它请求到的类C同步方法,然后就会阻塞。死锁就产生了。
源码下载:git@github.com:owenwilliam/Thread.git