在Java中,实现多线程的类是 Thread 类。
定义Thread对象有两种方法,一种是直接继承Thread,另一种是实现Runable接口。两者都需要写 run 方法。
而且,无论是哪一种方法,生成的线程都是 Thread。也就是说,其实线程类只有一种,那就是Thread。
线程也有自己的状态,除了新建,就绪,运行,等待/阻塞,死亡。
这些状态可以转换。线程个状态的转换是:就绪可以进入运行,运行也可以进入就绪;运行还可以进入等待/阻塞;
但是等待/阻塞不能进入运行,等待/阻塞可以进入就绪状态。
# 新建 :就是利用Thread的构造函数定义一个线程,从此,线程的生命开始了。
# 就绪 :当执行了Thread中的strat方法,及使得线程进入就绪状态,随时可以进入运行状态。
# 运行 :当操作系统的调度器把CPU分给了这个线程,这个线程就进入了运行状态,
也就是执行我们在线程中定义的run方法里面的内容。
# 等待/阻塞 :
1. 当一个线程的继续执行需要一个条件,但是该条件还不成熟,需要调度器来通知。
而等待调度器发出通知的这段时间内,线程就进入了等待状态。
2. 当一个线程访问一个资源的时候,这个资源正被另外的线程访问,而这个资源是加了锁的,
之允许一个线程访问。这时候,线程就进入了阻塞状态。
# 死亡:当线程的run方法运行完之后,线程死亡,这是正常死亡;
当线程方发生异常而没有捕获时,也会死亡,这是非正常死亡。
不管是正常死亡还是非正常死亡,都是死亡
下面是一些重要属性:
* 优先级:每个线程都有自己的优先级 。
Java中的线程有10个优先级。但是结合操作系统来说,每个操作系统的线程优先级的数量各不相同。
所以,Java提供了三个常量,分别是MIN_PRIORITY,NORM_PRIORITY,MAX_PRIORITY。
* 守护属性 :如果线程被标志为守护属性,那么这个线程就是守护线程 。
守护线程不能阻止程序(进程)的退出,只要没有主线程,程序就结束,
而不管守护线程的运行状态。所以,我们一般不在守护线程中做一些主要工作。
设置线程的守护属性函数为:setDaemon(true)
* 异常处理:如果线程抛出的异常没有被捕获,线程就会死亡。这就意味着,在设计线程时,要重视捕获异常。
但是,如果没有捕获异常,或者出现了一个异常没有捕捉到,是不是线程就马上死亡呢?
当然不是。线程有一个重要的属性,就是未捕获的异常处理器 。
使用这个方法设置:setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
如果设置了这个属性,就表示如果有异常没有被捕获,那么就会启动这个处理器来处理这个异常,
这样,就加强了我们都线程的处理能力。
UncaughtExceptionHandler是一个接口,我们需要实现unCaughtException(Thread t, Throwalbe e)方法。
如果我们忘了为每个线程设置UncaughtExceptionHandler的时候,是不是线程抛出为捕获的异常时,就会死亡呢?
答案是否定的。当线程抛出未捕获的异常时,而线程又没有设置处理器时,怎么办呢?
这时候,那么这时候的为捕获的异常处理器就变成了ThreadGroup,就是线程组(什么是ThreadGroup,等会再说)。
因为线程组实现了 UncaughtExceptionHandler。所以,ThreadGroup中也有uncaughtException方法,
所以,也可以使用这个 ThreadGroup作为处理器。
如果调用了ThreadGroup中的uncaughtException,那么会发生什么?
首先,会查看这个ThreadGroup有没有父线程组,如果有,则调用父线程组的uncaguhtException方法
否则,则使用全局的线程默认未捕获的异常处理器。
如果全局的线程默认为捕获异常处理器没有定义。再看该线程没有捕获的异常是不是ThreadDeath的一个实例,
如果是,则设么都不做。
如果不是ThreadDeath,错误信息会被输出到System.err上。
那么,如何设置线程的全局默认为捕获异常处理器呢?
调用Thread的一个静态方法:
Thread.sedDeafaultUncaughtExceptionHandler(UncaughtExceptionHandler eh)
至此,线程对为捕获异常的处理的过程就是这样: