五:多线程深入话题
1、优雅的停止线程
在多线程操作之中,启动多线程使用的是Thread类中的start()方法,而要对多线程进行停止处理,在原来的Thread类中提供有stop()方法,但此方法在JDK1.2版本之后就已经过期了,不可使用。而除了stop()方法之外,以下几个方法也被禁用了:
方法 | 方法定义 | 废除原因 |
停止多线程 | public final void stop() | 这些方法的使用可能导致线程的死锁 |
销毁多线程 | public void destroy() | |
挂起线程(暂停执行) | public final void suspend() | |
恢复执行(恢复挂起的线程执行) | public final void resume() |
在不使用上面方法的情况下,我们可以使用另外一种柔和的方式来让线程停止。
范例:线程的停止
package cn.demos;public class ThreadDemo {public static boolean flag = true;public static void main(String[] args) throws Exception {new Thread(() -> {long num = 0;while (flag) {try {//运行一次休眠50s,总共运行4次Thread.sleep(50);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在运行,num=" + num++);}}, "执行线程").start();// 让线程运行200msThread.sleep(200);flag = false;// 停止线程}}
从上面程序可以分析出,整个程序的运行与flag的值有关,线程总共运行200ms,而每次线程运行一次则休眠50ms,其执行结果为执行4次,利用休眠时间设置线程停止运行。
而万一现在有其他的线程去控制这个flag的内容,那么线程的停止会受到影响,程序需要通过对flag值的判断
来执行。
2、后台守护线程
在多线程程序里面,可以进行守护线程的定义。假如现在程序里主线程的程序或者其他线程还在执行,那么守护线程将一直存在,并且运行在后台状态。
而在Thread类中提供有如下的守护线程的操作方法:
方法定义 | |
设置为守护线程 | public final void setDaemon(boolean on) |
判断是否为守护线程 | public final boolean isDaemon() |
范例:使用守护线程
package cn.demos;public class ThreadDemo {public static boolean flag = true;public static void main(String[] args) throws Exception {// 用户线程Thread useThread = new Thread(() -> {for (int x = 0; x < Integer.MAX_VALUE; x++) {// 添加延迟try {Thread.sleep(100);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "用户线程正在执行,x=" + x);}}, "用户线程");// 完成核心任务// 守护线程Thread daemoThread = new Thread(() -> {for (int x = 0; x < Integer.MAX_VALUE; x++) {try {Thread.sleep(100);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "守护线程正在执行,x=" + x);}}, "守护线程");// 完成核心任务// 设置守护线程daemoThread.setDaemon(true);// 先启动用户线程,然后启动守护线程useThread.start();daemoThread.start();}}
从程序执行结果中可以得知,所有的守护线程都是围绕在用户线程的周围,所以,如果程序执行完毕那么守护线程也会消失。而在整个JVM最大的守护线程就是GC线程。
程序执行过程中GC线程会一直存在,如果程序执行完毕,那么GC线程也将消失。
3、volatile关键字
在程序定义中,volatile关键字主要是在属性定义上使用的,即表示此属性为直接数据操作,而不进行副本的拷贝处理。
在正常进行变量处理的时候往往会经历如下的步骤:
1)获取变量原有的数据内容副本;
2)利用副本为变量进行数学计算;
3)将计算后的变量,保存到原始空间之中;
若,在属性上追加volatile关键字,表示的就是不使用副本,而是直接操作原始变量,相当于节约了:拷贝副本、重新保存的步骤。
面试题:请解释volatile与synchronized的区别:
区别 | volatile | synchronized |
1 | 主要在属性上使用 | 在代码块与方法上使用的 |
2 | 无法描述同步的处理,是一种直接内存的处理,避免了副本的操作 | 是实现同步操作的 |
五:多线程深入话题
1、优雅的停止线程
在多线程操作之中,启动多线程使用的是Thread类中的start()方法,而要对多线程进行停止处理,在原来的Thread类中提供有stop()方法,但此方法在JDK1.2版本之后就已经过期了,不可使用。而除了stop()方法之外,以下几个方法也被禁用了:
方法 | 方法定义 | 废除原因 |
停止多线程 | public final void stop() | 这些方法的使用可能导致线程的死锁 |
销毁多线程 | public void destroy() | |
挂起线程(暂停执行) | public final void suspend() | |
恢复执行(恢复挂起的线程执行) | public final void resume() |
在不使用上面方法的情况下,我们可以使用另外一种柔和的方式来让线程停止。
范例:线程的停止
package cn.demos;public class ThreadDemo {public static boolean flag = true;public static void main(String[] args) throws Exception {new Thread(() -> {long num = 0;while (flag) {try {//运行一次休眠50s,总共运行4次Thread.sleep(50);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "正在运行,num=" + num++);}}, "执行线程").start();// 让线程运行200msThread.sleep(200);flag = false;// 停止线程}}
从上面程序可以分析出,整个程序的运行与flag的值有关,线程总共运行200ms,而每次线程运行一次则休眠50ms,其执行结果为执行4次,利用休眠时间设置线程停止运行。
而万一现在有其他的线程去控制这个flag的内容,那么线程的停止会受到影响,程序需要通过对flag值的判断
来执行。
2、后台守护线程
在多线程程序里面,可以进行守护线程的定义。假如现在程序里主线程的程序或者其他线程还在执行,那么守护线程将一直存在,并且运行在后台状态。
而在Thread类中提供有如下的守护线程的操作方法:
方法定义 | |
设置为守护线程 | public final void setDaemon(boolean on) |
判断是否为守护线程 | public final boolean isDaemon() |
范例:使用守护线程
package cn.demos;public class ThreadDemo {public static boolean flag = true;public static void main(String[] args) throws Exception {// 用户线程Thread useThread = new Thread(() -> {for (int x = 0; x < Integer.MAX_VALUE; x++) {// 添加延迟try {Thread.sleep(100);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "用户线程正在执行,x=" + x);}}, "用户线程");// 完成核心任务// 守护线程Thread daemoThread = new Thread(() -> {for (int x = 0; x < Integer.MAX_VALUE; x++) {try {Thread.sleep(100);} catch (Exception e) {// TODO Auto-generated catch blocke.printStackTrace();}System.out.println(Thread.currentThread().getName() + "守护线程正在执行,x=" + x);}}, "守护线程");// 完成核心任务// 设置守护线程daemoThread.setDaemon(true);// 先启动用户线程,然后启动守护线程useThread.start();daemoThread.start();}}
从程序执行结果中可以得知,所有的守护线程都是围绕在用户线程的周围,所以,如果程序执行完毕那么守护线程也会消失。而在整个JVM最大的守护线程就是GC线程。
程序执行过程中GC线程会一直存在,如果程序执行完毕,那么GC线程也将消失。
3、volatile关键字
在程序定义中,volatile关键字主要是在属性定义上使用的,即表示此属性为直接数据操作,而不进行副本的拷贝处理。
在正常进行变量处理的时候往往会经历如下的步骤:
1)获取变量原有的数据内容副本;
2)利用副本为变量进行数学计算;
3)将计算后的变量,保存到原始空间之中;
若,在属性上追加volatile关键字,表示的就是不使用副本,而是直接操作原始变量,相当于节约了:拷贝副本、重新保存的步骤。
面试题:请解释volatile与synchronized的区别:
区别 | volatile | synchronized |
1 | 主要在属性上使用 | 在代码块与方法上使用的 |
2 | 无法描述同步的处理,是一种直接内存的处理,避免了副本的操作 | 是实现同步操作的 |