在现代操作系统中,运行一个程序时,会为其创建一个进程。例如启动一个QQ程序,操作系统就会为其创建一个进程。而操作系统中调度的最小单位元是线程,也叫轻量级进程,在一个进程里可以创建多个进程,这些线程都拥有各自的计数器,堆栈和局部变量等属性,并且能够访问共享的内存变量。处理器在这些线程上高速切换,让使用者感觉到这些线程在同时执行。因此我们可以这样理解:
public class SubThread extends Thread {
@Override
public void run() {
Log.e("SubThread", "I'm a thread extends Thread");
}
}
public class SubRunnable implements Runnable {
@Override
public void run() {
Log.e("SubRunnable", "I'm a thread implements Runnable");
}
}
启动方式:
public static void main(String[] args) {
//继承Thread方式的启动线程
SubThread t1 = new SubThread();
t1.start();
//实现Runnable接口方式的启动线程
SubRunnable r1 = new SubRunnable();
Thread t2 = new Thread(r1);
t2.start();
}
调用start()方法后并不是立即执行线程的run()方法。而是使该线程变为可运行状态,什么时候运行run()方法是由操作系统决定的。
直接调用run()方法而不是start()方法,并没有启动线程,只是会执行run()方法内部的代码而已。
当线程的run()方法执行方法体中的最后一条语句后,并经由执行return语句返回时,或者在方法中出现没有捕获的异常时线程将终止。在java早期版本中有一个stop方法,其他线程可以调用它终止线程,但是这个方法现在已经被弃用了,因为这个方法会造成一些线程不安全的问题。
我们可以把中断理解为一个标识位的属性,它表示一个运行中的线程是否被其他线程进行了中断操作,而中断就好比其他线程对该线程打了个招呼,其他线程通过调用该线程的interrupt()方法对其进行中断操作。当一个线程调用interrupt()方法时,线程的中断状态(标识位)将被置位(改变),这是每个线程都具有的boolean标识。每个线程都应该不时地检查这个标识,来判断线程是否被中断。而要判断线程是否被中断,我们可以使用如下代码:
while(!Thread.currentThread().isInterrupted()){
do something
}
但是如果此时线程处于阻塞状态(sleep或wait),就无法检查中断状态,此时会抛出InterruptedException异常。如果每次迭代之后都调用sleep()方法(或者其他可中断的方法),isInterrupted()检测就没必要也没用处了。示例代码:
public void run(){
while(more work to do ){
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
//thread was interrupted during sleep
e.printStackTrace();
}finally{
//clean up , if required
}
}
}
最后关于中断线程,我们这里给出中断线程的一些主要方法:
首先,我们可以通过Thread.setDaemon(true)的方法将线程转化为守护线程。而守护线程的唯一作用就是为其他线程提供服务。计时线程就是一个典型的例子,它定时地发送“计时器滴答”信号告诉其他线程去执行某项任务。
当只剩下守护线程时,虚拟机就退出了,因为如果只剩下守护线程,程序就没有必要执行了。另外JVM的垃圾回收、内存管理等线程都是守护线程。还有就是在做数据库应用时候,使用的数据库连接池,连接池本身也包含着很多后台线程,监控连接个数、超时时间、状态等等。
最后有一点需要特别注意的是在Java虚拟机退出时Daemon线程中的finally代码块不一定会执行。因此在构建Daemon线程时,不能依靠finally代码块中的内容来确保执行关闭或清理资源的逻辑。
在现代操作系统中基本采用时分的形式调度运行的线程,操作系统会分出一个个时间片,线程会分配到若干时间片,当线程的时间片用完了就会发生线程调度,并等待着下一次分配。线程分配到的时间片多少也决定了线程使用处理器资源的多少,而线程优先级就是决定线程需要多或者少分配一些处理器资源的线程属性。
在java线程中,通过一个整型的成员变量Priority来控制线程优先级,每一个线程有一个优先级,默认情况下,一个线程继承它父类的优先级。可以将优先级设置在MIN_PRIORITY(在Thread类定义为1)与MAX_PRIORITY(在Thread类定义为10)之间的任何值。线程的默认优先级为NORM_PRIORITY(在Thread类定义为5)。尽量不要依赖优先级,如果确实要用,应该避免初学者常犯的一个错误。如果有几个高优先级的线程没有进入非活动状态,低优先级线程可能永远也不能执行。每当调度器决定运行一个新线程时,首先会在具有高优先级的线程中进行选择,尽管这样会使低优先级的线程可能永远不会被执行到。
因此我们在设置优先级时,针对频繁阻塞(休眠或者I/O操作)的线程需要设置较高的优先级,而偏重计算(需要较多CPU时间或运算)的线程则设置较低的优先级,这样才能确保处理器不会长久独占。还有要注意的是在不同的JVM以及操作系统上线程的规划存在差异,有些操作系统甚至会忽略对线程优先级的设定,如mac OS系统或者Ubuntu系统。
上图中的方法解析如下: