面试题:如何优雅的停止一个正在运行的java线程

线程状态转换图:

面试题:如何优雅的停止一个正在运行的java线程


几个容易混淆概念的方法

Thread.sleep()、 Thread.join() 或 Object.wait(),
他们都是阻塞方法并声明抛出 InterruptedException.
当线程调用这些方法进入阻塞状态后, 如果外部调用了interrupt(),
线程进入就绪状态, 并且这些方法抛出InterruptedException异常。

<code>public void Thread.interrupt() // 设置线程的中断状态flag.
public boolean Thread.isInterrupted() // 获取线程的中断状态flag值
public static boolean Thread.interrupted() // 取线程的中断状态flag值,并重置flag值为false.
public final void join() throws InterruptedException //指的是调用者进入阻塞状态, 而不是线程对象进入阻塞状态, 线程对象继续在执行, 不受影响, 看下面的例子.
public static void yield(); //线程从运行状态, 回到就绪状态./<code>
<code>//demo: join()
public class BottomBarManager {
private final Object handlePopupMenuAction(int actionCode, Object... args) {
switch (actionCode) {
case Actions.MenuContainer.DOWNLOAD: // 下载
ThreadTest tt = new ThreadTest();
tt.start();
try {
tt.join(); //当前线程等待tt线程执行完再往下执行
//作用是, 当前的UI线程进入阻塞状态, 直到等tt线程执行完再往下继续执行, tt线程该干什么还干什么, 不受影响.

//千万不要误理解成tt线程进入了阻塞状态.
} catch (InterruptedException e) {
e.printStackTrace();
}
JLog.i();
break;

}

private class ThreadTest extends Thread {

@Override
public void run() {
try {
sleep(100000);
} catch (InterruptedException e) {
e.printStackTrace();
}
JLog.i();
}
}

}/<code>

中断线程的最佳实践是: 中断 + 条件变量。

最佳模板:

<code>public class BestPractice extends Thread {
private volatile boolean finished = false; // ① volatile条件变量
public void stopMe() {
finished = true; // ② 发出停止信号
interrupt();
}
@Override
public void run() {
while (!finished) { // ③ 检测条件变量
try {
// do dirty work // ④业务代码
} catch (InterruptedException e) {
if (finished) {
return;
}

continue;
}
}
}
}/<code>

当④处的代码阻塞于wait()或sleep()时,线程不能立刻检测到条件变量。因此②处的代码最好同时调用interrupt()方法。

典型的体现是在volley项目中:

<code>// CacheDispatcher.java
public class CacheDispatcher extends Thread {
private volatile boolean mQuit = false;

public void quit() {
mQuit = true;
interrupt();
}

@Override
public void run() {
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);

// Make a blocking call to initialize the cache.
mCache.initialize();

while (true) {
try {
// Get a request from the cache triage queue, blocking until
// at least one is available.
final Request> request = mCacheQueue.take(); //****** block方法, it throws InterruptedException;
request.addMarker("cache-queue-take");

// Attempt to retrieve this item from cache.
Cache.Entry entry = mCache.get(request.getCacheKey());
if (entry == null) {
request.addMarker("cache-miss");
// Cache miss; send off to the network dispatcher.
mNetworkQueue.put(request);
continue;
}

// We have a cache hit; parse its data for delivery back to the request.

request.addMarker("cache-hit");
Response> response = null;
try {
response = request.parseNetworkResponse(
new NetworkResponse(entry.data, entry.responseHeaders));
} catch (Exception e) {
mDelivery.postError(request, new ParseError(e));
continue;
} catch (Error error) {
mDelivery.postError(request, new ParseError(error));
continue;
}
request.addMarker("cache-hit-parsed");

} catch (InterruptedException e) {
// We may have been interrupted because it was time to quit.
if (mQuit) {
return;
}
continue;
} catch (OutOfMemoryError error) {
if (mQuit) {
return;
}
continue;
}
}
}

}/<code>

在quit()代码里, 设置条件变量, 同时调用interrupt()方法.

以后写线程类时, 参考这个模板写就很优雅和规范。


中断是礼貌地请求另一个线程在它愿意并且方便的时候停止它正在做的事情。


分享到:


相關文章: