无障碍网站建设的摘要,昆明企业网站建设一条龙,珠海企业医疗网站建设,论文答辩免费ppt模板下载文章目录 4.2 启动和终止线程4.2.1 构造线程4.2.2 启动线程4.2.3 理解中断4.2.4 过期的suspend()、resume()和stop()4.2.5 安全地终止线程 4.2 启动和终止线程
在前面章节的示例中通过调用线程的start()方法进行启动#xff0c;随着run()方法的执行完毕#xff0c;线程也随之… 文章目录 4.2 启动和终止线程4.2.1 构造线程4.2.2 启动线程4.2.3 理解中断4.2.4 过期的suspend()、resume()和stop()4.2.5 安全地终止线程 4.2 启动和终止线程
在前面章节的示例中通过调用线程的start()方法进行启动随着run()方法的执行完毕线程也随之终止大家对此一定不会陌生下面将详细介绍线程的启动和终止。
4.2.1 构造线程
在运行线程之前首先要构造一个线程对象线程对象在构造的时候需要提供线程所需要的属性如线程所属的线程组、线程优先级、是否是Daemon线程等信息。以下代码摘自java.lang.Thread 中对线程进行初始化的部分。 /*** Initializes a Thread.** param g the Thread group* param target the object whose run() method gets called* param name the name of the new Thread* param stackSize the desired stack size for the new thread, or* zero to indicate that this parameter is to be ignored.* param acc the AccessControlContext to inherit, or* AccessController.getContext() if null* param inheritThreadLocals if {code true}, inherit initial values for* inheritable thread-locals from the constructing 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 its an applet or not *//* If there is a security manager, ask the security managerwhat to do. */if (security ! null) {g security.getThreadGroup();}/* If the security doesnt have a strong opinion of the matteruse the parent thread group. */if (g null) {g parent.getThreadGroup();}}/* checkAccess regardless of whether or not threadgroup isexplicitly passed in. */g.checkAccess();/** Do we have the required permissions?*/if (security ! null) {if (isCCLOverridden(getClass())) {security.checkPermission(SUBCLASS_IMPLEMENTATION_PERMISSION);}}g.addUnstarted();this.group g;this.daemon parent.isDaemon();this.priority parent.getPriority();if (security null || isCCLOverridden(parent.getClass()))this.contextClassLoader parent.getContextClassLoader();elsethis.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;/* Set thread ID */tid nextThreadID();}在上述过程中一个新构造的线程对象是由其parent线程来进行空间分配的而child线程继承丁parent是否为Daemon、优先级和加载资源的contextClassLoader以及可继承的ThreadLocal、同时还会分配一个唯一的ID来标识这个 child线程。至此一个能够运行的线程对象就初始化好了在堆内存中等待着运行。
4.2.2 启动线程
线程对象在初始化完成之后调用start()方法就可以启动这个线程。线程start()方法含义是当前线程(即parent线程)同步告知Java虚拟机只要线程规划器空闲应立即动调用 start()方法的线程。 注意启动一个线程前最好为这个线程设置线程名称因为这样在使用jstack分析程序者进行问题排查时就会给开发人员提供一些提示自定义的线程最好能够起个名字。 4.2.3 理解中断
中断可以理解为线程的一个标识位属性它表示一个运行中的线程是否被其他线程进行了中断操作。中断好比其他线程对该线程打了个招呼其他线程通过调用该线程的interrupt()方法对其进行中断操作。
线程通过检查自身是否被中断来进行响应线程通过方法isInterrupted()来进行判断否被中断也可以调用静态方法Thread.interupted()对当前线程的中断标识位进行复位。如果该线程已经处于终结状态即使该线程被中断过在调用该线程对象的isInterrupted()时依旧会返回 false。
从Java的API中可以看到许多声明抛出InterruptedException的方法(例如Thread.sleep(long millis)方法)这些方法InterruptedException之前Java虚拟机会先将该线程的中断标识位清除然后抛出InterruptedException此时调用isInterrupted()方法将会返回false。
在下面代码中首先创建了两个线程SleepThread和BusyThread前者不停地睡眠后者一直运行然后对这两个线程分别进行中断操作观察二者的中断标识位。 import java.util.concurrent.TimeUnit;public class Interrupted {public static void main(String[] args) throws InterruptedException {// sleepThread不停地尝试睡眠Thread sleepThread new Thread(new SleepRunner(), SleepThread);sleepThread.setDaemon(true);// busyThread不停地运行Thread busyThread new Thread(new BusyRunner(), BusyThread);busyThread.setDaemon(true);sleepThread.start();busyThread.start();// 休眠5秒让sleepThread和busyThread充分运行TimeUnit.SECONDS.sleep(5);sleepThread.interrupt();busyThread.interrupt();System.out.println(SleepThread interrupted is sleepThread.isInterrupted());System.out.println(BusyThread interrupted is busyThread.isInterrupted());// 防止sleepThread和busyThread立刻退出SleepUtils.second(2);}static class SleepRunner implements Runnable {Overridepublic void run() {while (true) {SleepUtils.second(10);}}}static class BusyRunner implements Runnable {Overridepublic void run() {while (true) {}}}
}import java.util.concurrent.TimeUnit;public class SleepUtils {public static final void second(long seconds) {try {TimeUnit.SECONDS.sleep(seconds);} catch (InterruptedException e) {e.printStackTrace();}}
} 打印 SleepThread interrupted is false BusyThread interrupted is true java.lang.InterruptedException: sleep interrupted at java.lang.Thread.sleep(Native Method) at java.lang.Thread.sleep(Thread.java:340) at java.util.concurrent.TimeUnit.sleep(TimeUnit.java:386) at com.xin.demo.threaddemo.bookdemo.SleepUtils.second(SleepUtils.java:8) at com.xin.demo.threaddemo.bookdemo.Interrupted$SleepRunner.run(Interrupted.java:32) at java.lang.Thread.run(Thread.java:748) 从结果可以看出抛出InterruptedException的线程SleepThread其中断标识位被清除了而一直忙碌运作的线程BusyThread中断标识位没有被清除。
4.2.4 过期的suspend()、resume()和stop()
大家对于CD机肯定不会陌生如果把它播放音乐比作一个线程的运作那么对音乐播放做出的暂停、恢复和停止操作对应在线程Thread的API就是suspend()、resume()和stop()。
在下面代码中创建了一个线程PrintThread它以1秒的频率进行打印而主线程对其进行暂停、恢复和停止操作。 import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.concurrent.TimeUnit;public class Deprecated {public static void main(String[] args) throws Exception {DateFormat format new SimpleDateFormat(HH:mm:ss);Thread printThread new Thread(new Runner(), printThread);printThread.setDaemon(true);printThread.start();TimeUnit.SECONDS.sleep(3);// 将PrintThread进行暂停输出内容工作停止printThread.suspend();System.out.println(main suspend PrintThread at format.format(new Date()));TimeUnit.SECONDS.sleep(3);// 将PrintThread进行恢复输出内容继续printThread.resume();System.out.println(main resume PrintThread at format.format(new Date()));TimeUnit.SECONDS.sleep(3);//将PrintThread进行终止输出内容停止printThread.stop();System.out.println(main stop PrintThread at format.format(new Date()));TimeUnit.SECONDS.sleep(3);}static class Runner implements Runnable {Overridepublic void run() {DateFormat format new SimpleDateFormat(HH:mm:ss);while (true) {System.out.println(Thread.currentThread().getName() Run at format.format(new Date()));SleepUtils.second(1);}}}
} 打印 printThread Run at 20:46:36 printThread Run at 20:46:37 printThread Run at 20:46:38 main suspend PrintThread at 20:46:39 main resume PrintThread at 20:46:42 printThread Run at 20:46:42 printThread Run at 20:46:43 printThread Run at 20:46:44 main stop PrintThread at 20:46:45 在执行过程中PrintThread运行了3秒随后被暂停3秒后恢复最后经过3秒被终止。通过示例的输出可以看到suspend()、resume()和stop()方法完成了线程的暂停、恢复和终止工作而且非常“人性化”。
不建议使用的原因主要有以suspend()方法为例在调用后线程不会释放已经占有的资源比如锁而是占有着资源进入睡眠状态这样容易引发死锁问题。同样stop()方法在终结一个线程时不会保证线程的资源正常释放。通常是没有给予线程完成资源释放工作的机会因此会导致程序可能工作在不确定状态下。 注意正因为 suspend()、resume()和stop()方法带来的副作用这些方法才被标注为不建议使用的过期方法而暂停和恢复操作可以用后面提到的等待/通知机制来替代。 4.2.5 安全地终止线程
在4.2.3节中提到的中断状态是线程的一个标识位而中断操作是一种简便的线程间交互方式而这种交互方式最适合用来取消或停止任务。除了中断以外还可以利用一个boolean 变量来控制是否需要停止任务并终止该线程。
在下面代码中创建了一个线程CountThread它不断地进行变量累加,面主线程尝试对其进行中断操作和停止操作。
import java.util.concurrent.TimeUnit;public class ShutDown {public static void main(String[] args) throws InterruptedException {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;Overridepublic void run() {while (on !Thread.currentThread().isInterrupted()) {i;}System.out.println(Count i i);}public void cancel() {on false;}}
} 打印 Count i 198875747 Count i 196373773 示例在执行过程中main线程通过中断操作和cancel()方法均可使CountThread得以终止。这种通过标识位或者中断操作的方式能够使线程在终止时有机会去清理资源而不是武断地将线程停止因此这种终止线程的做法显得更加安全和优雅。