本篇文章思维导图如下:

目录
Thread() -- thread空构造方法 --创建Thread对象
Thread(String name) -- 为线程起名,作为与其他线程区分,默认就是Thread-0,Thread-1.......
Thread(Runnable target) 使用Runnable对象来来创建Thread对象
Thread(Runnable target,String name),使用Runnable对象创建Thread对象并为线程起名字.
- public static class MyRunnable1 implements Runnable{
- @Override
- public void run() {
- System.out.println("我是Thread");
- }
- }
-
- public static void main(String[] args) {
- //为线程起名
- Thread t1 = new Thread(()->{
- System.out.println("我是Thread!!!");
- },"我是Thread线程");
- Runnable r = new MyRunnable1();
- //使用Runnable对象并为线程起名
- Thread t2 = new Thread(r,"我是Thread线程");
- }
获取线程在JVM中的身份标识 ----->作为身份区分
例如(内核 --pcb标识 , 用户态线程库 标识pthread JVM也有标识)
得到 --在Thread构造方法传入的名字
得到 线程的优先级 ,优先级越高越先被调度
获取pcb的状态
判断是否为守护线程
判断线程是否存活
判断线程是否中断
- public static void main(String[] args) {
- Thread t = new Thread (()->{
- //System.out.println("我是Thread!!!");
- },"我是使用lambda表达式创建的Thread线程");
- System.out.println("创建前的状态:"+t.getState());
- t.start();
- System.out.println("线程名字 : "+t.getName());
- System.out.println("线程身份Id : "+t.getId());
- System.out.println("当前线程状态 : "+t.getState());
- System.out.println("线程是否存活 : "+t.isAlive());
- System.out.println("线程是否为守护线程 : "+t.isDaemon());
- System.out.println("线程是否中断 : "+t.isInterrupted());
- System.out.println("线程的优先级 : "+t.getPriority());
- }

