1.线程各属性
属性名称 | 用途 |
编号(ID) | 每个线程都有自己的ID,用于标识不同的线程(ID从1开始) |
名称(Name) | 让用户或程序员在开发调试中更容易区分不同线程,定位问题 |
是否是守护线程(isDaemon) | true(守护线程)/false(非守护线程也称为:“用户线程”) 设置守护线程方法:thread.setDaemon(true) |
优先级(Priority) | 优先级属性目的是告诉调度器用户希望哪些线程多运行,哪些线程少运行(总共10个级别我们创建的线程优先级默认为:5) |
1.1.守护线程
作用:
给用户线程提供服务;
守护线程特性:
1.线程类型默认继承自父线程(即:守护线程创建出来的线程默认就是守护线程);
2.通常守护线程都是由JVM启动的;
3.不影响JVM退出;
1.2.优先级
线程优先级总共有10个级别,我们创建出来的线程优先级默认都是5,程序设计不应依赖于优先级,因为不同操作系统优先级不一样且优先级会被操作系统改变,
2.如何处理线程未捕获异常(重要)
使用“UncaughtExceptionHandler”类来处理未捕获的异常;
2.1.为什么需要UncaughtExceptionHandler?
因为子线程异常时无法将异常传递给主线程去捕捉(即:子线程异常时主线程使用try-cache捕捉不到);
2.2.演示主线程捕捉不到子线程抛出的异常
/**
* 演示主线程捕捉不到子线程异常
*/
public class ThreadException implements Runnable{
@Override
public void run() {
throw new RuntimeException("子线程抛出异常");
}
public static void main(String[] args) {
try {
new Thread(new ThreadException()).start();
}catch (Exception e){
System.out.println("主线程捕捉到子线程抛出的异常:" + e);
}
}
}
2.3.使用"UncaughtExceptionHandler"实现全局处理子线程异常
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 定义未捕获线程异常处理器
*/
public class MyUncaughtExceptionHandler implements Thread.UncaughtExceptionHandler {
@Override
public void uncaughtException(Thread t, Throwable e) {
// 日志记录异常原因
Logger logger = Logger.getAnonymousLogger();
logger.log(Level.INFO,t.getName() + "异常,被终止啦..");
logger.log(Level.WARNING,t.getName() + "异常原因:" + e);
}
}
测试
public class ThreadExcptionMakeOwnUncaughtExceptionHandler implements Runnable{
@Override
public void run() {
throw new RuntimeException("模拟子线程异常");
}
public static void main(String[] args) {
// 设置“未捕获线程异常”处理类
Thread.setDefaultUncaughtExceptionHandler(new MyUncaughtExceptionHandler());
new Thread(new ThreadExcptionMakeOwnUncaughtExceptionHandler(),"线程一").start();
}
}
3.线程安全
3.1.什么是线程安全?
业务中多线程同时访问一个对象或方法时我们不需要做额外的处理(像单线程编程一样)程序可以正常运行并能获取到正确的结果(不会因为多线程而出错)我们就认为上面的对象或方法是线程安全的;
3.2.线程安全问题三大类?
1.运行结果错误;
2.活跃性问题(死锁、活锁、饥饿);
3.对象发布和初始化时安全问题;
4.面试题
4.1.守护线程和普通线程区别?
都是线程整体无区别,唯一区别在于是否能影响到JVM退出,若是用户线程其会响应到JVM的退出(所有用户线程执行完后JVM才会退出),守护线程不会影响到JVM的退出,通常守护线程是由JVM创建的用于执行一些后台任务如“gc垃圾回收”,用户线程是由用户来创建的,守护线程用于给我们创建的用户线程提供服务;
4.2.我们是否需要给线程设置为守护线程?
# 线程设置为守护线程方法
Thread thread = new Thread(...);
thread.setDaemon(true);
我们不应该把自己的线程设置为守护线程,假如我们的线程设置为“守护线程”当JVM发现没有用户线程时JVM会自动退出,这时我们代码就会被强制关闭,易导致数据不一致问题;
4.3.run()方法是否可以抛出异常?
run()方法在设计时就没有抛出异常,所以run()方法不能向外抛出异常,我们可以在run()方法内部使用try-cache去捕获处理异常,也可以设置一个全局处理“线程未捕获异常(UncaughtExceptionHandler)”的处理器去处理未捕获线程异常;
4.4.线程中如何处理未捕获的异常?
使用UncaughtExceptionHandler全局处理未捕获到的线程异常;
4.5.多线程带来性能问题的原因?
1.上下文切换;
2.内存同步(单个线程所独占的“本地内存”与所有线程所共享的“主内存”之间的同步(本地内存与主内存同步));
4.6.什么是上下文切换?
正在运行的线程被其它线程抢占了CPU资源,原本正在运行的线程需要把当前上下文信息(CPU寄存器值,程序计数器等数据)保存到内存中以便下次再次被CPU调度到继续执行,再执行另一个线程的过程叫作上下文切换;
4.7.何时会进行上下文切换?
1.线程执行时间超过CPU为其分配的时间片;
2.线程执行了sleep()/wait()方法;
3.线程IO过程引起的阻塞;
4.8.有哪些线程不安全的情况?
1.运行结果错误
2.活跃性问题(死锁、活锁、饥饿)
3.对象发布和初始化时安全问题
4.9.哪些情况需额外注意线程安全?
1.访问共享变量或资源会有并发安全问题比如对象属性、静态变量、共享缓存;
2.所有依赖时序的操作;(如:原本应该是先写后读,而多线程下可能是先读后写);
3.不同数据之间存在捆绑关系(如:多个线程间操作应同时成功或同时失败);