前言
上节我们对线程有了个基本的概念和认识,从线程状态转变过程我们也已经知道了线程通过调用start方法进行启动,直到run方法执行线程结束,今天我们就来详细的说说启动和终止线程的细节,OK,让我们开始今天的并发之旅吧。
创建线程
在使用一个线程之前我们需要先构造线程,即new一个线程
Thread thread = new Thread();
线程对象在构建的时候需要提供线程所需要的属性,如线程组、优先级等等,下面我们看下如下的源代码:
private void init(ThreadGroup g, Runnable target, String name,
long stackSize) {
// 如果新线程名字为null,抛出异常
if (name == null) {
throw new NullPointerException("name cannot be null");
}
// 获取该线程的父线程
Thread parent = currentThread();
// 获取安全管理组件
SecurityManager security = System.getSecurityManager();
if (g == null) {
// 如果安全组件不为空,就调用SecurityManager的线程组
if (security != null) {
g = security.getThreadGroup();
}
// 如果SecurityManager为空,并且该线程的线程组也为空,则调用其父线程的线程组
if (g == null) {
g = parent.getThreadGroup();
}
}
// 显示通过 允许线程访问线程组
g.checkAccess();
// 检查访问权限
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
// 对线程组中未使用的线程计数器+1
g.addUnstarted();
// 调用父线程的线程组
this.group = g;
// 调用父线程守护线程
this.daemon = parent.isDaemon();
// 调用父线程的优先级
this.priority = parent.getPriority();
// 将字符串转换为新的字符数组
this.name = name.toCharArray();
// 加载父线程的ContextClassLoader
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext = AccessController.getContext();
this.target = target;
setPriority(priority);
// 加载父线程的ThreadLoad
if (parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
// 指定堆栈大小
this.stackSize = stackSize;
// 设置线程ID
tid = nextThreadID();
}
从上面源码我们可以看到,一个新的线程是由其父线程来进行空间分配的,子线程继承父线程的优先级,是否为守护线程、contextClassLoader以及ThreadLocal,最后分配一个唯一的线程ID,新的线程就被创建完毕,在堆内存中等待着被运行。
也可以通过构建Runnable对象来构建线程:
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
...
}
}, "t1");
启动线程
线程创建完毕,我们就可以开始使用该线程了,启动一个线程很简单,直接使用start方法
thread.start();
我们再来对start方法的源码进行分析:
/**
* 该方法不是给main线程和系统线程调用的
* 由虚拟机创建和设置的组线程
*/
public synchronized void start() {
// 0代表线程的状态NEW,初始化状态,如果线程不是初始化状态,则抛出异常
if (threadStatus != 0)
throw new IllegalThreadStateException();
// 通知线程组,该线程即将被启动,添加到组的运行线程列表中,同时未使用线程数计数器-1
group.add(this);
// 启动成功标识符
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
// 如果启动不成功,线程组做相应启动失败处理
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
// 如果什么都不做,则
}
}
}
执行start方法之后,当前线程(父线程)就会同步通知虚拟机,只要线程规划器空闲,就应该立即启动该线程(调用start方法的线程)。
线程中断
线程中断表示一个运行中的线程是否被其他线程进行中断操作,中断在线程中是由一个Boolean值来标识的,我们看下如何判断当前线程中断状态和相关操作:
public static void main(String[] args) throws Exception{
Thread thread1 = new Thread();
thread1.start();
System.out.println("线程:"+thread1.getName()+"是否被中断:"+thread1.isInterrupted());
}
结果:线程:Thread-0 是否被中断:false
thread1.interrupt();
System.out.println("线程:"+thread1.getName()+"是否被中断:"+thread1.isInterrupted());
结果:线程:Thread-0是否被中断:true
public static void main(String[] args) throws Exception{
Thread thread1 = new Thread();
thread1.start();
thread1.interrupt();
System.out.println("线程:"+thread1.getName()+"是否被中断:"+thread1.isInterrupted());
thread1.currentThread().interrupt();
System.out.println("线程:"+thread1.getName()+"是否被中断:"+thread1.isInterrupted());
}
结果:
线程:Thread-0是否被中断:true
线程:Thread-0是否被中断:false
安全的终止线程
上面提到的中断方式是一种比较常见的终止方式,除此之外还有2种方式,一个是使用一个标志位来通知线程终止,还有一个就是使用stop方法(不推荐,下面会详解),下面我们先看看使用标志位和中断如何终止线程:
public class ThreadStartDemo {
// 线程终止标识位
static volatile Boolean flag = false;
static int time1 = 0;
static int time2 = 0;
public static void main(String[] args) throws Exception {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("线程1启动");
while (!flag) {
}
time2 = Integer.parseInt(DateUtil.getNowTimestamp());
int i = time2 - time1;
System.out.println("线程t1退出,等待时间为" + i + "秒");
}
}, "t1");
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
time1 = Integer.parseInt(DateUtil.getNowTimestamp());
System.out.println("线程2启动,5秒后修改flag的值");
}
}, "t2");
t1.start();
t2.start();
t2.sleep(5000);
flag = true;
}
}
运行结果如下:
5秒过后:注意看,线程以及终止运行
使用中断机制终止线程
public class ThreadExitDemo extends Thread {
public void run() {
System.out.println("线程运行中!!!");
System.out.println("请输入任意键盘值来发出中断信号");
}
public static void main(String[] args) throws Exception {
Thread thread = new ThreadExitDemo();
thread.start();
System.in.read();
thread.interrupt();
thread.join();
System.out.println("线程已经退出!!!");
}
}
运行结果如下:
线程中几个废弃的方法
在线程运行过程中还有三个被抛弃的方法,分别是suspend()、resume()、stop()方法,分别代表暂停、恢复和停止的意思,那么为什么这3个方法被废弃了呢?
resume和suspend是成对出现的,既然suspend被抛弃了,当然好基友resume也就没有用武之地了,也是不被推荐使用的方法;
以上就是今天所讲的线程启动和终止的相关内容,希望通过这篇文章,你能对如何正确的启动和关闭一个线程有所掌握,感谢您的阅读!!!
相关文章:
原文始发于微信公众号(Justin的后端书架):