线程分为前台线程和后台线程;
前台线程:前台线程也是用户线程,默认创建的线程都是前台线程,前台线程会阻止进程结束,进程会保证所有前台线程全部执行完成才会结束整个进程
后台线程:后台线程也是守护线程十位用户线程而服务的,后台线程不会组织进程结束,进程退出时不会管后台进程结束.
注意设置后台线程setDaemon(True),判断一个下次线程是否为守护线程利用isDaemon方法,守护线程必须在创建线程之前设置,创建线程之后不能设置
创建线程首先需要创建Thread类的实例 --有7种方法创建Thread类实例,要重写run方法,创建实例后我们要调用start()来真正的创建一个线程.
还要注意start()和run()区别
中断线程有两种方式
- public static boolean flg = false;
-
- public static void main(String[] args) {
-
- Thread t = new Thread(()->{
- while(!flg){
- System.out.println("我是Thread!!!");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
- t.start();
- try {
- Thread.sleep(5000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("我是main");
- flg = true;
- }
这里设置一个标记,因为线程是并发执行的,等待sleep(5000)然后将flg=true,中断线程(将标记设置为true--结果为false)
自定义标记法缺点
- public static boolean flg = false;
-
- public static void main(String[] args) {
-
- Thread t = new Thread(()->{
- while(!flg){
- System.out.println("我是Thread!!!");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- System.out.println("终止Thread");
- }
- });
- t.start();
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- flg = true;
- System.out.println("我是main");
- }
这时当flag置为true时,依然打印"终止thread"
所以这里的自定义标记法中断不及时
因为线程是并发执行的,当把flag标记置为true时,线程还在执行中,无法循环判断,需要等待下一轮判断是才会中断--中断不及时
- public static void main(String[] args) throws InterruptedException {
- Thread t = new Thread(()->{
- //Thread.currentThread().isInterrupted() 默认为false
- while(!Thread.currentThread().isInterrupted()){
- System.out.println("正在运行中...");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- });
-
- t.start();
-
- Thread.sleep(3000);
- t.interrupt();
- System.out.println("线程结束!!!");
- }

为啥抛出异常而线程依然运行呢???
原因是使用interrupt()不是让线程立即中断,而是发出中断信号触发阻塞的方法抛出异常,具体怎么处理还是交给代码编写者处理.
???什么意思
这里只是让阻塞的方法抛出异常也就是只是打印一下调用栈的信息,然后什么都没有做,继续执行循环.
我们这里可以在catch里进行设置
1. break -- 立即退出
2.使用sleep方法 等待一会在退出
3.什么也不做,继续执行循环
- while(!Thread.currentThread().isInterrupted()){
- System.out.println("正在运行中...");
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- //[1]break;//立即退出
- //[2]使用sleep等待一会在退出
- /*try{
- Thread.sleep(1000);
- }catch(InterruptedException e1){
- e1.printStackTrace();
- }
- break;
- */
- //什么也不做继续执行
- }
- }
调用interrupt方法相当于发送一个中断指令,而这里不是立即中断.如果没有在阻塞状态会设置标记位然后中断运行,如果处在阻塞状态会让线程内部产生阻塞的方法抛出异常(例如sleep抛出异常),然后具体怎么处理交给代码编写者处理(如立即退出,等待一会退出,还是什么都不做).
当我们不想让某个线程与主线程并发执行就可以使用join() --使用join方法就会让主线程等待(进行阻塞)其他线程执行完成.
使用join()---等待子线程对象销毁
当前线程没有执行结束 ,main线程进行阻塞等待,一直到线程run方法执行完(线程对象销毁)
当线程执行结束,立即返回,main线程执行任务,然后退出进程
使用join还要抛出异常
比如要对两个线程使用join()方法时顺序无关(也就是先调用t1还是调用t2无关)
原因是 : 线程是随机调度的取决于操作系统的调度,
比如代码 t1.join() t2.join()
两种情况 当t1先结束时: main线程会阻塞等待,等待t1任务执行完,再去执行线程t2,main线程继续阻塞等待t2执行完.
当t2先结束时: main线程会阻塞等待,等待t1任务执行完,在执行t2线程,由于t2线程早就执行结束,main线程不用阻塞等待,直接返回,main线程继续执行
| public void join() | 等待线程结束 |
| public void join(long millis) | 等待线程结束,最多等 millis 毫秒 |
| public void join(long millis, int nanos) | 同理,但可以更高精度 |
直接join是主线程一直等到其他线程结束了,在执行主线程,而这里如果出了bug就容易卡死
所以优先推荐使用等待一段时间join(毫秒数ms),如果时间到了还没有等到线程任务完成,就要采取一些措施
一个静态方法来获取当前线程引用
| public static Thread currentThread(); | 返回当前线程对象的引用 |
对线程进行操作(线程等待,线程中断,获取各种线程的属性),就要获取当前线程引用,通过Thread的静态方法currentThread()方法来获取
| public static void sleep(long millis) throws InterruptedException | 休眠当前线程 millis |
| public static void sleep(long millis, int nanos) throws | 可以更高精度的休眠 |

有就绪队列和阻塞队列
就绪队列:pcb在CPU正在运行或立即开始运行,如果阻塞就要从就绪队列到阻塞队列中
阻塞队列:pcb暂时不在CPU运行,等待休眠结束就从阻塞队列调度回就绪队列
线程有以下状态:
NEW : 新建状态(创建好了Thread实例对象,还没创建线程,安排好工作了,还没有开始工作)
RUNNABLE : 就绪状态(可能是正在CPU上运行,可能是立即开始运行(还没有在CPU运行,处在就绪))
WAITING : 阻塞状态 引调用wait方法而等待
TIME_WAITING : 阻塞状态 : 因调用sleep方法二阻塞等待
BLOCKED : 阻塞状态 : 等待锁
TERMINATED : run任务已经执行完毕,线程已经执行结束销毁,但Thread对象还在.

- public static void main(String[] args) throws InterruptedException {
- Thread t = new Thread(()-> {
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- });
- //线程还未创建处于新建状态
- System.out.println(t.getState());
- t.start();
- //线程正在运行状态处于RUNNABLE状态
- System.out.println(t.getState());
- Thread.sleep(500);
- System.out.println(t.getState());//使用sleep阻塞TIME_WAITING
- t.join();
- System.out.println(t.getState());//线程t1结束,TERMINATED状态
-
- }
