1. 构造线程
运行线程之前需要先构造线程对象,构造线程对象时需指定线程所需要的属性,比如:所属线程组、线程优先级、是否为daemon线程等信息。
java.lang.Thread中对线程初始化的方法如下:
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");
}
//线程名称
this.name = name;
//当前线程就是该线程的父线程
Thread parent = currentThread();
SecurityManager security = System.getSecurityManager();
if (g == null) {
/* Determine if it's an applet or not */
/* If there is a security manager, ask the security manager
what to do. */
if (security != null) {
g = security.getThreadGroup();
}
/* If the security doesn't have a strong opinion of the matter
use the parent thread group. */
if (g == null) {
g = parent.getThreadGroup();
}
}
/* checkAccess regardless of whether or not threadgroup is
explicitly passed in. */
g.checkAccess();
/*
* 是否有访问权限
*/
if (security != null) {
if (isCCLOverridden(getClass())) {
security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);
}
}
g.addUnstarted();
//线程组
this.group = g;
//使用父线程的daemon、priority属性
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* 分配一个线程ID */
tid = nextThreadID();
}
一个 新 构造的 线程对象 是由其 parent 线程来进行空间 分配 的, 而child线程继承了 parent 是否为 Daemon、优先级和加载资源的 contextClassLoader 以及可继承的 ThreadLocal, 同时还会分配一个唯一的ID 来标识这个child线程。至此,一个能够运行的线程对象就初始化好了, 在堆内存中等待着运行。
2. 实现多线程
实现多线程编程的方式主要有两种:
一种继承Thread类,
public class MyThread extends Thread {
@Override
public void run() {
super.run();
System.out.println("我是自定义线程MyThread");
}
}
一种实现Runnable接口。
public class MyRunnable implements Runnable {
@Override
public void run() {
System.out.println("我是Runnable");
}
}
使用继承Thread类最大的局限性是不支持多继承,由于java单继承的特性,为了突破单继承的限制,于是就有了另一个实现方式,就是实现Runnable接口。使用这两种方式创建的线程工作性质是一样的,没有本质的区别。
3. 启动线程
线程对象初始化完成后,调用线程的start()方法就可以启动线程了,start()方法是告诉Java虚拟机,如果线程规划期空闲,应该立即启动调用了start()方法的线程。
同一个线程不能多次 调用 start() 方法, 否则会出现异常 Exception in thread" main" java. lang. IllegalThreadStateException。
线程的start()方法,会新启动一个线程,而线程run()方法则是同步等待当前线程调用。
4.判断线程是否停止状态
中断可以理解为线程的一个标识位属性,它表示一个运行中的线程是否被其他线程进行了中断操作。
1) this.interrupted(): 判断当前线程是否已经中断
2) this.isInterrupted(): 判断线程是否已经中断
那么这两种方法的区别是什么呢? 先来看看 this.interrupted()方法: 判断当前线程是否已经中断,当前线程是指运行 this. interrupted()方法 的 线程。
-
/** * Tests whether the current thread has been interrupted. The * * <i>interrupted status</i> of the thread is cleared by this method. In * other words, if this method were to be called twice in succession, the * second call would return false (unless the current thread were * interrupted again, after the first call had cleared its interrupted * status and before the second call had examined it). * * <p>A thread interruption ignored because a thread was not alive * at the time of the interrupt will be reflected by this method * returning false. * * @return <code>true</code> if the current thread has been interrupted; * <code>false</code> otherwise. * @see #isInterrupted() * @revised 6.0 */ // 验证当前线程是否被中断过。对当前线程的中断标识位进行复位。换句话说,如果连续两次调用该方法,第二次调用时会返回false(除非是调用第一次后,调用第二次之前的时间空挡内又被中断过,会返回true) public static boolean interrupted() { return currentThread().isInterrupted(true); }
public static void main(String[] args) { Thread.currentThread().interrupt(); String threadName = Thread.currentThread().getName(); System.out.println(threadName + " 当前线程是否已停止:=" + Thread.interrupted()); System.out.println(threadName + " 当前线程是否已停止:=" + Thread.interrupted()); } main 当前线程是否已停止:=true main 当前线程是否已停止:=false
从执行结果可知 interrupt()方法确实停止了线程,但是第二个判断结果为什么是false呢,通过查看官方文档当解释得知。interrupted() 具有清除状态的功能,所以第二个判断结果为false
public static void main(String[] args) {
Thread.currentThread().interrupt();
String threadName = Thread.currentThread().getName();
System.out.println(threadName + " 当前线程是否已停止:=" + Thread.currentThread().isInterrupted());
System.out.println(threadName + " 当前线程是否已停止:=" + Thread.currentThread().isInterrupted());
}
main 当前线程是否已停止:=true
main 当前线程是否已停止:=true
输出的结果都为true,isInterrupted()并未清除状态标志,最后我们得出如下结论:
1) this.interrupted(): 判断线程终止的状态, 执行后具有将状态标志置为false的功能
2) this.isInterrupted():判断线程终止的状态,不具有清除状态标志的功能
5. 线程终止
java中有3种方式可以终止正在运行的线程
①线程正常退出,即run()方法执行完毕了
②使用Thread类中的stop()方法强行终止线程。但stop()方法已经过期了,不推荐使用。
过期的 suspend()、 resume() 和 stop()方法终止线程:suspend()在调用后线程不会释放已经占有的资源,二是占有着资源进入睡眠状态,这样容易引发死锁问题。同样的stop()方法在终止一个线程时不会保证线程的资源正常释放,通常线程没有机会去释放资源,因此会导致程序工作状态的不确定性。正是因为 suspend()、 resume() 和 stop() 方法 带来的副作用, 这些方法才被标志为不建议使用的过期方法, 而暂停和恢复可以使用 等待/ 通知机制 来替代。
③使用中断机制
try…catch与this.interrupt方法终止线程
public class MyThread extends Thread {
@Override
public void run() {
super.run();
for (int i = 0; i < 5000; i++) {
if (interrupted()) {
System.out.println(Thread.currentThread().getName() + " 我被停止了,退出循环");
break;
}
System.out.println(" i=" + (i + 1));
}
}
}
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.setName("Thread-interrupt-0");
thread.start();
try {
Thread.sleep(1);
thread.interrupt();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(" 结束!");
}
输出结果:
.....
i=32
i=33
i=34
i=35
i=36
i=37
i=38
i=39
i=40
i=41
结束!
Thread-interrupt-0 我被停止了,退出循环
如何安全的终止线程:
1).中断操作方式-interrupt()
2).自定义标识位
public class Shutdown {
public static void main(String[] args) throws Exception {
Runner one = new Runner();
Thread countThread = new Thread(one, "CountThread");
countThread.start();
// 睡眠1秒,main线程对CountThread进行中断,使CountThread能够感知中断而结束
TimeUnit.SECONDS.sleep(1);
countThread.interrupt();
Runner two = new Runner();
countThread = new Thread(two, "CountThread");
countThread.start();
// 睡眠1秒,main线程对Runner two进行取消,使CountThread能够感知on为false而结束
TimeUnit.SECONDS.sleep(1);
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;
}
}
}
示例在执行过程中,main线程通过中断操作(interrupt()方法)和cancel()方法均可使CountThread得以终止。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源,而不是武断地将线程停止,因此这种终止线程的做法显得更加安全和优雅。
原文:https://blog.csdn.net/u010647035/article/details/82317134