Java的多线程之基础篇

一、线程

1>创建新线程

a>自定义类并实现Runnable接口,实现run()方法。 

b >创建Thread子类的实例,用实现Runnable接口的对象作为参数实例化该Thread对象。 

c>调用Thread的start()方法来启动该线程。

使用Thread t = new Thread(Runnable target);将创建一个线程。

注:不要调用Thread类或Runnable对象的run方法。直接调用run方法,只会执行同一个线程中的任务,而不会启动新线程。应该调用Thread.start方法,这个方法将创建一个执行run方法的新线程。

2>中断线程

a>当线程的run方法执行方法体中最后一条语句后,并经由执行return语句返回时,或者出现了在方法中没有捕获的异常时,线程将终止。在Java的早期版本中,还有一个stop方法,其它线程可以调用它终止程序。但是,这个方法现在已经被弃用了。

b>使用Interrupt方法可以用来请求终止线程,当对线程调用interrupt方法时,线程的中断状态将被置位。这是每一个线程都具有的boolean状态。每个线程都应该不时地检查这个标志,以判断线程是否被中断。

c>判断中断状态是否被置位,使用如下语句:

while(!Thread.currentThread().isInterrupted()&& more work to do){
     do more work
}

如果线程被阻塞,就无法检测中断状态,这是产生InterruptedException异常的地方。当在被阻塞的线程(调用sleep或wait)上调用interrupt方法时,阻塞调用将会被Interrupted Exception异常中断。

如果每次迭代之后都调用sleep方法(或者其他可中断的方法),isInterrupted检测就没必要也没用处了,如果在中断状态被置位时调用sleep方法,它不会休眠反而会清除这一状态并抛出InterruptedException。所以如果在循环中调用sleep,不要去检测中断状态,只需捕获InterruptedException。

在很多发布的代码中会发现InterruptedException被抑制在很低的层次上:

void mySubTask(){
...
try{sleep(delay);}
catch(InterruptedException e){}
...
}

不要这样做,如果不认为catch中做一处理有什么好处的话,有两种合理的选择:

i>在catch中调用Thread.currentThread().interrupt()来设置中断状态。调用者可以对其进行检测。

void mySubTask(){
...
try{sleep(delay);}
catch(InterruptedException e){Thread.currentThread().interrupted();}
...
}

ii>更好的选择用throw InterruptedException标记你的方法,不采用try语句块来捕获已成。这样调用者可以捕获这个异常:

void mySubTask() throws InterruptedException {
...
sleep(delay);
...
}

3>线程状态

线程可以有如下6种状态:

a>New(新创建)

当用new操作符创建一个新线程时,该线程还没有开始运行。这意味着它的状态是new。

b>Runnable(可运行)

一旦调用start方法,线程处于runnable状态。一个可运行的线程可能正在运行也可能没有运行,这取决于操作系统给线程提供运行的时间。(Java的规范说明没有将它作为一个单独状态。一个正在运行中的线程仍然处于可运行状态。)

一旦一个线程开始运行,它不必始终保持运行。事实上,运行中的线程被中断,目的是为了让其他的线程获得运行机会。线程调度的细节依赖于操作系统提供的服务。抢占式调度系统给每一个可运行线程一个时间片来执行任务。当时间片用完,操作系统剥夺该线程的运行权,并给另一个线程运行机会。当选择下一个线程时,操作系统考虑线程的优先级。

注:现在所有的桌面以及服务器操作系统都使用抢占式调度。但是,像手机这样的小型设备可能使用协作式调度。在这样的设备中,一个线程只有在调用yield方法、或者被阻塞或等待时,线程才失去控制权。

在具有多个处理器的机器上,每个处理器运行一个线程,可以有多个线程并行运行。当然,如果线程的数目多于处理器的数目,调度器依然采用时间片机制。

c>Blocked(被阻塞)

d>Waiting(等待)

e>Timed waiting(计时等待)

当线程处于被阻塞或等待状态时,它暂时不活动。它不允许任何代码且消耗最少的资源。直到线程调度器重新激活它。

f>Terminated(被终止)

线程因如下两个原因之一而被终止:

i>因为run方法正常退出而自然死亡。

ii>因为一个没有捕获的异常终止了run方法而意外死亡。

特别是,可以调用线程的stop方法杀死一个线程。该方法抛出ThreadDeath错误对象,由此杀死线程。但是,stop方法已过时,不要在代码中使用。

常用方法:

join()方法等待终止指定的线程。

4>线程属性

a>线程优先级

在Java程序设计语言中,每一个线程有一个优先级。默认情况下,一个线程继承它的父线程的优先级。可以用setPriority方法设置线程的优先级。

注:每当线程调度器有机会选择新线程时,它首先选择具有较高优先级的线程。

       不要将程序构建为功能的正确性依赖于优先级。

常用方法:yield()导致当前线程处于让步状态。如果有其他的可运行线程具有至少与此线程同样高的优先级,那么这些线程接下来会被调度。这是一个静态方法。

b>守护线程

t.setDaemon(true);

将线程转换为守护线程(daemon thread)。守护线程的唯一用途是为其他线程提供服务。当只剩下守护线程时,虚拟机将退出。

注:守护线程应该永远不去访问固有资源,如:文件、数据库,因为它会在任何时候甚至在一个操作的中间发生中断。

c>未捕获异常处理器

线程的run方法不能抛出任何被检测的异常,但是,不被检测的异常会导致线程终止。在这种情况下,线程就死亡了。

但是,不需要任何catch子句来处理可以被传播的异常。相反,就在线程死亡之前,异常被传递到一个用于未捕获异常的处理器。

该处理器必须属于一个实现Thread.UncaughtExceptionHandler接口的类。这个接口只有一个方法。

void uncaughtException(Thread t,Throwable e)

i>可以用setUncaughtExceptionHandler方法为任何线程安装一个处理类。

ii>也可以用Thread类的静态方法setDefaultUncaughtExceptionHandler为所有的线程安装一个默认的处理器。替换处理器可以使用日志API发送未捕获异常的报告到日志文件。

注:如果不安装默认的处理器,默认的处理器为空。但是,如果不为独立的线程安装处理器,此时的处理器就是该线程的ThreadGroup对象。

       线程组是一个可以统一管理的线程集合。默认情况下,创建的所有线程属于相同的线程组,但是,也可能会建立其他的组。现在引入了更好的特性用于线程集合的操作,所以建议不要在程序中使用线程组。

ThreadGroup类实现Thread.uncaughtExceptionHandler接口。它的uncaughtException方法如下操作:

i>如果该线程组有父线程组,那么父线程组的uncaughtException方法被调用。

ii>否则,如果Thread.getDefaultExceptionHandler方法返回一个非空的处理器,则调用该处理器。

iii>否则,如果Throwable是ThreadDeath的一个实例,什么都不做。

iv>否则,线程的名字以及Throwable的栈踪迹被输出到System.err上。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值