Java线程2 --- Java线程状态?中断?基本的调度原则?
参考资料<<Java2核心技术,卷Ⅱ:高级性能 第五版>>
线程的状态
new(新)
线程尚未运行。
runnable(可运行)
调用了start方法。可运行可以是尚未运行。(指点了start方法后,可能还没有分配到时间片,或分配了时间片还没到此时间片)
blocked(被中断运行)
有人调用该线程的sleep()方法
该线程调用了一个输入/输出时中断的操作,也就是说,在输入和输出操作完成之前,该操作将不会返回到它的调用程序。
该线称调用了wait()方法
该线程试图锁定了一个当前被另一个线程锁定了的对象。
有人调用了该线程的suspend()方法。但是现在该方法已经作废。并且在你的代码中你不应调用该方法。
dead(死)
由于run方法的正常退出而自然死亡。
没有抓取到的异常事件终止了run方法的执行,从而导致线程的突然死亡。
PS:判断某个线程是否’活着’(这里’活着’是指它是否处于可运行状态或者中断状态)。可使用isAlive方法。如果该线程是可运行线程或者被中断的线程,那么该方法将返回true,如果该线程仍然是个新线程或者尚未成为可运行线程,或者该线程是个死线程。那么该方法将返回false.
线程的中断
线程在不睡觉或者没有被中断的时候时候调用interrupt()方法不抛出InterruptedException()异常,只是设置中断标志!!!!!
(PS:在<<Java2核心技术,卷Ⅱ:高级性能 第五版>>书里第5行这样写 “如果在线程处于睡眠或者等待状态时调用interrupt方法,那么将不会产生任何InterruptException事件,该线程需要调用interrupted()来确定它最近是否被中断了” 这句话差点没把我害死,正好把情况写反了,害我看这一章花了好久时间,还摸不出个头绪来。看这本书同志注意了。
关于中断的几种代码框架:
1把中断解释为一个终止运行的请求。这种线程的run方法形式如下:
(PS:这个框架有点小问题,就是线程在正常情况下调用interrupt()方法,线程不会退出,而只是把中断标志被设为true,表明被中断过,其实还跑的真欢呢)
public void run()
{
Try
{
…
While ( more work to do)
{
do more work
}
}
catch ( InterruptedException exception)
{
// thread was interrupted during sleep or wait
}
finally
{
// Clean up
}
// exit method and terminate thread
}
2 上面的代码框架存在一个问题,如果在线程不处于睡眠或者等待状态时调用interrupt方法,那么将不会产生任何InterruptedException事件,该线程需要调用interrupted方法来确定它最近是否被中断了。
While(!interrupted() && more work to do
(PS:这里interrupted和isInterrupted两种判断线程是否被中断过的方法见下面的JDK的详细说明)
{
Do more work;
}
3 将sleep只定义为在发生中断时返回”中断”标志,那么这是最好的。这时根本不必使用InterruptedExcetption.当然,你可以在抓取到InterruptedException时,用人工方法设置”中断”标志:
(PS: 因为线程在不睡觉或者没有被中断的时候时候调用interrupt()方法不抛出InterruptedException()异常,只是设置中断标志)
Try
{
Sleep(delay);
}
catch(InterruptedException exception)
{
Thread.currentThread().interrupt();
}
如果是通过一个不能产生任何异常事件的方法来调用sleep方法,那么必须使用上面这种方法。
4 将”中断”标志转换成一个异常事件
(PS:因为线程在不睡觉或者没有被中断的时候时候调用interrupt()方法不抛出InterruptedException()异常,而只是把中断标志设为true,
见下面interrupt方法的详细定义,这样子程序不会被中断,所以我们可以在程序中插入下面这行代码,一旦中断标志为true,手工抛出异常让程序终止。
if (isInterrupted()) throw new InterruptedException()
interrupt public void interrupt() 中断线程。 如果当前线程没有中断它自己(这在任何情况下都是允许的),则该线程的 checkAccess 方法就会被调用,这可能抛出 SecurityException。 如果线程在调用 Object 类的 wait()、wait(long) 或 wait(long, int) 方法,或者该类的 join()、join(long)、join(long, int)、sleep(long) 或 sleep(long, int) 方法过程中受阻,则其中断状态将被清除,它还将收到一个 InterruptedException。 如果该线程在可中断的通道上的 I/O 操作中受阻,则该通道将被关闭,该线程的中断状态将被设置并且该线程将收到一个 ClosedByInterruptException。 如果该线程在一个 Selector 中受阻,则该线程的中断状态将被设置,它将立即从选择操作返回,并可能带有一个非零值,就好像调用了选择器的 wakeup 方法一样。 如果以前的条件都没有保存,则该线程的中断状态将被设置(为true)。 抛出: SecurityException - 如果当前线程无法修改该线程 public class InterruptedException extends Exception 当线程在很长一段时间内一直处于正在等待、休眠或暂停状态,而另一个线程用 Thread 类中的 iterrupt 方法中断它时,抛出该异常。
interrupted public static boolean interrupted() 测试当前线程是否已经中断。线程的中断状态 由该方法清除。换句话说,如果连续两次调用该方法,则第二次调用将返回 false(在第一次调用已清除了其中断状态之后,且第二次调用检验完中断状态前,当前线程再次中断的情况除外)。 返回: 如果当前线程已经中断,则返回 true;否则返回 false。 另请参见:
public boolean isInterrupted() 测试线程是否已经中断。线程的中断状态 不受该方法的影响。 返回: 如果该线程已经中断,则返回 true;否则返回 false。 另请参见:
|
基本的调度原则
(下面这5个原则摘录自<<Java2核心技术,卷Ⅱ:高级性能 第五版>>第一章)
调度原则确实是很复杂的,不过使用起来实际上是相当简单的,你只需要按照下面这5个原则进行操作即可:
1) 如果两个或多个线程修改一个对象,请将执行修改的方法声明为synchronized方法,受到对象修改影响的只读方法也必须实现同步。
2) 如果一个线程必须等待某个对象的状态出现变更,那么它应该在对象的内部等待,而不是在外边等待,这可通过输入一个synchronized方法,并且调用wait方法来实现。
3) 不要在synchronized方法中花费大量的时间。大多数操作只是更新数据结构,然后很快返回。如果你不能立即完成synchronized方法的操作。那么请调用wait方法,这样你可以在等待时释放该对象锁。
4) 每当一个方法改变某个对象的状态时,它就应该调用notifyAll方法,这样可以给等待线程一个机会,以便查看环境有没有发生变化。
5) 请记住,wait和notifyAll/notify方法都属于Object类的方法,而不是Thread类的方法,请反复检查你对wait方法的调用与同一个对象上的通知是否匹配。
(PS:上面几条都很好理解,就第3条” 那么它应该在对象的内部等待,而不是在外边等待”不怎么明白,这里指的在外面等待是什么?)
几个小例子,可以直接运行,研究下对刚学习线程的朋友应该很有帮助的:
打印当前所有线程的例子,可以直接放在程序查看当前活动线程。很有用。 public class ThreadLister { private static void printThreadInfo(Thread t, String indent) { if (t == null) return; System.out.println(indent + "Thread: " + t.getName() + " Priority: " + t.getPriority() + (t.isDaemon() ? " Daemon" : "") + (t.isAlive() ? "" : " Not Alive")); }
/** Display info about a thread group */ private static void printGroupInfo(ThreadGroup g, String indent) { if (g == null) return; int numThreads = g.activeCount(); int numGroups = g.activeGroupCount(); Thread[] threads = new Thread[numThreads]; ThreadGroup[] groups = new ThreadGroup[numGroups];
g.enumerate(threads, false); g.enumerate(groups, false);
System.out.println(indent + "Thread Group: " + g.getName() + " Max Priority: " + g.getMaxPriority() + (g.isDaemon() ? " Daemon" : ""));
for (int i = 0; i < numThreads; i++) printThreadInfo(threads[i], indent + " "); for (int i = 0; i < numGroups; i++) printGroupInfo(groups[i], indent + " "); }
/** Find the root thread group and list it recursively */ public static void listAllThreads() { ThreadGroup currentThreadGroup; ThreadGroup rootThreadGroup; ThreadGroup parent;
// Get the current thread group currentThreadGroup = Thread.currentThread().getThreadGroup();
// Now go find the root thread group rootThreadGroup = currentThreadGroup; parent = rootThreadGroup.getParent(); while (parent != null) { rootThreadGroup = parent; parent = parent.getParent(); }
printGroupInfo(rootThreadGroup, ""); }
public static void main(String[] args) {
ThreadLister.listAllThreads(); } } |
一个死锁的例子 public class AnotherDeadLock { public static void main(String[] args) { final Object resource1 = "resource1"; final Object resource2 = "resource2"; // t1 tries to lock resource1 then resource2 Thread t1 = new Thread() { public void run() { // Lock resource 1 synchronized (resource2) { System.out.println("线程1取得resource1的锁"); try { Thread.sleep(50); } catch (InterruptedException e) { } System.out.println("线程1尝试去取得resource1的锁"); try{ sleep(1000); }catch(Exception e){ } synchronized (resource1) { System.out.println("线程1取得了资源1的锁"); } System.out.println("线程1释放了了资源1的锁"); } System.out.println("线程1释放resource1的锁"); }
public void release(){ notifyAll(); } };
// t2 tries to lock resource2 then resource1 Thread t2 = new Thread() { public void run() { synchronized (resource1) { System.out.println("线程2取得resource2的锁");
try { Thread.sleep(50); } catch (InterruptedException e) { } System.out.println("线程2尝试去取得resource2的锁"); try{ sleep(1000); }catch(Exception e){ } synchronized (resource2) { System.out.println("线程2取得了资源2的锁"); } System.out.println("线程2释放了资源2的锁"); } System.out.println("线程2释放了资源2的锁"); } };
t1.start(); t2.start(); } } |
使用同步方法的程序,看看synchronized的用法。 public class OnlyOneInMethod extends Object { private String objID;
public OnlyOneInMethod(String objID) { this.objID = objID; }
public synchronized void doStuff(int val) { print("entering doStuff()"); int num = val * 2 + objID.length(); print("local variable num=" + num);
try { Thread.sleep(2000); } catch (InterruptedException x) { }
print("leaving doStuff()"); }
public void print(String msg) { threadPrint("objID=" + objID + " - " + msg); }
public static void threadPrint(String msg) { String threadName = Thread.currentThread().getName(); System.out.println(threadName + ": " + msg); }
public static void main(String[] args) { final OnlyOneInMethod ooim = new OnlyOneInMethod("obj1");
Runnable runA = new Runnable() { public void run() { ooim.doStuff(3); } };
Thread threadA = new Thread(runA, "threadA"); threadA.start();
try { Thread.sleep(200); } catch (InterruptedException x) { }
Runnable runB = new Runnable() { public void run() { ooim.doStuff(7); } };
Thread threadB = new Thread(runB, "threadB"); threadB.start();
Runnable runC = new Runnable() { public void run() { ooim.doStuff(10); } };
Thread threadC = new Thread(runC, "threadC"); threadC.start(); } } |