一、创建线程并启动
1.两种方式
-
创建一个Thread子类的实例,并调用其start方法
-
创建一个实现Runnable接口的类的实例,并以此实例作为target参数构造一个Thread实例,
2.两种方式的比较
-
将同一个Runnable实例的引用作为多个Thread实例的target构造参数,可以实现资源的共享。因此,Runnable适合于多个相同程序代码线程去处理统一资源的情况,把虚拟的cpu(线程)同程序的代码,数据有效分离,较好体现面向对象的编程的思想
-
Runnable可以避免由于java的单继承机制带来的局限。可以再继承其他类的同时,还能实现多线程的功能。
二、中断线程
1.中断的方式:调用interrupt方法
-
当interrupt方法被调用后,线程的执行并未真的中断,而是将中断状态置位,这是每个java线程都具有的boolean标志。每个线程都应该不停地检查这个状态,以判断线程是否被中断。
while(!Thread.CurrentThread().isInterrupted()){...}
-
如果在每次工作迭代后都调用sleep方法,则isInterrupted检测既无必要也无用处,而应直接捕捉 InterruptedException 。因为在一个 isInterrupted为true的线程上调用sleep方法,它不会休眠。相反,它将清除这一状态并抛出 InterruptedException
-
当一个线程被阻塞(sleep,wait),此线程的 interrupt被调用时,阻塞调用会被InterruptedException中断
-
对 InterruptedException的处理,一种做法是在catch块中调用Thread.CurrentThread().interrupt();以使调用者可以检测。
-
InterruptedException异常被处理后,程序设计者可以决定线程继续执行还是正常结束。
三、控制线程的结束
如果获得了一个线程的引用,可以调用join等待该线程结束。
对于一个持续工作的线程, run方法内可写成while(needEnd){...}的形式 ,通过更改boolean成员变量 needEnd 来控制线程的结束。
四、线程状态
1. NEW - A thread that has not yet started is in this state.
2. RUNNABLE - A thread executing in the Java virtual machine is in this state.
3. BLOCKED - A thread that is blocked waiting for a monitor lock is in this state. Include:
synchronized(lock) { .. }
4. WAITING - A thread that is waiting indefinitely for another thread to perform a particular action is in this state. Include:
(1) Object.wait with no timeout
(2) Thread.join with no timeout
(3) LockSupport.park
5. TIMED_WAITING - A thread that is waiting for another thread to perform an action for up to a specified waiting time is in this state. Include:
(1) Thread.sleep
(2) Object.wait with timeout
(3) Thread.join with timeout
(4) LockSupport.parkNanos
(5) LockSupport.parkUntil
6. TERMINATED - A thread that has exited is in this state.
假设t1,t2先后两个线程,都执行如下代码:
synchronized(Obj){Obj.wait();}
t1先进,最后在Obj.wait()下卡住,这时java管t1的状态为waitting状态,是线程主动等待其他线程唤醒自己。
t2后进,直接在第一行就卡住了,这时java叫t2为blocked状态,是线程进临界区时被动(被jvm)阻塞住。
五、守护线程
jvm内线程分为用户线程和守护线程,两种线程没有实质区别,但是jvm会等待所有用户线程执行完才会退出,而不会等待守护线程。守护线程常用来做监控,但是不适合做IO,因为jvm随时可能退出。
守护线程需要在调用start前设置,否则会抛出IllegalStateExeption。
守护线程产生的线程仍是守护线程。
守护线程与unix守护进程的概念没有任何关系。
六、未捕获异常处理器
Thread的run方法不能抛出任何可被检测的异常,但是不被检测的异常被抛出会导致线程终止。为避免这一情况,可以为线程设置未捕获异常处理器,被抛出的未检测异常会被传递到该处理器来处理。
static void setDefaultUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
void setUncaughtExceptionHandler(Thread.UncaughtExceptionHandler eh)
未检测异常被抛出时,先获取默认处理器,如果未安装,则获取该线程独立处理器,如果也未安装,则此线程的异常处理器为该Thread所属ThreadGroup,因为ThreadGroup实现了Thread.UncaughtExceptionHandler接口。
七、线程组(ThreadGroup)
可以把线程归属到某一个线程组中,线程组中可以有线程对象,也可以有线程组,组中还可以有线程,这样的组织结构有点类似于树的形式,如图所示:
线程组的作用是:可以批量管理线程或线程组对象,有效地对线程或线程组对象进行组织,例如将一组线程放到同一个ThreadGroup中,通过ThreadGroup实例进行start/interrupt。
public class ThreadGroupSample {
public static void main(String[] args) {
TestThread mt0 = new TestThread();
TestThread mt1 = new TestThread();
ThreadGroup tg = new ThreadGroup("新建线程组1");
Thread t0 = new Thread(tg, mt0);
Thread t1 = new Thread(tg, mt1);
t0.start();
t1.start();
System.out.println("活动的线程数为:" + tg.activeCount());
System.out.println("线程组的名称为:" + tg.getName());
try {
Thread.sleep(6000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
tg.interrupt();
}
}
class TestThread implements Runnable{
@Override
public void run() {
try
{
while (!Thread.currentThread().isInterrupted())
{
System.out.println("ThreadName = " + Thread.currentThread().getName());
Thread.sleep(1000);
}
}
catch (InterruptedException e)
{ }
System.out.println(Thread.currentThread().getName() + " is interrupted");
}
}
阅读(364) | 评论(0) | 转发(0) |