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

【Java】多线程Thread类

1. 进程与线程

进程与线程的基本认识

进程(Process):进程是程序的一次动态执行过程,它经历了从代码加载、执行、到执行完毕的一个完整过程;同时也是并发执行的程序在执行过程中分配和管理资源的基本单位,竞争计算机系统资源的基本单位。

线程(Thread):线程可以理解为进程中的执行的一段程序片段,是进程的一个执行单元,是进程内可调度实体,是比进程更小的独立运行的基本单位,线程也被称为轻量级进程。

即使多进程可以提高硬件资源的利用率,但是进程的启动和销毁需要消耗大量的系统性能,从而导致程序的执行性能下降。所以为了进一步提升并发操作的处理能力,在进程的基础上又划分了多线程概念。

2. 多线程实现

Thread类实现多线程

 Thread类的方法

1.通过继承Thread类,并重写父类的run()方法实现
public void run(){ }

定义线程类:


class MyThread extends Thread{@Overridepublic void run() {while(true){System.out.println("hello Thread");try{Thread.sleep(1000);}catch(InterruptedException e){throw new RuntimeException(e);}}}
}
public class Demo1 {public static void main(String[] args)throws InterruptedException {Thread t=new MyThread();t.start();//创建一个线程//t.run();while(true){System.out.println("hello main");Thread.sleep(1000);}//两个循环在同时进行}
}

Runnable接口实现多线程

使用Thread类的确可以实现多线程,但是也容易发现它的缺陷:面向对象的单继承局限,因此才采用Runnable接口来实现多线程。

该接口的定义如下(以下并不需要在代码中实现):


class MyRunnable implements Runnable{@Overridepublic void run(){while(true){System.out.println("hello main");try{Thread.sleep(1000);}catch(InterruptedException e){throw new RuntimeException(e);}}}
}
public class Demo2 {public static void main(String[] args)throws Exception{Runnable runnable=new MyRunnable();Thread t=new Thread(runnable);t.start();while(true){System.out.println("hello main");Thread.sleep(1000);}}
}

Thread(String name)后边两种方法,我们可以给线程对象命名。

默认情况下,线程对象名字是Thread-N,N是从0开始增加。

3.Thread常见方法

start()

start() 启动一个线程。

start()方法可以开启一个线程,线程创建好后是不会开始执行的,要使用start来开启。

只有当start(),操作系统内核才会创建一个真真正正的线程PCB,然后去调用run()方法,此时操作系统层面才会真的创建一个线程。

sleep()

使用sleep时会有一个中断异常(非受查异常),需要我们手动抛出

join()

join(): 等待一个线程;

join(t)表示等待一个线程t毫秒。
因为我们线程是随即调度的过程,线程的执行顺便我们无法把控,但在一些场景下我们需要有明确的线程程序顺序,等待线程,就是可以控制两个线程的结束顺序。

比如在main方法中有两个线程t1、t2,在主线程中有t1.join() ,那么主线程就要阻塞 等待t1线程执行完了才能在执行。如果在t1线程中有t2.join(),那么t1线程就要阻塞 等待t2线程执行完了才能执行任务。

如果去掉join()方法后,主线程与Thread线程抢占式执行!


public class Demo12 {public static void main(String[] args)throws Exception {Thread t=new Thread(()->{for(int i=0;i<3000;i++){System.out.println("hello thread");try{Thread.sleep(1000);}catch(InterruptedException e) {throw new RuntimeException(e);}}System.out.println("线程结束");});t.start();t.join(3000);//等待线程t执行完毕System.out.println("main 线程结束");}
}

中断线程

线程中断: 我们这里的中断,并不是线程立即就结束了,而是通知该线程,你应该结束了,但实际是否结束,取决于线程里具体的代码

package Thread;public class Demo3 {public static  boolean flag=true;public static void main(String[] args)throws InterruptedException{Thread t1=new Thread(()->{while(flag){System.out.println("hello Thread");try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}}});t1.start();Thread.sleep(3000);flag=false;}
}

flag 变量的作用是作为线程执行的控制开关,用于安全地终止 t1 线程的循环执行。

flag 变量是主线程与 t1 线程之间的一个通信媒介,通过修改它的值,主线程可以安全地通知 t1 线程“该停止工作了”,避免了线程的无限循环。如果没有 flagt1 线程的 while(true) 循环将无法终止,导致线程永久运行

即使 t1 线程因 sleep 进入阻塞状态,只要 sleep 时间结束,线程恢复运行后会立即检查 flag,从而响应终止信号。

4.Thread类的常见属性

getId():

