JavaEE初阶-多线程1

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录

  • 前言
  • [1. 线程](#1. 线程)
    • [1.1 创建线程的方法](#1.1 创建线程的方法)
    • [1.2 构造方法](#1.2 构造方法)
    • [1.3 属性](#1.3 属性)
    • [1.4 启动⼀个线程 - start()](#1.4 启动⼀个线程 - start())
    • [1.5 中断⼀个线程,终止一个线程,让一个线程结束](#1.5 中断⼀个线程,终止一个线程,让一个线程结束)
    • [1.6 等待⼀个线程 - join()](#1.6 等待⼀个线程 - join())
    • [1.7 获取当前线程引⽤](#1.7 获取当前线程引⽤)
    • [1.8 休眠当前线程](#1.8 休眠当前线程)
  • [2. 线程的状态](#2. 线程的状态)
  • 总结

前言

1. 线程

1.1 创建线程的方法

sql 复制代码
class MyTread extends Thread {
    @Override
    public void run() {
        System.out.println("hello");
    }
}
sql 复制代码
        MyTread myTread = new MyTread();
        //创建线程
        myTread.start();

调用start,就会进入进程内部,创建一个新的线程,新的线程就会执行run方法

run方法并没有去调用,但是执行了------》run就是回调函数,自己写好run,别的方法start来调用

这个代码执行,有一个进程,两个线程

调用main方法的线程就是主线程

myTread.start();就是创建了一个新的线程,和主线程是并发执行的

sql 复制代码
class MyTread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
        }
    }
}

public class test   {
    public static void main(String[] args) {
        MyTread myTread = new MyTread();
        //创建线程
        myTread.start();
        while (true) {
            System.out.println("hello main");
        }
    }
}
sql 复制代码
        MyTread myTread = new MyTread();
        //创建线程
        myTread.run();
        while (true) {
            System.out.println("hello main");
        }

myTread.run();并没有创建新的线程,所以主线程就进入到了run方法里面去了

sql 复制代码
class MyTread extends Thread {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}

这里只能try,catch,针对异常的话,为什么呢

因为父类的run方法后面没有throws对应异常

子类重写父类就不能修改方法

所以就只能try catch

sql 复制代码
public class test   {
    public static void main(String[] args) throws InterruptedException {
        MyTread myTread = new MyTread();
        //创建线程
        myTread.start();
        while (true) {
            Thread.sleep(1000);
            System.out.println("hello main");
        }
    }
}

多个线程之间,谁先去CPU上执行,这个是不确定的,随机的

现在我们通过第三方工具来直观的看两个线程之间的关系

点开idea的项目结构

点击SDK编辑

找到JDK的路径

bin目录----》二进制目录,可执行程序目录

javac.exe---》编译器

java.exe--->运行java程序的jvm

点击jconsole.exe

这个会显示本地java的进程

点击303312,就是我们刚刚写的进程

然后点击不安全连接

选择线程

左下角就是java进程中的所有进程

发现一个进程里面不只是两个线程

main是主线程

Thread-0就是我们刚刚创建的线程

点击线程可以看到详细信息

堆栈跟踪:

java.base@17.0.14/java.lang.Thread.sleep(Native Method)

app//com.ck.demo.demo1.test.main(test.java:24)

这个是调用栈

描述了函数方法之间的调用关系

其他的线程就是辅助的线程

比如垃圾回收的线程,合适的时机,释放不使用的对象

比如统计信息,调试信息的线程

我们通过idea内置的调试器也可以看到线程信息

调试运行

创建线程的方法:继承Thread,重新run

第二个就是实现Runnable,重新run

sql 复制代码
class MyThread2 implements Runnable {
    @Override
    public void run() {
        while (true) {
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
}
sql 复制代码
        MyThread2 myThread2 = new MyThread2();
        Thread t2 = new Thread(myThread2);
        t2.start();
        while (true) {
            System.out.println("hello main");
        }

通过Thread来创建线程

线程要执行的任务,是通过Runnable来描述的,而不是通过Thread自己来描述的

Runnable只是一个任务,并不一定和线程是强相关的,后续执行任务,可以是线程,也可以是其他东西(虚拟线程,线程池)

轻量级线程---》协程,虚拟线程

第三种方法

继承Thread,重写run,通过匿名内部类来实现

sql 复制代码
        Thread t = new Thread() {
            @Override
            public void run() {
                System.out.println("hello thread");
            }
        };
        t.start();

匿名内部类没有类名,定义在其他类里面

new Thread(){},这个就是定义匿名内部类,这个类是Thread的子类,然后重写了Thread这个父类的run方法,还创建了一个子类的实例,赋值给了t

一次性使用的类,用完就丢了

然后是第四种写法

sql 复制代码
        Runnable runnable = new Runnable() {
            @Override
            public void run() {
                System.out.println("hello thread2");
            }
        };
        Thread t = new Thread(runnable);
        t.start();

然后是第五个写法,Lambda

sql 复制代码
        Thread t = new Thread(() -> {
            while (true) {
                System.out.println("hello thread3");
            }
        });
        t.start();
        while (true) {
            System.out.println("hello main");
        }

我们采取Lambda

1.2 构造方法

sql 复制代码
        Thread t = new Thread(()->{
            System.out.println("hello thread4");
        },"自定义线程");
        t.start();

这个就是定义线程的名字了

最后一个构造方法,是通过线程组的方式,把多个线程放在一个组里面,方便统一设置---》用的少

1.3 属性

Id是Jvm自动分配的,不能手动设置

就是Thread对象的id

sql 复制代码
        Thread t = new Thread(()->{
            System.out.println("hello thread");
        },"自定义线程");
        t.start();
        System.out.println("线程id"+t.getId());;
        System.out.println("线程name"+t.getName());;
        System.out.println(t.getState());
        System.out.println(t.getPriority());
sql 复制代码
        Thread t = new Thread(()->{
            System.out.println("hello thread");
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        },"自定义线程");
        t.start();
        System.out.println("线程id"+t.getId());;
        System.out.println("线程name"+t.getName());;
        System.out.println(t.getState());
        System.out.println(t.getPriority());

后台线程:这个线程在执行中,不能阻止进程结束,进程结束了,代表这个线程也结束了,也叫做守护线程

前台线程:如果某个线程在执行过程中能够阻止进程结束

sql 复制代码
        Thread t = new Thread(()->{
            while (true) {
                System.out.println("hello thread4");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"自定义线程");
        t.start();

这个主线程虽然结束了,但是子线程没有结束,所以进程也没有结束

sql 复制代码
        Thread t = new Thread(()->{
            while (true) {
                System.out.println("hello thread4");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"自定义线程");
        t.setDaemon(true);
        t.start();

设置为后台线程,就不能阻止进程结束了,所以其他线程结束了,进程就结束了

main线程也是前台线程,所有前台线程结束,进程才会结束

然后进程结束了,这个后台线程也结束了

Thread对象和线程不是一个东西,线程结束了,但是Thread对象还有

sql 复制代码
        Thread t = new Thread(()->{
            while (true) {
                System.out.println("hello thread4");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        },"自定义线程");
        System.out.println(t.isAlive());
        t.start();
        System.out.println(t.isAlive());

isAlive就是看这个线程有没有创建成功

1.4 启动⼀个线程 - start()

run():描述线程要执行的任务,线程的入口

start:在系统内核中创建线程(创建PCB,加入到链表中),速度比较快,然后执行线程run方法

任何线程都可以创建其他线程

一个Thread对象只能调用一下start

一个Thread对象只能对应一个线程,所以就只能一次start

IllegalThreadStateException意思就是状态不合法

1.5 中断⼀个线程,终止一个线程,让一个线程结束

就是结束到一半,强制结束了



事实final就是,没有final修饰,也没有对变量进行修改的变量

所以这样不会报错

这个就是java的特殊限制

为什么这样就可以呢

sql 复制代码
        Thread t = new Thread(()->{
            //先获取线程的引用
            Thread currentThread = Thread.currentThread();
            while (!currentThread.isInterrupted()) {
                System.out.println("hello thread4");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        t.interrupt();

isInterrupted是看是否被终止

t.interrupt();是终止线程

currentThread是获取调用这个方法的线程

Lambda里面的代码,不能访问t的,因为Lambda的代码先执行

sql 复制代码
        Thread t = new Thread(()->{
            //先获取线程的引用
            Thread currentThread = Thread.currentThread();
            while (!currentThread.isInterrupted()) {
                System.out.println("hello thread4");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();

这个是因为sleep(1000)的时候,直接就interrupt了-----》Thread.sleep直接就被唤醒了,然后抛出了InterruptedException异常,由于Thread.sleep抛出的

sql 复制代码
        Thread t = new Thread(()->{
            //先获取线程的引用
            Thread currentThread = Thread.currentThread();
            while (!currentThread.isInterrupted()) {
                System.out.println("hello thread4");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
                    System.out.println("异常");
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();

为什么会这样呢

这个是因为sleep等阻塞函数被唤醒之后,就会清空interrupted标志位

sql 复制代码
        Thread t = new Thread(()->{
            //先获取线程的引用
            Thread currentThread = Thread.currentThread();
            while (!currentThread.isInterrupted()) {
                System.out.println("hello thread4");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
//                    throw new RuntimeException(e);
                    System.out.println("异常");
                    break;
                }
            }
        });
        t.start();
        Thread.sleep(3000);
        t.interrupt();

这样就可以了

1.6 等待⼀个线程 - join()

sql 复制代码
        Thread t = new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("hello thread");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t.start();
        t.join();//等待t线程结束
        System.out.println("hello main");

如果t线程等待主线程到t.join()已经结束了,那么就什么都不用等了

sql 复制代码
        Thread t1 = new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("hello 1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        Thread t2 = new Thread(()->{
            for (int i = 0; i < 3; i++) {
                System.out.println("hello 2");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        t1.join();//等待t线程结束
        t2.start();
        t2.join();
        System.out.println("hello main");
sql 复制代码
        t1.start();
        t2.start();
        t1.join();//等待t线程结束
        t2.join();

t1.join()就是一直等待t1结束,死等

1.7 获取当前线程引⽤

sql 复制代码
        Thread mainThread = Thread.currentThread();
        Thread t1 = new Thread(()->{
            try {
                mainThread.join();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            for (int i = 0; i < 3; i++) {
                System.out.println("hello 1");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        t1.start();
        Thread.sleep(3000);
        System.out.println("hello main");

这个就是子线程等待主线程

1.8 休眠当前线程


2. 线程的状态

NEW: 安排了⼯作, 还未开始⾏动

• RUNNABLE: 可⼯作的. ⼜可以分成正在⼯作中和即将开始⼯作.

• BLOCKED: 这⼏个都表⽰排队等着其他事情

• WAITING: 这⼏个都表⽰排队等着其他事情

• TIMED_WAITING: 这⼏个都表⽰排队等着其他事情

• TERMINATED: ⼯作完成了.

sql 复制代码
        Thread t1 = new Thread(()->{
        });
        System.out.println(t1.getState());
        t1.start();
        Thread.sleep(1000);
        System.out.println(t1.getState());

new是线程还没创建

TERMINATED是线程已经没了

RUNNABLE:正在运行,或者随时可以运行

sql 复制代码
        Thread t1 = new Thread(()->{
            while (true) {

            }
        });
        System.out.println(t1.getState());
        t1.start();
        Thread.sleep(1000);
        System.out.println(t1.getState());

BLOCKED:锁竞争引起的阻塞

WAITING:join,没有超时时间的等待

TIMED_WAITING:sleep,超时时间的等待

都是阻塞等待

sql 复制代码
        Thread t1 = new Thread(()->{
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
        System.out.println(t1.getState());
        t1.start();
        Thread.sleep(1000);
        System.out.println(t1.getState());
sql 复制代码
        Thread mainThread = Thread.currentThread();
        Thread t1 = new Thread(()->{
            System.out.println(mainThread.getState());
            while (true) {
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        });
//        System.out.println(t1.getState());
        t1.start();
        t1.join();

总结

相关推荐
m***56721 小时前
【Spring】Spring MVC案例
java·spring·mvc
Geoking.2 小时前
【Java】Java Stream 中的 collect() 方法详解:流最终操作的核心工具
java·开发语言
清风徐来QCQ2 小时前
javaScript(map,ref,?,forEach,watch)
java·前端·javascript
q***73552 小时前
windows配置永久路由
android·前端·后端
7***n752 小时前
Java构建工具
java·开发语言
m***9822 小时前
万字详解 MySQL MGR 高可用集群搭建
android·mysql·adb
u***u6853 小时前
Kotlin多平台开发实践
android·开发语言·kotlin
Dandelion____z3 小时前
AI 驱动业务的致命风险:如何用架构设计守住安全底线?
java·大数据·人工智能·spring boot·aigc·jboltai
Q***K553 小时前
Kotlin与Java互操作指南
java·开发语言·kotlin