当前位置: 首页 > news >正文

JavaEE初级——Thread多线程

多线程

  • 认识线程
    • 线程的概念
    • 创建线程
  • Thread类
    • Thread常见的构造函数
    • Thread常见属性
    • Thread中常用方法
    • 线程的状态

认识线程

线程的概念

一个线程就是一个执行流,每一个线程都可以按照顺序执行自己代码,多个线程可以“同时”执行
多线程是并发编程的刚需
随着不断发展,单核CPU发展遇到了瓶颈,为了提高算力,就开始采用多核CPU,并发编程可以很好利用多核CPU
并且线程比进程更轻量,也程轻量级进程
创建 / 销毁 / 调度线程的效率多比进程快

创建线程

创建线程常用方法
继承 Thread, 重写 run
实现 Runnable, 重写 run
继承 Thread, 重写 run, 使用匿名内部类
实现 Runnable, 重写 run, 使用匿名内部类
使用 lambda 表达式

1.继承 Thread, 重写 run

class MyThread extends Thread{
//重写其Thread中的run方法@Overridepublic void run() {System.out.println("hello Thread");}
}
public class demo {public static void main(String[] args) {Thread t = new MyThread();t.start();System.out.println("hello main");}
}

2.实现 Runnable, 重写 run

class MyRunnalbe implements Runnable{//实现run方法@Overridepublic void run() {System.out.println("hello Thread");}
}
public class demo2 {public static void main(String[] args) {//传入Runnable对象MyRunnalbe myRunnalbe = new MyRunnalbe();Thread t = new Thread(myRunnalbe);t.start();System.out.println("hello main");}}

3.继承 Thread, 重写 run, 使用匿名内部类

//使用匿名内部类实现
public class demo3 {public static void main(String[] args) {Thread t = new Thread(){@Overridepublic void run() {System.out.println("hello Thread");}};t.start();System.out.println("hello main");}
}

4.实现 Runnable, 重写 run, 使用匿名内部类

public class demo4 {public static void main(String[] args) {Thread t = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("hello Thread");}});t.start();System.out.println("hello main");}
}

5.使用 lambda 表达式

public class demo5 {public static void main(String[] args) {Thread t = new Thread(()->{System.out.println("hello Thread");});t.start();System.out.println("hello main");}
}

运行结果如下,但是其两个线程执行顺序是不确定的,所以结果有两种
一种先打印 hello main hello Thread,另一种就相反的顺序
在这里插入图片描述

t.start()是开启线程