 ID是线程的唯一标识,不同的线程不重复

package Thread;class MyThread1 extends Thread{@Overridepublic void run(){System.out.println("hello word");}
}
public class getIdtest {public static void main(String[] args) {Thread t=new MyThread1();System.out.println(t.getId());}
}

getName: 

名称是我们构造方法设置的名称,如果没有设置,那就是默认名。

public class getNametest {public static void main(String[] args) {Thread t=new Thread();System.out.println(t.getName());//因为我们没有命名,所以使用的是默认名。}
}

getState(): 

状态:

getState()方法可以获取当前Thread对象的状态,这是Java层面定义的线程状态,要注意和PCB上的区分。

线程有6种状态:NEW, RUNNABLE, BLOCKED, WAITING, TIMED_WAITING,  TERMINATED。

NEW表示创建好了一个Java线程对象,安排好了任务,只是还没有启动,没有调用start方法之前是不会创建PCB的,和PCB没有关系。

RUNNABLE是运行状态或就绪状态,在操作系统层面已经有了对应的PCB。

BLOCKED是阻塞状态的一种,在等待锁

WAITING表示一直没有时间限制的等待,一直等直到被唤醒。

TIMED_WAITING指定了等待时间的阻塞状态,过时就继续执行任务。

TERMINATED是执行结束,PCB已经销毁,Java层面的线程对象还在。

查看Thread对象的所有状态:

package Thread;public class Demo14 {public static void main(String[] args) {for(Thread.State state: Thread.State.values()){System.out.println(state);}}
}

getPriority: 

可以获取当前线程的优先级,但没有太大的用,实际还得取决于操作系统内核的具体调度。

package Thread;public class test {public static void main(String[] args) {Thread t=new Thread();System.out.println(t.getPriority());}
}

isDaemon(): 

是否为守护线程(也叫后台线程), 简单的理解一下,我们手动创建的线程都是前台线程,JVM自带的线程都是后台线程,我们也可以通过setDaemon()方法将前台线程设置为后台线程。

 public static void main(String[] args) {Thread t=new MyThread2();System.out.println(t.isDaemon());}

isAlive(): 

线程的run方法是否执行结束。

package Thread;
class MyThread2 extends Thread{@Overridepublic void run(){System.out.println("hello thread1");}
}
public class Demo4 {public static void main(String[] args) {Thread t=new MyThread2();System.out.println(t.isAlive());t.start();System.out.println(t.isAlive());}}

interrupt()方法

第一种解释:

  1. 发送信号:通知线程“需要中断当前操作”,而非强制杀死线程。
  2. 打破阻塞:中断 sleep/wait 等阻塞方法,让线程从阻塞中唤醒并处理中断。
  3. 协作终止:线程通过检查中断状态(isInterrupted())或处理 InterruptedException,决定是否结束,体现“谁运行谁控制”的设计原则

在 Demo3 中,正是由于 t1 线程内部通过 while (!isInterrupted()) 主动响应中断信号,主线程的 t1.interrupt() 才能安全、优雅地终止线程。

 public static void main(String[] args)throws InterruptedException{Thread t1=new Thread(()->{while(!Thread.currentThread().isInterrupted()){System.out.println("hello Thread");try{Thread.sleep(1000);}catch(InterruptedException e){e.printStackTrace();}}});t1.start();Thread.sleep(3000);t1.interrupt();}

第二个解释:

1.把线程内部的标志位设置为true
2.如果线程正在sleep,会触发异常,将sleep唤醒。
sleep唤醒的同时,会清空标志位,将刚才设置的标志位在设置为false(这就导致,我们的循环会继续执行).
如果我们想去中断,我们可以在异常里加一个break。

2.3 多线程运行状态

任意线程具有5种基本的状态:

1.创建状态

实现Runnable接口和继承Thread类创建一个线程对象后,该线程对象处于创建状态,此时它已经有了内存空间和其它资源,但他还是处于不可运行的。

2.就绪状态

新建线程对象后,调用该线程的start方法启动该线程。启动后,进入线程队列排队,由CPU调度服务。

3.运行状态

就绪状态的线程获得处理器的资源时,线程就进入了运行状态,此时将自动调用run方法。

4.阻塞状态

正在运行的线程在某些特殊情况下,如:当前线程调用sleep、suspend、wait等方法时,运行在当前线程里的其它线程调用join方法时,以及等待用户输入的时候。只有当引起阻塞原因消失后,线程才能进入就绪状态。

5.终止状态

当线程run方法运行结束后,或者主线程的main()方法结束后,线程才能处于终止状态,线程一旦死亡就不能复生。

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

相关文章:

  • 什么是AIGC(人工智能生成内容)
  • 牛客周赛 Round 104(小红的树不动点)
  • 人工智能入门②:AI基础知识(下)
  • 计算机程序编程软件开发设计之node..js语言开发的基于Vue框架的选课管理系统的设计与实现、基于express框架的在线选课系统的设计与实现
  • STM32——软硬件I2C
  • Font Awesome Kit 使用详解
  • OTA升级
  • Vue Router 嵌套路由与布局系统详解:从新手到精通
  • 【牛客刷题】随机加减操作:两种高效解法详解(DFS记忆化搜索和动态规划集合更新法)
  • java序列化和反序列化
  • FX10/20 (CYUSB401X)开发笔记5 固件架构
  • 【个人项目】跑者天地—测试报告
  • 深入解析二维数组传参的本质
  • 运动场和光流-动手学计算机视觉17
  • 正则表达式实用面试题与代码解析专栏
  • 【Nginx】限流设置
  • 二三层交换转发业务~基础汇总
  • Mysql笔记-错误条件\处理程序
  • SSM从入门到实践:1.1 Spring框架概述与IoC容器入门
  • 堆(Heap):高效的优先级队列实现
  • duiLib 解决点击标题栏中按钮无响应问题
  • ROS2基础
  • C语言零基础第19讲:自定义类型—联合体和枚举
  • 解锁Java开发神器:XXL-Job从入门到精通
  • BT_LE_ADV_CONN_ONE_TIME 参数详解
  • Spring 创建 Bean 的 8 种主要方式
  • [创业之路-556]:创新的本质是赚不确定性带来的潜在价值,把不确定性逐步转化确定性,周而复始。
  • 产品设计.Ai产品经理
  • 48.Seata认识、部署TC服务、微服务集成
  • 网络中的一些基本概念