-
线程创建
-
了解
- 继承Thread类,覆盖run()方法;(创建线程方法1)
- 实现Runnable接口,并传递该实现类对象作为Thread对象的构造函数参数(创建线程方法2)
- 每个java程序至少存在一个线程(主线程);
- 只有调用线程对象的start()方法才真正启动了线程;
- 当所有非后台线程执行结束(或者某个线程调用System.exit())时,该程序执行完毕。
-
线程中断
-
了解
- java提供中断机制来通知线程表明我们想结束执行(线程设置中断标识),但需要线程检查是否被中断,线程也可以决定对中断请求的处理方式,即可忽略中断请求继续执行。(好比女友让你别老玩游戏,但是是否继续玩游戏,玩得程度完全取决于你)
- Thread相关接口
void
interrupt
()设置线程的中断状态
boolean
isInterrupted
()检测是否中断状态
static boolean
interrupted
()检测是否中断状态,并清除中断状态
public static void main(String[] args) { Thread t = new Thread(new Runnable() { @Override public void run() { while (true) { System.out.println("游戏中...请勿扰"); if (Thread.currentThread().isInterrupted()) { System.out.println("女友:别玩游戏了"); // ------------------1.继续玩 ------------------ // System.out.println("我:没有理会继续玩"); // ------------------1.继续玩 ------------------ // ------------------2.好吧,马上关机 ------------------ // System.out.println("我:好吧,马上关机!睡觉!"); // break; // ------------------2.好吧,马上关机 ------------------ // ------------------3.在玩一会儿 ------------------ System.out.println("我:等等,再玩一会儿就赢了!"); try { TimeUnit.SECONDS.sleep(1); System.out.println("Game Over!"); } catch (InterruptedException e) { e.printStackTrace(); } break; // ------------------3.再玩一会儿 ------------------ // ------------------4.跪键盘中 ------------------ // System.out.println("(我)跪键盘中..."); // break; // ------------------4.跪键盘中 ------------------ } } } }); // 开始玩游戏 t.start(); try { // 刚开始玩 TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // 女友走过来,让我别玩了,并且罚我跪键盘(实质:设置线程的中断标识) t.interrupt(); }
上面代码,线程中断后可以有多种响应。检测到中断是不理会继续执行任务?马上退出当前执行任务?sleep后退出?又或者是sleep后继续执行?这就需要与中断请求方协商好,视实际情况而定。当执行第3种相应时,会抛出异常:“java.lang.InterruptedException: sleep interrupted ”。因为Thread.sleep、Thread.join、Object.wait、LockSupport.park等阻塞接口在检查到线程的中断状态时,会抛出InterruptedException,同时会清除线程的中断状态
-
线程Join
-
了解
- thread的join(),可暂停执行调用线程,直到被调用线程执行完毕。
上面代码,主线程调用了t1.join()、t2.join(),那么主线程会在等待t1、t2线程均执行完毕才会执行System.out.println("主线程3");其中注释的t1.join(1000)\t1.join(1000,10000)会在等待若干毫秒\毫秒+纳秒后继续执行下面代码。public static void main(String[] args) { Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("---我是分支线程1beg---"); try { TimeUnit.SECONDS.sleep(3); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("---我是分支线程1end---"); } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println("---我是分支线程2beg---"); try { TimeUnit.SECONDS.sleep(2); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("---我是分支线程2end---"); } }); t1.start(); t2.start(); try { System.out.println("主线程1"); t1.join(); // t1.join(1000); // t1.join(1000,10000); System.out.println("主线程2"); t2.join(); System.out.println("主线程3"); } catch (InterruptedException e) { e.printStackTrace(); } }
-
线程组创建
-
了解
- 每一个线程归属于某个线程组管理,main()中创建的线程归属线程组main,也可以创建线程时可以明确指定线程组
- 可中断线程组下及其子线程组下所有线程,即该组下的子孙线程都调用interrupt()
public static void main(String[] args) { // 每一个线程归属于某个线程组管理,main()中创建的线程归属线程组main Thread oneT = new Thread(new SleepTask(), "testMain"); ThreadGroup mainGroup = oneT.getThreadGroup(); System.out.println("main函数创建的线程归属线程组:" + mainGroup.getName()); // 创建一线程组 ThreadGroup group = new ThreadGroup("testGroup"); int size = 5; for (int i = 0; i < size; i++) { // 创建线程时明确指定的线程组 Thread t = new Thread(group, new SleepTask()); t.start(); } int num = group.activeCount(); Thread[] threads = new Thread[num]; // 获取线程组testGroup下活跃的线程 group.enumerate(threads); // 统一归属线程组:testGroup for (Thread t : threads) { System.out.println(t.getName() + "归属线程组:" + t.getThreadGroup().getName()); } // 中断线程组testGroup下及其子线程组下所有线程,即该组下的子孙线程都调用interrupt() group.interrupt(); } static class SleepTask implements Runnable { @Override public void run() { try { System.out.println(Thread.currentThread().getName() + "-beg"); TimeUnit.SECONDS.sleep(new Random().nextInt(8)); System.out.println(Thread.currentThread().getName() + "-end"); } catch (InterruptedException e) { System.out.println(Thread.currentThread().getName() + "-" + e.getMessage()); } } }
-
线程(组)异常处理
-
了解
- run() 方法不接受 throws 子句
- 当运行时异常被抛出,默认的行为是在控制台写下stack trace并结束任务(可自定义设置未捕获到异常处理程序);
- 当非运行时异常被抛出,必须捕捉并处理。
- 未捕获到异常处理程序顺序
- 线程对象通过setUncaughtExceptionHandler()设置的未捕获异常处理程序(不存在查找2)
- 线程对象所归属的线程组对象(重写ThreadGroup uncaughtException())设置的未捕获异常处理程序(不存在查找3)
- 通过Thread.setDefaultUncaughtExceptionHandler()设置的默认未捕获异常处理程序(不存在查找4)
- 未捕获异常的stack trace写入操控台并结束任务
- Thread相关接口
设置该线程由于未捕获到异常而突然终止时调用的处理程序
static void
setDefaultUncaughtExceptionHandler
设置当线程由于未捕获到异常而突然终止,并且没有为该线程定义其他处理程序时所调用的默认处理程序。
public class ThreadUncheckedException { public static void main(String[] args) { //设置默认未捕获异常处理程序 Thread.setDefaultUncaughtExceptionHandler(new CustomDefaultUncaughtHandler()); MyThreadGroup threadGroup = new MyThreadGroup("myGroup"); Thread t = new Thread(threadGroup,new SleepTask()); //设置线程对象的未捕获异常处理程序 t.setUncaughtExceptionHandler(new CustomThreadUncaughtHandler()); t.start(); } private static class MyThreadGroup extends ThreadGroup { public MyThreadGroup(String name) { super(name); } @Override //设置线程组对象的未捕获异常处理程序 public void uncaughtException(Thread t, Throwable e) { // e.printStackTrace(); System.out.printf("[线程组]异常处理程序:已处理线程%s异常 [%s:%s]", t.getName(), e.getClass().getName(), e.getMessage()); // 一子错满盘皆输 interrupt(); } } private static class SleepTask implements Runnable { @Override public void run() { // 当非运行时异常被抛出,必须捕捉并处理 try { TimeUnit.SECONDS.sleep(1); } catch (InterruptedException e) { e.printStackTrace(); } // throws NumberFormatException(运行时异常) Integer.parseInt("test"); System.out.println("看到此输出,表明木有异常!"); } } private static class CustomDefaultUncaughtHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { // e.printStackTrace(); System.out.printf("[默认线程]异常处理程序:已处理线程%s异常 [%s:%s]", t.getName(), e.getClass().getName(), e.getMessage()); } } private static class CustomThreadUncaughtHandler implements UncaughtExceptionHandler { @Override public void uncaughtException(Thread t, Throwable e) { // e.printStackTrace(); System.out.printf("[线程]异常处理程序:已处理线程%s异常 [%s:%s]", t.getName(), e.getClass().getName(), e.getMessage()); } } }
可以通过注释相关的未捕获异常处理程序来验证异常处理程序顺序。