/**
* Created by lihong10 on 2017/6/16.
*/publicclassThreadDeathWatcherTest {/**
*
该方法运行结果如下,并且控制台输出以下三行内容的顺序永远不变。
main thread done
the watched thread is alive ? : false
the watched thread is dead, so , I can run !
*/publicstaticvoidmain(String[] args) throws InterruptedException {
//获取主线程
Thread main = Thread.currentThread();
//创建一个主线程结束后才会开始运行的任务
Runnable delayedTask = new TestRunnable(main);
//ThreadDeathWatcher是netty的util包下的一个工具类,// watch方法的第一个参数可以认为是提交线程,只有提交线程运行结束,//第二个参数代表的Runnable任务才会开始执行//这个类是ReferenceCountUtil的public static <T> T releaseLater(T msg) 方法//实现的关键
ThreadDeathWatcher.watch(main, delayedTask);
//只有当前线程main线程运行结束,TestRunnable中的run方法才开始运行
Thread.currentThread().sleep(1000);
System.out.println("main thread done ");
}
privatestaticclassTestRunnableimplementsRunnable {private Thread thread;
publicTestRunnable(Thread thread) {
this.thread = thread;
}
@Overridepublicvoidrun() {
System.out.println("the watched thread is alive ? : " + thread.isAlive());
System.out.println("the watched thread is dead, so , I can run !");
}
}
}
/*
* Copyright 2014 The Netty Project
*
* The Netty Project licenses this file to you under the Apache License,
* version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at:
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations
* under the License.
*/import io.netty.util.concurrent.DefaultThreadFactory;
import io.netty.util.internal.StringUtil;
import io.netty.util.internal.SystemPropertyUtil;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Checks if a thread is alive periodically and runs a task when a thread dies.
* <p>
* This thread starts a daemon thread to check the state of the threads being watched and to invoke their
* associated {@link Runnable}s. When there is no thread to watch (i.e. all threads are dead), the daemon thread
* will terminate itself, and a new daemon thread will be started again when a new watch is added.
* </p>
*/publicfinalclassThreadDeathWatcher {privatestaticfinal InternalLogger logger = InternalLoggerFactory.getInstance(ThreadDeathWatcher.class);
// visible for testingstaticfinal ThreadFactory threadFactory;
// Use a MPMC queue as we may end up checking isEmpty() from multiple threads which may not be allowed to do// concurrently depending on the implemenation of it in a MPSC queue.privatestaticfinal Queue<Entry> pendingEntries = new ConcurrentLinkedQueue<Entry>();
privatestaticfinal Watcher watcher = new Watcher();
privatestaticfinal AtomicBoolean started = new AtomicBoolean();
privatestaticvolatile Thread watcherThread;
static {
String poolName = "threadDeathWatcher";
String serviceThreadPrefix = SystemPropertyUtil.get("io.netty.serviceThreadPrefix");
if (!StringUtil.isNullOrEmpty(serviceThreadPrefix)) {
poolName = serviceThreadPrefix + poolName;
}
// because the ThreadDeathWatcher is a singleton, tasks submitted to it can come from arbitrary threads and// this can trigger the creation of a thread from arbitrary thread groups; for this reason, the thread factory// must not be sticky about its thread group
threadFactory = new DefaultThreadFactory(poolName, true, Thread.MIN_PRIORITY, null);
}
/**
* Schedules the specified {@code task} to run when the specified {@code thread} dies.
*
* @param thread the {@link Thread} to watch
* @param task the {@link Runnable} to run when the {@code thread} dies
*
* @throws IllegalArgumentException if the specified {@code thread} is not alive
*/publicstaticvoidwatch(Thread thread, Runnable task) {
if (thread == null) {
thrownew NullPointerException("thread");
}
if (task == null) {
thrownew NullPointerException("task");
}
if (!thread.isAlive()) {
thrownew IllegalArgumentException("thread must be alive.");
}
schedule(thread, task, true);
}
/**
* Cancels the task scheduled via {@link #watch(Thread, Runnable)}.
*/publicstaticvoidunwatch(Thread thread, Runnable task) {
if (thread == null) {
thrownew NullPointerException("thread");
}
if (task == null) {
thrownew NullPointerException("task");
}
schedule(thread, task, false);
}
privatestaticvoidschedule(Thread thread, Runnable task, boolean isWatch) {
pendingEntries.add(new Entry(thread, task, isWatch));
if (started.compareAndSet(false, true)) {
Thread watcherThread = threadFactory.newThread(watcher);
///
watcherThread.setDaemon(false); 本人在源码中添加了这行代码,用以保证即使main线程结束,watch线程也能继续运行///
watcherThread.start();
ThreadDeathWatcher.watcherThread = watcherThread;
}
}
/**
* Waits until the thread of this watcher has no threads to watch and terminates itself.
* Because a new watcher thread will be started again on {@link #watch(Thread, Runnable)},
* this operation is only useful when you want to ensure that the watcher thread is terminated
* <strong>after</strong> your application is shut down and there's no chance of calling
* {@link #watch(Thread, Runnable)} afterwards.
*
* @return {@code true} if and only if the watcher thread has been terminated
*/publicstaticbooleanawaitInactivity(long timeout, TimeUnit unit) throws InterruptedException {
if (unit == null) {
thrownew NullPointerException("unit");
}
Thread watcherThread = ThreadDeathWatcher.watcherThread;
if (watcherThread != null) {
watcherThread.join(unit.toMillis(timeout));
return !watcherThread.isAlive();
} else {
returntrue;
}
}
privateThreadDeathWatcher() { }
privatestaticfinalclassWatcherimplementsRunnable {privatefinal List<Entry> watchees = new ArrayList<Entry>();
@Overridepublicvoidrun() {
for (;;) {
fetchWatchees();
notifyWatchees();
// Try once again just in case notifyWatchees() triggered watch() or unwatch().
fetchWatchees();
notifyWatchees();
try {
Thread.sleep(1000);
} catch (InterruptedException ignore) {
// Ignore the interrupt; do not terminate until all tasks are run.
}
if (watchees.isEmpty() && pendingEntries.isEmpty()) {
// Mark the current worker thread as stopped.// The following CAS must always success and must be uncontended,// because only one watcher thread should be running at the same time.boolean stopped = started.compareAndSet(true, false);
assert stopped;
// Check if there are pending entries added by watch() while we do CAS above.if (pendingEntries.isEmpty()) {
// A) watch() was not invoked and thus there's nothing to handle// -> safe to terminate because there's nothing left to do// B) a new watcher thread started and handled them all// -> safe to terminate the new watcher thread will take care the restbreak;
}
// There are pending entries again, added by watch()if (!started.compareAndSet(false, true)) {
// watch() started a new watcher thread and set 'started' to true.// -> terminate this thread so that the new watcher reads from pendingEntries exclusively.break;
}
// watch() added an entry, but this worker was faster to set 'started' to true.// i.e. a new watcher thread was not started// -> keep this thread alive to handle the newly added entries.
}
}
}
privatevoidfetchWatchees() {
for (;;) {
Entry e = pendingEntries.poll();
if (e == null) {
break;
}
if (e.isWatch) {
watchees.add(e);
} else {
watchees.remove(e);
}
}
}
privatevoidnotifyWatchees() {
List<Entry> watchees = this.watchees;
for (int i = 0; i < watchees.size();) {
Entry e = watchees.get(i);
if (!e.thread.isAlive()) {
watchees.remove(i);
try {
e.task.run();
} catch (Throwable t) {
logger.warn("Thread death watcher task raised an exception:", t);
}
} else {
i ++;
}
}
}
}
privatestaticfinalclassEntry {final Thread thread;
final Runnable task;
finalboolean isWatch;
Entry(Thread thread, Runnable task, boolean isWatch) {
this.thread = thread;
this.task = task;
this.isWatch = isWatch;
}
@OverridepublicinthashCode() {
return thread.hashCode() ^ task.hashCode();
}
@Overridepublicbooleanequals(Object obj) {
if (obj == this) {
returntrue;
}
if (!(obj instanceof Entry)) {
returnfalse;
}
Entry that = (Entry) obj;
return thread == that.thread && task == that.task;
}
}
}