public class demo5 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{while (true){System.out.println("hello Thread");try {Thread.sleep(100);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();while (true){System.out.println("hello main");Thread.sleep(100);}}
}

上面这个代码是一直执行main和Thread这两个线程,并且执行顺序是不确定的,线程是和平常调用函数是不一样的,因为函数调用是会一直等函数执行完才可以执行下面的语句,因此如果这里是函数调用的话,只会出现一个值,要么一直打印hello Thread,要么是一直打印hello main
在这里插入图片描述

上面看似有5种,其实相当于三种,重写run方法,实现Runnable接口,使用lambda表达式
使用jdk目录下的bin目录中的的jconsole 来找出对应的线程文件,在这里面就可以找到我们创建的线程,此时main是线程,Thread-0也是我们创建的线程,由于我们创建的时候并没有给其命名,所以其系统自动命名为Thread - 数字
在这里插入图片描述

在这里插入图片描述

Thread类

Thread常见的构造函数

方法说明
Thread()创建线程对象
Thread(Runnable target)使用Runnable对象创建线程对象
public Thread(String name)创建线程对象,并命名
public Thread(Runnable target, String name)使用Runnable对象创建线程对象,并命名
public Thread(Runnable target, String name)线程分组管理
public class demo6 {public static void main(String[] args) {Thread t1 = new Thread(()->{System.out.println(Thread.currentThread().getName());},"线程1");Thread t2 = new Thread(() ->{System.out.println(Thread.currentThread().getName());},"线程2");Thread t3 = new Thread(()->{System.out.println(Thread.currentThread().getName());},"线程3");t1.start();t2.start();t3.start();System.out.println("main");}
}

运行结果如下,但是顺序是不确定的,可以看出给线程命名成功
这里的**Thread.currentThread()**是获取当前Thread线程对象,**getName()**是获取当前线程的名称
在这里插入图片描述

Thread常见属性

属性获取方法
IDgetId()
名称getName()
状态getState()
优先级getPriority()
是否后台线程isDaemon()
是否存活isAlive()
是否被中断isInterrupted()

ID:是线程的唯一标识,不同线程不会重复
名称:就是线程的名字,这里可以自己命名,系统自动命名是Thread-数字
状态:表示线程的情况,这里可以简单认为阻塞和就绪,细说的话有好几种状态
优先级:我们可以给线程设置优先级,优先级高的先执行,但是对于像Windows、Lunix等高级系统,不能只看优先级,谁先执行决定权还是在操作系统
后台线程:也称守护线程,后台线程不会阻断整个线程结束,而前台线程会阻断整个线程结束,并且所有非后台进程结束,才算真的结束
存活:线程是否在执行,简单理解是run方法是否结束
中断:停止某一个线程运行
下面进行详细讲解

getName()获取线程名,默认为Thread - 数字

public class demo7 {public static void main(String[] args) {Thread t = new Thread(()->{//获取线程名System.out.println(Thread.currentThread().getName());});Thread t2 = new Thread(()->{//获取线程名System.out.println(Thread.currentThread().getName());});Thread t3 = new Thread(()->{//获取线程名System.out.println(Thread.currentThread().getName());});t.start();t2.start();t3.start();System.out.println("hello main");}
}

结果如下,其顺序是不确定的
在这里插入图片描述
在这里插入图片描述

isDaemon(),将一个线程设置为后台线程

public class demo8 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(()->{for (int i = 0; i < 5; i++) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});
//设置其为后端线程(守护线程)t.setDaemon(true);t.start();Thread.sleep(1000);System.out.println("主线程结束");}
}

在这里插入图片描述
可以看出这里的后端线程没有执行完,但是前台线程执行完了,此时就结束了
如果不设置后端线程,其t线程和main线程都执行完才结束
在这里插入图片描述

Thread中常用方法

1.休眠当前线程 参数是休眠时间,一个是休眠当前线程millis毫秒,一个更精确

方法说明
public static native void sleep(long millis)休眠当前线程millis毫秒
public static void sleep(long millis, int nanos)同理,就是精度提高了

但是sleep(0)是特殊情况,属于线程主动放权,起到降低CPU的使用率,在资源紧张的时候,可能会进行一些合理的调整

中断一个线程

使用自己定义的一个标志位进行中断线程

//中断一个线程
public class demo9 {private static boolean running = true;public static void main(String[] args) {Thread t = new Thread(() ->{while (running){System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}System.out.println("t线程结束");});t.start();Scanner sc = new Scanner(System.in);System.out.println("输入0,t线程结束");int n = sc.nextInt();if(n == 0){running = false;}}
}

运行结果如下
在这里插入图片描述
但是这个标识符要注意位置
像下面这样,将这个标识符放到main函数中,这样就会出现变量捕获
java中变量捕获,其实就是变量的一份拷贝,一旦拷贝了不一样可能会导致代码混乱,所以说java直接禁止了修改
但是可以像上面一样写成成员变量,这里就变成内部类访问外部类成员,这个是可以访问的
在这里插入图片描述
使用interrupted()和isInterrupted()来代替标志位

方法说明
public void interrupt()中断其对象关联的线程,如果线程阻塞,以异常的方式,反之设置标志位
public boolean isInterrupted()判断当前线程中断标志位是否设置,不清理中断标志位
public static Thread currentThread()返回当前线程对象的引用
public class demo10 {public static void main(String[] args) {Thread t = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) {System.out.println("hello Thread");try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});t.start();Scanner sc = new Scanner(System.in);System.out.println("输入0,阻断t线程");int n = sc.nextInt();if(n == 0){t.interrupt();}}
}

此时就会直接中断sleep
在这里插入图片描述
当线程因为wait/jioin/sleep方法引起阻塞,会以InterruptedException异常的形式通知,并中断标志,当然接不接书线程,主要还是取决于catch中是如何处理的

等待一个线程 join

方法说明
public void join()等待线程结束
public void join(long millis)等待线程结束,最多等millis毫秒
public void join(long millis,int nanos和上面一样,但是时间的精度更高

有时候需要等待一个线程结束,才进行其他步骤

public class demo11 {private static int sum = 0;public static void main(String[] args) throws InterruptedException {//创建一个线程,计算1 ~ 1000Thread t = new Thread(() ->{for(int i = 1;i <= 1000;i++){sum += i;}});t.start();//这里main线程要等到其t线程执行完才可以执行t.join();System.out.println(sum);}
}

在这里插入图片描述
此时如果不等待t线程结束,拿此时main线程打印的sum值就不正确
在这里插入图片描述

线程的状态

状态说明
NEW安排了,工作还没做
RUNNABLE可工作的,又可以分为正在工作和准备开始工作
TERMINATED工作完成了,线程已经销毁,但Thread对象还在
WAITING标识在等待,死等状态
TIMED_WAITING有时间限制的等待
BLOCKED等待,特指由锁引起的等待
public class demo12 {public static void main(String[] args) throws InterruptedException {Thread t = new Thread(() ->{System.out.println("Thread:"+Thread.currentThread().getState());});System.out.println(t.getState());t.start();Thread.sleep(1000);System.out.println("main:"+t.getState());}
}

此时先打印NEW,此时t线程都没有启动,t需要等到start才能执行
启动后 RUNNABLE 正在执行或准备执行,最后TERMINATED,说明已经执行完了
在这里插入图片描述

public class demo12 {public static void main(String[] args) throws InterruptedException {//获取main的线程对象Thread mainThread = Thread.currentThread();Thread t = new Thread(() ->{while (true){System.out.println("mainThread:"+mainThread.getState());try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});System.out.println(t.getState());t.start();t.join();System.out.println(t.getState());}
}

此时main线程要等待t线程执行完毕,并且这里是死等,因此t线程在执行时候,mainThread状态是 WAITING
在这里插入图片描述

public class demo12 {public static void main(String[] args) throws InterruptedException {//获取main的线程对象Thread mainThread = Thread.currentThread();Thread t = new Thread(() ->{while (true){System.out.println("mainThread:"+mainThread.getState());try {Thread.sleep(1000);} catch (InterruptedException e) {throw new RuntimeException(e);}}});System.out.println(t.getState());t.start();t.join(100000);System.out.println(t.getState());}
}

有时间限制的等待
在这里插入图片描述
当然使用sleep也是同样的效果
在这里插入图片描述

http://www.dtcms.com/a/469478.html

相关文章:

  • GJOI 10.9 题解
  • 如何设计一个架构良好的前端请求库?
  • 精灵图(雪碧图)的生成和使用
  • Web 开发 27
  • 网站制作主要公司学校网站开发系统的背景
  • Linux小课堂: 目录操作命令深度解析(LS 与 CD 命令)
  • 面向财经新闻的文本挖掘系统设计与实现(论文)
  • 【Redis-cli操作数据类型】Redis八大数据类型详解:从redis-cli操作到场景落地
  • linux安装海量数据库和操作
  • Redis分片+Sentinel熔断设计TP99控制在15ms内
  • 山海关城乡建设局网站佛山网络科技公司有哪些
  • 我的算法模板1(快速幂、逆元、组合数)
  • 八股-2025.10.11
  • 图片上传网站变形的处理旅游网站建设的概念
  • 集团培训网站建设注册公司需要什么条件和手续
  • Spark RDD详解 —— RDD特性、lineage、缓存、checkpoint、依赖关系
  • 玩转Docker系列专栏大纲
  • 网络攻防技术:信息收集技术
  • spark on hive 还是 hive on spark?
  • 搞懂数据通信不用愁!网络基础 + 参考模型 + VRP 配置,一篇全搞定
  • 解决MacOS上CLion调试的时候容器的值显示为0的问题
  • Docker快速入门——第一章Docker入门
  • 建站助手官网淘宝seo具体优化方法
  • 邯郸网站建设哪儿好网站建设参考文献英文书籍
  • STM32 串口收发数据-中断的方式
  • k8s ingress-nginx 学习
  • 【含文档+PPT+源码】基于springboot的旅游路线推荐系统的设计与实现
  • 嘉兴seo网站优化竞价托管的注意事项
  • K8S 概念、安装与核心工作机制详解
  • 做网站需要公司有哪些网站最新点击量排名