判断线程是否执行完毕_Java线程详解
线程简介操作系统运行一个程序时会创建一个进程,而操作系统调度的最小单位是线程,一个进程中可以创建多个线程。例如一个视频播放器是一个进程,而其中音频和视频都可以分别看做一个线程。线程中都有各自的程序计数器、虚拟机栈和本地方法栈,并且能够访问操作共享内存变量。每个时刻单个处理器只运行一个线程,但由于处理器高速运行,宏观上感觉同时运行了多个程序。Java程序从main方法开始执行,执行main...
线程简介
操作系统运行一个程序时会创建一个进程,而操作系统调度的最小单位是线程,一个进程中可以创建多个线程。例如一个视频播放器是一个进程,而其中音频和视频都可以分别看做一个线程。线程中都有各自的程序计数器、虚拟机栈和本地方法栈,并且能够访问操作共享内存变量。每个时刻单个处理器只运行一个线程,但由于处理器高速运行,宏观上感觉同时运行了多个程序。
Java程序从main方法开始执行,执行main方法的是一个main线程,一个Java程序的运行时main线程和其他线程同时运行。
为何使用多线程
现代处理器上核心数越来越多,如何更高效的利用多核处理器已是非常重要的课题。将计算逻辑分割为不同线程安排到不同核心上同时运算,可以很大程度提高运行效率。
有时候我们会编写一些非常复杂的业务逻辑,比如创建一笔订单,可能经过插入订单数据、生成订单快照、发送邮件等等。若用户从单击“提交”按钮开始,一直等很久才得到订单创建成功通知,很大程度上影响了用户的交互体验。但使用多线程,将一些数据一致性不是很强的操作分发给其他线程进行处理,能大大缩短响应时间。
线程优先级
现代操作系统都是采用分配时间片方式运行线程,当时间片用完就会发生线程调度,等待下次分配时间片。线程优先级决定了线程被分配时间片的多少。Java中,使用setPriority(int)来设置优先级,默认为5,范围是1~10。针对频繁阻塞线程设置较高优先级,而偏重计算即使用CPU较多的设置较低优先级,确保处理器不会被长久的独占。
程序的正确性千万不能依赖线程优先级来判断,就算设置了线程优先级,程序可能完全不会理会。
线程的状态
传统的进程一般有new、ready、running、waiting和terminated五种状态,具体的可参见相关操作系统书籍。在Java中,总共分为六种状态,详解见下表。
状态名称 | 说明 |
new | 初始线程被构建,还未调用start()方法 |
runnable | 运行,Java将就绪和运行笼统称为runnable |
blocked | 阻塞,阻塞于锁 |
waiting | 等待,当前线程需要等待其他线程唤醒 |
timed_waiting | 超时等待,超过一定时间可以自行苏醒 |
terminated | 终止,线程执行完 |
下图详细展示了Java线程状态转化。
阻塞状态是线程阻塞在进入synchronized关键字修饰的方法或代码块(获取锁)时状态,但阻塞在java.util.concurrent包中Lock接口的线程却是等待状态,因为java.util.concurrent包中Lock接口对于阻塞的实现均使用了LockSupport类中相关方法。 new关于start()这里思考两个问题:(1)能否反复调用start()方法?(2)若一个程序执行完毕,此时处于terminated状态,再次调用start()是否可行?下面先来看下start()源码。public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
在start()方法内部有个threadStatus状态变量,若不为0,调用start()会抛出异常,这里我们可以知道,调用一次start()之后,threadStatus值会改变(即不为0),此时再调用start()会抛出IllegalThreadStateException异常。
runnable处于runnable状态线程可能在JVM中运行也可能在等待CPU分配资源,所以其包括了传统进程的ready和running两种状态。
blocked处于blocked状态的线程等待锁释放以进入同步区。
waiting处于等待状态的线程变成runnable状态需要其他线程唤醒。
timed_waiting线程等待一个具体的时间,时间到后会被自动唤醒。
terminated终止状态,此时线程已执行完毕。
Daemon守护线程
Daemon线程主要用作程序中后台调度,当一个Java虚拟机中不存在非Daemon线程时,Java虚拟机会退出。可以通过调用Thread.setDaemon(true)来将线程设置为Daemon线程。在Java虚拟机退出时Daemon线程中的finally块并不一定会执行,如下代码示例。
public class Daemon {
static class DaemonRunner implements Runnable {
public static void main(String[] args) {
Thread thread = new Thread(new DaemonRunner(), "DaemonRunner");
thread.setDaemon(true);
thread.start();
}
@Override
public void run() {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("DaemonThread finally run.");
}
}
}
}
命令行没有任何输出。main线程启动了线程DaemonRunner之后随着main线程执行完毕而终止,而此时JVM中没有非Daemon线程,虚拟机退出。在构建Daemon线程时,不能依靠finally块中的内容来确保执行关闭或清理资源的逻辑。
构造线程
运行线程前需要构建一个线程对象,需要提供线程属性,如线程所属线程组、线程优先级、是否是Daemon线程。如下是Thread类中初始化线程的init()方法。
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
if (name == null) {
throw new NullPointerException("name cannot be null");
}
// 当前线程就是该线程的父线程
Thread parent = currentThread();
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
中断
中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作,通过调用interrupt()进行中断操作。线程通过方法isInterrupted()方法判断是否被中断,可以调用Thread.interrupted()对当前线程的中断标识位进行复位。若该线程已是终结状态,即使该线程被中断过,此时isInterrupted也是false。 安全终止线程
中断操作是最适合用来取消或停止任务,除了中断外,还可以利用boolean变量来控制是否需要停止任务并终止该线程。示例代码如下。
public class ShutDown {
public static void main(String[] args) throws InterruptedException {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
Thread.sleep(1000);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
Thread.sleep(1000);
two.cancel();
}
private static class Runner implements Runnable {
private long i;
private volatile boolean on = true;
@Override
public void run() {
while (on && !Thread.currentThread().isInterrupted()) {
i++;
}
System.out.println("Count i = " + i);
}
public void cancel() {
on = false;
}
}
}
//Count i = 435742146
//Count i = 388964025
更多推荐
所有评论(0)