1.Thread详解,直接看示例代码,注释非常详细
package com.landon.mavs.example.concurrent;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Arrays;
import java.util.Map;
import java.util.Map.Entry;
import java.util.concurrent.TimeUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.landon.mavs.example.util.PrintUtil;
/** *//**
*
* 线程基础示例
*
* <pre>
* 1.public class Thread implements Runnable,其实现了Runnable接口.且如果其构造中传入了Runnable
* target,则其实现的run直接调用target.run.否则需要覆写run
*
* 2.其内部定义了一个表示线程状态的枚举
* {@link Thread.State}.包括NEW/RUNNABLE/BLOCKED/WAITING/TIMED_WAITING/TERMINATED
* 6个状态.
*
* 3.其内部还定义了一个接口{@link java.lang.Thread.UncaughtExceptionHandler}
* ,用来表示线程因未捕获的异常而突然终止时,调用处理程序的接口. 接口中只有一个方法:void uncaughtException(Thread t,
* Throwable e).
* {@link Thread#setUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)}
* {@link Thread#getUncaughtExceptionHandler()}
* {@link Thread#setDefaultUncaughtExceptionHandler(java.lang.Thread.UncaughtExceptionHandler)}},该方法是一个静态方法.
* ->{@link ThreadGroup#uncaughtException(Thread, Throwable)}
*
* 4.其内部定义了3个线程优先级常量,分别是{@link Thread#MIN_PRIORITY},{@link Thread#NORM_PRIORITY},{@link Thread#MAX_PRIORITY}
* ->三个常量值分别是1,5,10
* </pre>
*
* @author landon
*
*/
public class ThreadBaseExample {
private static final Logger LOGGER = LoggerFactory
.getLogger(ThreadBaseExample.class);
public static void main(String[] args) {
// public Thread(ThreadGroup group, Runnable target, String name,long
// stackSize)
// 1.Thread构造方法最多可传入4个参数,分别是线程组/run方法的目标任务/新线程的名称/新线程的预期堆栈大小,为0表示忽略该参数
// 2.Thread的其他7个构造方法也均是对这4个参数直接或间接的指定
// 从源码看:
// 1.其内部实现均是调用了私有的private void init(ThreadGroup g, Runnable target,
// String name,long stackSize)方法.
// 2.构造如果未指定线程名字,则会初始化为"Thread-" +
// nextThreadNum(),nextThreadNum则为线程的一个内部静态计数器,从0开始(synchronized)
// 3.如果未指定线程组group.则首先设置group为SecurityManager的线程组,如果还为空的话,则设置为当前调用线程所属的线程组.
// 同时isDaemon/priority属性设置和当前调用线程一致.
// 4.stackSize这个参数,从API
// doc来看:该参数的作用具有高度的平台依赖性(在某些平台上,该参数可能不会起任何作用).因为使用它时要非常小心(所以API说要Java实现者文档化该参数的实现行为).
// (可参考JVM参数-Xss来理解stackSize).
// 5.线程的tid也是根据nextThreadID生成,即也是一个内部计时器,从1开始(synchronized),区别于name的计数器从0开始(一个是++i,一个是i++)
try {
Thread t1 = new Thread(null, null, null, 0);
// 运行程序,发现报了一个空指针异常,因为第三个参数name传了null.而在Thread的源码中是用char[]存储name的,即在init要对name进行进行name.toCharArray()操作.
// 所以就报了空指针.所以如果用上面的构造函数,必须指定name.即name要么指定不为空,要么不指定自生成
LOGGER.debug(
"t1.name:{},t1.groupName:{},t1.id:{},t1.isDaemon:{},t1.priority:{}",
t1.getName(), t1.getThreadGroup().getName(), t1.getId(),
t1.isDaemon(), t1.getPriority());
} catch (Exception e) {
LOGGER.warn("", e);
}
// 其内部调用:init(null, null, "Thread-" + nextThreadNum(), 0)
Thread t2 = new Thread();
// 输出:t2.name:Thread-0,t2.groupName:main,t2.id:9,t2.isDaemon:false,t2.priority:5
// 问题:为什么t2.id什么为9呢?因为线程id从1开始,也就是说已经初始化了8个线程
// landon:1.因为jvm程序启动后,会启动一些守护线程(JVM内部线程).从下面的systemGroup输出即可了解大概.
// 2.用jstack查看了一下,另外启动的三个线程是Low Memory Detector/C2 CompilerThread1/C2
// CompilerThread0
LOGGER.debug(
"t2.name:{},t2.groupName:{},t2.id:{},t2.isDaemon:{},t2.priority:{},t2.state:{}",
t2.getName(), t2.getThreadGroup().getName(), t2.getId(),
t2.isDaemon(), t2.getPriority(), t2.getState());
// 获得系统线程组
ThreadGroup systemGroup = t2.getThreadGroup().getParent();
Thread[] totalThreads = new Thread[systemGroup.activeCount()];
systemGroup.enumerate(totalThreads);
// 输出: [Thread[Reference Handler,10,system], Thread[Finalizer,8,system],
// Thread[Signal Dispatcher,9,system], Thread[Attach Listener,5,system],
// Thread[main,5,main]
// 从输出可以看出,系统线程组下有多个活动线程,当然有一些是守护线程,除了main线程外,其余均是守护线程
LOGGER.debug("application.totalThreads:{}",
Arrays.toString(totalThreads));
// 从输出可以看出,主线程main的id为1,即为初始化的第一个线程,然后是Reference
// Handler(2)/Finalizer(3)/Signal Dispatcher(4)/Attach Listener(5)
// 这5个均是jvm内部线程
for (Thread t : totalThreads) {
LOGGER.debug("{} .id:{}", t.getName(), t.getId());
}
ThreadGroup[] totalGroups = new ThreadGroup[systemGroup
.activeGroupCount()];
systemGroup.enumerate(totalGroups);
// 输出:[java.lang.ThreadGroup[name=main,maxpri=10]] 也就是说系统线程组下只有一个主线程组
LOGGER.debug("application.totalGroups:{}", Arrays.toString(totalGroups));
Thread t3 = new Thread(new BaseExampleTask(), "t3");
// Thread#public synchronized void start()
// 使该线程开始执行(结果是两个线程并发的执行,即当前调用start的线程以及另一个执行run的线程)
// 不能多次启动一个线程/当线程已经结束执行后,不能再重新启动,否则会抛出java.lang.IllegalThreadStateException
// 这个很重要.不要试图有"重启线程"这个想法.如果想复用线程,则任务可采用while(flag)形式,详细可参考线程池实现
// 可从os上来理解线程的本质(假如"线程可重启的话",会有很多问题.1.抛出异常的任务你重启有毛用 2.重启后如何再提交任务?
// 3.何时销毁线程?内存泄露? 等等。。) // 此纯属landon个人猜测
// 从源码上看:
// 1.判断当前状态,如果不是"NEW"或者this !=
// me(线程初始化init的时候,会将this赋值给me),则抛出IllegalThreadStateException
// 2.group.add(this)
// 将自身加入到所属的线程组中->即只有执行了start方法才会将线程加入到所属的线程组中,这里是main线程组
t3.start();
try {
LOGGER.debug("t3.state:{}", t3.getState());
// 从输出可以看到,抛出了IllegalThreadStateException.因为此时state已经是RUNNABLE了
t3.start();
} catch (Exception e) {
LOGGER.warn("", e);
}
try {
// 主线程等待t3执行完毕
t3.join();
} catch (Exception e) {
LOGGER.warn("", e);
}
try {
LOGGER.debug("t3.state:{}", t3.getState());
// 从输出可以看到,抛出了IllegalThreadStateException.因为此时state已经是TERMINATED了
t3.start();
} catch (Exception e) {
LOGGER.warn("", e);
}
// Thread#public static int activeCount()
// 该方法是一个静态方法.返回当前调用线程所属的线程组中活动线程的数目,而当前调用线程为main
LOGGER.debug("main.group.activeCount:{}", Thread.activeCount());
// Thread#public final void checkAccess()
// 如果有SecurityManager,则调用security.checkAccess(this)
// {@link SecurityManager#checkAccess(Thread t)}
// 即判断当前的调用线程是否有权限修改该线程,如果不允许则抛出SecurityException
// 在setName/setPriority/setDaemon方法的实现中都均调用了checkAccess()方法,如果无权限修改则抛出SecurityException,则不会执行后续修改逻辑
t3.checkAccess();
// Thread#public final void setName(String name) 改变线程名称
t3.setName("t-modify-3");
// Thread# 更改的线程优先级
// 从源码看:
// 1.checkAccess 判断是否有权限修改
// 2.判断参数的合法化,即参数必须要在[MIN_PRIORITY,MAX_PRIORITY],否则抛出IllegalArgumentException
// 3.判断所属线程组的最大允许优先级,线程优先级被设置为指定的newPriority和所属线程组的最大允许优先级中较小的一个{@link
// ThreadGroup#setMaxPriority(int pri)}
// ThreadGroup默认的maxPriority为parent的maxPriority,main的parent为system线程组,而system线程组的maxPriority为10
t3.setPriority(Thread.MIN_PRIORITY);
// 从这里输出看发现t3的优先级并没有被修改,还是5.why?看下面的解释(因为线程已经运行结束.所属线程组已经变为了null.所以设置优先级失败)
LOGGER.debug("t3.name:{},t3.priority:{}", t3.getName(),
t3.getPriority());
// Thread#public final ThreadGroup getThreadGroup()
// 返回该线所属的线程组.注意,如果该线程已经停止运行,则该方法返回null.
// 从输出看,因为前面的代码调用了join,即t3肯定已经结束了.所以输出抛出了空指针异常
// 从源码看:
// 1.Thread内部有一个私有的exit方法.private void exit().
// 该方法应该是被系统调用,即线程真正退出的时候给线程一个清理的机会
// 2.该方法的实现中,如果group不为null,则从group中移除自身并设置group为null.
try {
LOGGER.debug("mainGroup.maxPriority:{}", t3.getThreadGroup()
.getMaxPriority());
} catch (Exception e) {
LOGGER.warn("", e);
}
// Thread#public static native Thread currentThread() 静态方法
// 返回当前正在执行调用的线程对象的引用,即正在执行这句代码的线程 而当前调用线程为主线程main
LOGGER.debug("currentThread.name:{}", Thread.currentThread().getName());
// Thread#public static void dumpStack() 静态方法 将当前线程的堆栈跟踪打印至标准错误流
// 源码实现:new Exception("Stack trace").printStackTrace();
Thread.dumpStack();
// 相当于Thread.dumpStack()
new Exception("dump stack trace").printStackTrace();
Thread[] mainActiveThreads = new Thread[Thread.activeCount()];
// Thread#public static int enumerate(Thread tarray[]) 静态方法
// 将当前调用的线程组及其子组的另一个活动线程复制到指定的数组
// 源码实现:return currentThread().getThreadGroup().enumerate(tarray)
// {@link ThreadGroup#enumerate(Thread list[])}
Thread.enumerate(mainActiveThreads);
LOGGER.debug("mainActiveThreads:{}", Arrays.toString(mainActiveThreads));
// Thread#public static Map<Thread, StackTraceElement[]>
// getAllStackTraces()
// 静态方法 返回所有活动线程的一个堆栈跟踪的map.每个线程的堆栈跟踪至代表一个快照
// 源码看:
// 1.检查SecurityManager,即是否有GET_STACK_TRACE_PERMISSION/MODIFY_THREADGROUP_PERMISSION的权限
// 2.调用Thread内部的私有方法 private native static Thread[] getThreads()
// 获取线程列表快照
// 3.调用Thread内部私有方法private native static StackTraceElement[][]
// dumpThreads(Thread[] threads) 执行dump
// 4.StackTraceElement[][]转为map
Map<Thread, StackTraceElement[]> mainActiveStackTraceMap = Thread
.getAllStackTraces();
for (Entry<Thread, StackTraceElement[]> entry : mainActiveStackTraceMap
.entrySet()) {
// 从输出可以看出:如果当前没有线程的堆栈跟踪信息,则反返回一个零长度数组而非null.因为PrintUtil.printStackTrace未报空指针异常
LOGGER.debug("mainActiveStackTraceMap.thread.name:{}", entry
.getKey().getName());
PrintUtil.printStackTrace(entry.getValue(), System.err);
}
// Thread#public StackTraceElement[] getStackTrace()
// 返回一个表示该线程堆栈转储的堆栈跟踪元素数组.如果该线程尚未启动或者已经终止,则该该方法返回一个零长度数组.{@link
// Thread#EMPTY_STACK_TRACE},是一个private static final
// StackTraceElement[].数组的第一个元素代表堆栈顶,其是该序列中最新的方法调用,最后一个元素代表堆栈底,是该序列中最旧的方法调用
// 从源码看:
// 1.会检查this和Thread.currentThread是否一致,如果不一致,则调用SecurityManager是否有GET_STACK_TRACE_PERMISSION
// ->判断线程是否alive,如果非alive,则返回EMPTY_STACK_TRACE
// ->调用dumpThreads(new Thread[] {this})
// 2.如果this == Thread.currentThread,则直接return (new
// Exception()).getStackTrace()
StackTraceElement[] mainThreadStackTraceElements = Thread
.currentThread().getStackTrace();
PrintUtil.printStackTrace(mainThreadStackTraceElements, System.err);
// Thread#public ClassLoader getContextClassLoader()
// 返回该线程的上下文Classloader.上下文Classloader由线程创建者提供,供运行于该线程中的代码在加载类和资源时使用.如果未设定,则默认为父线程的Classloader上下文.
// 原始线程的上下文Classloader通常设定为用于加载应用程序的类加载器.
// 从输出的结果看:返回的是sun.misc.Launcher$AppClassLoader
// 从源码看:如果有SecurityManager且调用者的类加载器不为null且也不同于其上下文类加载器正在被请求的线程上下文类加载器的祖先,则check是否有GET_CLASSLOADER_PERMISSION的权限
// {@link Thread#setContextClassLoader(ClassLoader cl)}
ClassLoader mainContextClassLoader = Thread.currentThread()
.getContextClassLoader();
LOGGER.debug("mainContextClassLoader:{}", mainContextClassLoader);
// Thread#public static UncaughtExceptionHandler
// getDefaultUncaughtExceptionHandler()
// 静态方法,返回线程由于未捕获到异常而突然终止时调用的默认处理程序{@link
// Thread#setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler
// eh)}
// 从源码上看:
// 1.当线程由于未捕获到异常而突然终止时,是首先调用{link
// Thread#getUncaughtExceptionHandler()},在私有的方法dispatchUncaughtException(Throwable
// e)调用
// 2.getUncaughtExceptionHandler首先判断线程自身的uncaughtExceptionHandler是否为null,如果不为null,则直接返回当前;否则返回group.
// =>因为ThreadGroup implements Thread.UncaughtExceptionHandler
// 3.在ThreadGroup#uncaughtException方法中,判断parent是否为null,如果不为null则调用parent.uncaughtException方法.
// 否则则调用Thread.getDefaultUncaughtExceptionHandler()
// 也就说线程的这个静态DefaultUncaughtExceptionHandler是在线程自身未指定handler,线程的线程组系列(包括父线程组)也未指定uncaughtException方法,则才会转至这个默认的handler
UncaughtExceptionHandler defaultUncaughtExceptionHandler = Thread
.getDefaultUncaughtExceptionHandler();
// 从输出看,现在为null
LOGGER.debug("defaultUncaughtExceptionHandler:{}",
defaultUncaughtExceptionHandler);
// Thread#public static void
// setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
// 注意一个问题:不要设置handler为线程的ThreadGroup对象,否则会引起无限递归.
// {@link ThreadGroup#uncaughtException(Thread t, Throwable e)}
Thread.setDefaultUncaughtExceptionHandler(new ExampleDefaultUncaughtExceptionHandler());
// 启动了一个抛出异常的任务.从输出看,执行了ExampleDefaultUncaughtExceptionHandler的uncaughtException方法
Thread t4 = new Thread(new ExampleExceptionTask(), "t4");
t4.start();
// Thread#public UncaughtExceptionHandler getUncaughtExceptionHandler()
// return uncaughtExceptionHandler != null ? uncaughtExceptionHandler :
// group;
// 输出:t4.uncaughtExceptionHandler:java.lang.ThreadGroup[name=main,maxpri=10]
// 从输出看出,返回的是group
LOGGER.debug("t4.uncaughtExceptionHandler:{}",
t4.getUncaughtExceptionHandler());
Thread t5 = new Thread(new ExampleExceptionTask(), "t-5");
// Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler eh)
// 从输出看,抛出异常时执行了{@link AUncaughtExcaptionHandler#uncaughtException}方法
// 源码实现中首先调用了 checkAccess(),即是否有权限修改
t5.setUncaughtExceptionHandler(new AUncaughtExcaptionHandler());
t5.start();
// 从输出看:com.landon.mavs.example.concurrent.ThreadBaseExample$AUncaughtExcaptionHandle
// 即处处了线程设置的handler
LOGGER.debug("t5.uncaughtExceptionHandler:{}",
t5.getUncaughtExceptionHandler());
Thread t7 = new Thread(new ExampleDaemonTask(), "t7");
t7.setDaemon(true);
t7.start();
try {
// 这里主线程等待t7结束.因为主线程是用户线程,所以t7可完全结束.
t7.join();
} catch (Exception e) {
LOGGER.warn("t7.join.exception.", e);
}
Thread t6 = new Thread(new ExampleDaemonTask(), "t6");
// Thread#public final void setDaemon(boolean on)
// 将线程标记为守护线程或者用户线程.当正在运行的线程都是守护线程时,Java虚拟机退出
// 注意该方法必须在启动线程前调用
// 源码上看:1.checkAccess
// 2.判断isAlive(),如果alive,则抛出IllegalThreadStateException 3.设置daemon属性为参数
t6.setDaemon(true);
// 从输出看,设置t6为守护线程后,任务只输出了begin,未输出end或者异常输出.也就是说虚拟机直接退出了.未执行打断守护线程类似的逻辑.
t6.start();
Thread t8 = new Thread("t8");
// Thread#public String toString() 返回该线程的字符串表示形式
// 返回:Thread[name,priority,group.name],如果group为null,则没有group.name
LOGGER.debug("t8:{}", t8.toString());
// 静态方法, 暂停当前正在执行的线程对象,并执行其他线程
// yield()只是使当前线程重新回到可执行状态,所以执行yield()的线程有可能在进入到可执行状态后马上又被执行
// yield()只能使同优先级的线程有执行的机会
Thread.yield();
// 从输出大致可以看到,每到2的倍数时就切换了一下线程
for (int i = 0; i < 2; i++) {
new YieldThread("yt-" + i).start();
}
// Thread#public final native boolean isAlive() 测试线程是否处于活动状态
// 如果线程已经启动且尚未终止则返回true
LOGGER.debug("t6.isAlive:{}", t6.isAlive());// true 已启动,但是为结束
LOGGER.debug("t8.isAlive:{}", t8.isAlive());// false,因为还未启动
LOGGER.debug("t7.isAlive:{}", t7.isAlive());// false 已启动,但是已结束
// 测试Thread#holdsLock(Object obj)方法
HoldsLockThread hlt = new HoldsLockThread();
hlt.start();
}
private static class BaseExampleTask implements Runnable {
@Override
public void run() {
LOGGER.debug("BaseExampleTask begin");
// sleep模拟任务耗时
try {
Thread.sleep(3 * 1000);
} catch (Exception e) {
LOGGER.debug("BaseExampleTask was interrupted.");
}
LOGGER.debug("BaseExampleTask end");
}
}
private static class ExampleExceptionTask implements Runnable {
@Override
public void run() {
throw new RuntimeException();
}
}
private static class ExampleDefaultUncaughtExceptionHandler implements
UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.debug(String.format("thread.name:%s", t.getName()), e);
}
}
private static class AUncaughtExcaptionHandler implements
UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
LOGGER.debug("this a thread:{} UncaughtExcaptionHandler.", t);
LOGGER.warn("", e);
}
}
// 一个Daemon任务
private static class ExampleDaemonTask implements Runnable {
@Override
public void run() {
Thread curThread = Thread.currentThread();
LOGGER.debug("ExampleDaemonTask" + "[" + curThread.getName() + "]"
+ " begin");
try {
// 这里让sleep时间长了一下,看一下守护任务是否被终止
TimeUnit.SECONDS.sleep(10);
} catch (InterruptedException e) {
LOGGER.warn("ExampleDaemonTask was interrupted.", e);
}
LOGGER.debug("ExampleDaemonTask" + "[" + curThread.getName() + "]"
+ " end");
}
}
private static class YieldThread extends Thread {
public YieldThread(String name) {
super(name);
}
@Override
public void run() {
for (int i = 3; i <= 10; i++) {
LOGGER.debug(String.format("[%s]:%d", getName(), i));
if (i % 2 == 0) {
// 执行yield,暂停一下,让出cpu
Thread.yield();
}
}
}
}
private static class HoldsLockThread extends Thread {
private final Object lock = new Object();
@Override
public void run() {
try {
LOGGER.debug("HoldsLockThread.holdsLock:{}", holdsLock(lock));// false
synchronized (lock) {
lock.wait(1 * 1000);
// Thread#public static native boolean holdsLock(Object obj)
// 静态方法 当且仅当当前线程在指定对象上保持监视器锁时,才返回true
LOGGER.debug("HoldsLockThread.holdsLock:{}",
holdsLock(lock));// true
}
LOGGER.debug("HoldsLockThread.holdsLock:{}", holdsLock(lock));// false
} catch (InterruptedException e) {
}
}
}
}
2.本篇结合jdk源码+示例代码详细介绍了Thread的API.
posted on 2013-12-23 15:23
landon 阅读(1702)
评论(0) 编辑 收藏 所属分类:
Program