一、线程的基本概念

news/2024/4/25 9:23:59/文章来源:https://blog.csdn.net/weixin_36091079/article/details/129146918

文章目录

  • 基础概念
    • 线程与进程
      • 什么是进程?
      • 什么是线程?
      • 进程和线程的区别:
    • 多线程
      • 什么是多线程?
      • 多线程的局限性
    • 串行、并行、并发
    • 同步异步、阻塞非阻塞
  • 线程的创建
    • 1、继承Thread类,重写run方法
    • 2、实现Runnable接口,重写run方法
    • 3、实现Callable,重写call方法,配合FutureTask
    • 4、基于线程池构建线程 ExecutorService
  • 线程的使用
    • 线程的状态
    • 线程的常用方法
      • 获取当前线程Thread.currentThread()
      • 线程的名字getName()
      • 线程的优先级Thread.getPriority()
      • 线程的让步Thread.yield()
      • 线程的休眠Thread.sleep()
      • 线程的强占Thread.join()
      • 守护线程Daemon
      • 线程的等待和唤醒 Object.wait()&Object.notify()
    • 线程的结束方式
      • stop方法(不用)
      • 使用共享变量(很少用)
      • interrupt方式
    • wait和sleep的区别?

基础概念

线程与进程

什么是进程?

进程是指运行中的程序。 比如我们使用钉钉浏、览器等,启动这个程序时操作系统会给这个程序分配一定的资源(占用内存资源)。

什么是线程?

线程是CPU调度的基本单位,每个线程执行的都是某一个进程的代码的某个片段。
所谓进程就是线程的容器,需要线程利用进程中的一些资源,处理一个代码、指令,最终实现进程所预期的结果。

进程和线程的区别:

  • 根本不同:进程是操作系统分配的资源,而线程是CPU调度的基本单位。
  • 资源方面:同一个进程下的线程共享进程中的一些资源。线程同时拥有自身的独立存储空间。进程之间的资源通常是独立的。
  • 数量不同:进程一般指的就是一个进程。而线程是依附于某个进程的,而且一个进程中至少会有一个或多个线程。
  • 开销不同:毕竟进程和线程不是一个级别的内容,线程的创建和终止的时间是比较短的。而且线程之间的切换比进程之间的切换速度要快很多。而且进程之间的通讯很麻烦,一般要借助内核才可以实现,而线程之间通讯相当方面。
  • … …

多线程

什么是多线程?

多线程是指:单个进程下同时运行的多个线程。
多线程的目的是为了提高CPU的利用率。
可以通过避免一些网络IO或者磁盘IO等需要等待的操作,让CPU去调度其他线程。这样可以大幅度的提升程序的效率,提高用户的体验。比如Tomcat可以做并行处理,提升处理的效率,而不是一个一个排队。比如要处理一个网络等待的操作,开启一个线程去处理需要网络等待的任务,让当前业务线程可以继续往下执行逻辑,效率是可以得到大幅度提升的。

多线程的局限性

  • 如果线程数量特别多,CPU在切换线程上下文时,会额外造成很大的消耗。
  • 任务的拆分需要依赖业务场景,有一些异构化的任务,很难对任务拆分,还有很多业务并不是多线程处理更好。
  • 线程安全问题:虽然多线程带来了一定的性能提升,但是再做一些操作时,多线程如果操作临界资源,可能会发生一些数据不一致的安全问题,甚至涉及到锁操作时,会造成死锁问题。

串行、并行、并发

串行就是一个一个排队,第一个做完,第二个才能上。
并行就是同时处理。
这里的并发并不是三高中的高并发问题,这里是多线程中的并发概念(CPU调度线程的概念)。 CPU在极短的时间内,反复切换执行不同的线程,看似好像是并行,但是只是CPU高速的切换。

并行囊括并发。并行就是多核CPU同时调度多个线程,是真正的多个线程同时执行。单核CPU无法实现并行效果,单核CPU是并发。

同步异步、阻塞非阻塞

同步与异步:执行某个功能后,被调用者是否会主动反馈信息
阻塞和非阻塞:执行某个功能后,调用者是否需要一直等待结果的反馈。
两个概念看似相似,但是侧重点是完全不一样的。

同步阻塞:比如用锅烧水,水开后,不会主动通知你。烧水开始执行后,需要一直等待水烧开。
同步非阻塞:比如用锅烧水,水开后,不会主动通知你。烧水开始执行后,不需要一直等待水烧开,可以去执行其他功能,但是需要时不时的查看水开了没。
异步阻塞:比如用水壶烧水,水开后,会主动通知你水烧开了。烧水开始执行后,需要一直等待水烧开。
异步非阻塞:比如用水壶烧水,水开后,会主动通知你水烧开了。烧水开始执行后,不需要一直等待水烧开,可以去执行其他功能。

异步非阻塞这个效果是最好的,平时开发时,提升效率最好的方式就是采用异步非阻塞的方式处理一些多线程的任务。

线程的创建

创建线程的几种方式:

1、继承Thread类,重写run方法

启动线程是调用start方法,这样会创建一个新的线程,并执行线程的任务。
如果直接调用run方法,这样会让当前线程执行run方法中的业务逻辑。

static class MyThread extends Thread {@Overridepublic void run() {System.out.println("Hello MyThread!");}
}

2、实现Runnable接口,重写run方法

static class MyRun implements Runnable {@Overridepublic void run() {System.out.println("Hello MyRun!");}
}
// 最常用的两种方式:
public void myRunMethod(){// 匿名内部类方式:Thread thread1 = new Thread(new Runnable() {@Overridepublic void run() {System.out.println("MyRunnable!");}});// lamdba方式:Thread thread2 = new Thread(()->{System.out.println("MyRunnable!");});
}

3、实现Callable,重写call方法,配合FutureTask

Callable一般用于有返回结果的非阻塞的执行方法。同步非阻塞。

static class MyCall implements Callable<String> {@Overridepublic String call() throws Exception {System.out.println("Hello MyCall!");return "success";}
}

4、基于线程池构建线程 ExecutorService

public class HowToCreateThread {// 启动线程的几种方式public static void main(String[] args) {new MyThread().start();new Thread(new MyRun()).start();new Thread(() -> {System.out.println("Hello Lambda!");}).start();Thread t = new Thread(new FutureTask<String>(new MyCall()));t.start();ExecutorService service = Executors.newCachedThreadPool();service.execute(() -> {System.out.println("Hello ThreadPool!");});service.shutdown();}
}

注:追其底层,其实只有一种,实现Runnable。

线程的使用

线程的状态

网上对线程状态的描述有很多,有5种、6种、7种,都可以接受。
5种状态一般针对传统的线程状态来说的(操作系统层面的)
线程的五种状态

Java中给线程定义的是6种状态。
线程的6种状态

  • NEW:Thread对象被创建出来了,但是还没有执行start方法。
  • RUNNABLE: Thread对象调用了start方法,就为RUNNABLE状态(CPU调度/没有调度)
  • BLOCKED:synchronized没有拿到同步锁,被阻塞的情况。
  • WAITING:调用wait方法就会处于WAITING状态,需要被手动唤醒。
  • TIME_WAITING:调用sleep方法或者join方法,会被自动唤醒,无需手动唤醒。
  • TERMINATED:run方法执行完毕,线程生命周期结束。

BLOCKED、WAITING、TIME_WAITING都可以理解为是阻塞、等待状态,因为处在这三种状态下,CPU不会调度当前线程。

下面在Java代码中验证一下具体效果:

public static void main(String[] args) throws InterruptedException {// NEWThread t1 = new Thread();System.out.println(t1.getState());// RUNNABLEThread t2 = new Thread(() -> {while (true) {}});t2.start();Thread.sleep(500);System.out.println(t2.getState());// BLOCKEDObject obj3 = new Object();Thread t3 = new Thread(() -> {// t3线程拿不到锁资源,导致变为BLOCKED状态synchronized (obj3) {}});// main线程拿到obj的锁资源synchronized (obj3) {t3.start();Thread.sleep(500);System.out.println(t3.getState());}// WAITINGObject obj4 = new Object();Thread t4 = new Thread(() -> {synchronized (obj4) {try {obj4.wait();} catch (InterruptedException e) {e.printStackTrace();}}});t4.start();Thread.sleep(500);System.out.println(t4.getState());// TIMED_WAITINGThread t5 = new Thread(() -> {try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}});t5.start();Thread.sleep(500);System.out.println(t5.getState());// TERMINATEDThread t6 = new Thread(() -> {try {Thread.sleep(500);} catch (InterruptedException e) {e.printStackTrace();}});t6.start();Thread.sleep(1000);System.out.println(t6.getState());
}

线程的常用方法

获取当前线程Thread.currentThread()

Thread的静态方法获取当前线程对象。

public static void main(String[] args) {// 获取当前线程的方法Thread main = Thread.currentThread();System.out.println(main);// Thread[#1,main,5,main]// "Thread[" + getName() + "," + getPriority() + "," + group.getName() + "]";
}

线程的名字getName()

在构建Thread对象完毕后,一定要设置一个有意义的名称,方面后期排查错误。

public static void main(String[] args) {Thread t1 = new Thread(() -> {System.out.println(Thread.currentThread().getName());});t1.setName("模块-功能-计数器");t1.start();
}

线程的优先级Thread.getPriority()

其实就是CPU调度线程的优先级。 Java中给线程设置的优先级别有10个级别,从1~10任取一个整数。 如果超出这个范围,会排除参数异常的错误。

public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println("t1:"+i);}});Thread t2 = new Thread(() -> {for (int i = 0; i < 200; i++) {System.out.println("t2:"+i);}});t1.setPriority(1);t2.setPriority(10);t2.start();t1.start();
}

线程的让步Thread.yield()

可以通过Thread的静态方法yield,让当前线程从运行状态转变为就绪状态。

public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 100; i++) {if (i == 50) {Thread.yield();}System.out.println("t1:" + i);}});Thread t2 = new Thread(() -> {for (int i = 0; i < 100; i++) {System.out.println("t2:" + i);}});t2.start();t1.start();
}

线程的休眠Thread.sleep()

Thread的静态方法,让线程从运行状态转变为等待状态。

sleep有两个方法重载:

  • 第一个就是native修饰的,让线程转为等待状态的效果
  • 第二个是可以传入毫秒和一个纳秒的方法(如果纳秒值大于等于0.5毫秒,就给休眠的毫秒值 +1。如果传入的毫秒值是0,纳秒值不为0,就休眠1毫秒)

sleep会抛出一个InterruptedException

public static void main(String[] args) throws InterruptedException {System.out.println(System.currentTimeMillis());Thread.sleep(1000);System.out.println(System.currentTimeMillis());
}

线程的强占Thread.join()

Thread的非静态方法join方法。需要在某一个线程下去调用这个方法。
如果在main线程中调用了t1.join(),那么main线程会进入到等待状态,需要等待t1线程全部执行完毕,再恢复到就绪状态等待CPU调度。
如果在main线程中调用了t1.join(2000),那么main线程会进入到等待状态,需要等待t1执行2s后, 再恢复到就绪状态等待CPU调度。如果在等待期间,t1已经结束了,那么main线程自动变为就绪状态等待CPU调度。

public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("t1:" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t1.start();for (int i = 0; i < 10; i++) {System.out.println("main:" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}if (i == 1) {try {t1.join(2000);} catch (InterruptedException e) {e.printStackTrace();}}}
}

守护线程Daemon

默认情况下,线程都是非守护线程。
JVM会在程序中没有非守护线程时,结束掉当前JVM。
主线程默认是非守护线程,如果主线程执行结束,需要查看当前JVM内是否还有非守护线程,如果没有JVM直接停止。

public static void main(String[] args) {Thread t1 = new Thread(() -> {for (int i = 0; i < 10; i++) {System.out.println("t1:" + i);try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();}}});t1.setDaemon(true);t1.start();
}

线程的等待和唤醒 Object.wait()&Object.notify()

可以让获取synchronized锁资源的线程通过wait方法进去到锁的等待池,并且会释放锁资源。
可以让获取synchronized锁资源的线程,通过notify或者notifyAll方法,将等待池中的线程唤醒,添加到锁池中。
notify随机的唤醒等待池中的一个线程到锁池。
notifyAll将等待池中的全部线程都唤醒,并且添加到锁池。
在调用wait方法和notify以及norifyAll方法时,必须在synchronized修饰的代码块或者方法内部才可以,因为要操作基于某个对象的锁的信息维护。

public class MiTest {public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {sync();}, "t1");Thread t2 = new Thread(() -> {sync();}, "t2");t1.start();t2.start();Thread.sleep(12000);synchronized (MiTest.class) {MiTest.class.notifyAll();}}public static synchronized void sync() {try {for (int i = 0; i < 10; i++) {if (i == 5) {MiTest.class.wait();}Thread.sleep(1000);System.out.println(Thread.currentThread().getName());}} catch (InterruptedException e) {e.printStackTrace();}}
}

线程的结束方式

线程结束方式很多,最常用就是让线程的run方法结束。无论是return结束,还是抛出异常结束都可以。

stop方法(不用)

强制让线程结束,无论你在干嘛,不推荐使用。但是,他确实可以把线程干掉。

public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {try {Thread.sleep(5000);} catch (InterruptedException e) {e.printStackTrace();}});t1.start();Thread.sleep(500);t1.stop();System.out.println(t1.getState());
}

使用共享变量(很少用)

这种方式用的也不多,有的线程可能会通过死循环来保证一直运行。
可以通过修改共享变量在破坏死循环,让线程退出循环,结束run方法。

static volatile boolean flag = true;public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (flag) {// 处理任务}System.out.println("任务结束");});t1.start();Thread.sleep(500);flag = false;
}

interrupt方式

共享变量方式。

public static void main(String[] args) throws InterruptedException {// 线程默认情况下, interrupt标记位:falseSystem.out.println(Thread.currentThread().isInterrupted());// 执行interrupt之后,再次查看打断信息Thread.currentThread().interrupt();// interrupt标记位:tureSystem.out.println(Thread.currentThread().isInterrupted());// 返回当前线程,并归位为false interrupt标记位:tureSystem.out.println(Thread.interrupted());// 已经归位了System.out.println(Thread.interrupted());// =====================================================Thread t1 = new Thread(() -> {while (!Thread.currentThread().isInterrupted()) { // 处理业务}System.out.println("t1结束");});t1.start();Thread.sleep(500);t1.interrupt();
}

通过打断WAITING或者TIMED_WAITING状态的线程,从而抛出异常自行处理。这种停止线程方式是最常用的一种,在框架和JUC中也是最常见的。

public static void main(String[] args) throws InterruptedException {Thread t1 = new Thread(() -> {while (true) {// 获取任务// 拿到任务,执行任务// 没有任务了,让线程休眠try {Thread.sleep(1000);} catch (InterruptedException e) {e.printStackTrace();System.out.println("基于打断形式结束当前线程");return;}}});t1.start();Thread.sleep(500);t1.interrupt();
}

wait和sleep的区别?

  • 单词不一样
  • sleep属于Thread类中的static方法,wait属于Object类的方法。
  • sleep属于TIMED_WAITING,自动唤醒;wait属于WAITING,需要手动唤醒。
  • sleep方法在持有锁时,执行,不会释放资源。wait在执行后,会释放锁资源。
  • sleep方法可以在持有锁或者不持有锁执行。wait方法必须在持有锁的时才能执行。

wait方法会将持有锁的线程从owner扔到waitSet集合中,这个操作是在修改ObjectMointor对象,如果没有持有synchronized锁的话,是无法操作ObjectMointor的。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.luyixian.cn/news_show_72521.aspx

如若内容造成侵权/违法违规/事实不符,请联系dt猫网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

软件质量测试中的健壮性测试是什么?一文和你说

当大多数人开车时&#xff0c;他们不会担心刹车失灵。当他们的孩子得到一个新玩具时&#xff0c;他们也不担心因故障受伤。事实上&#xff0c;大多数人在日常生活中根本不担心系统故障。 这是因为软件开发人员或质量控制工程师已经解决了质量问题。如果目标是交付高质量、可靠…

Win11安装软件报缺失.NET的解决方法

1.问题描述&#xff1a;安装软件时提示这个 2.解决方法&#xff1a; WinR 打开运行界面&#xff0c;输入control回车&#xff0c;打开控制面板 点击打开程序和功能 选择 启用或关闭Windows功能 --》勾选.NET Framework3.5...这一项&#xff0c;点击确定&#xff0c;如果电脑上…

学习Flask之五、数据库

学习Flask之五、数据库 数据库有组织的存贮应用数据。根据需要应用发布查询追踪特定部分。网络应用最常用的数据库是基于关系模式的&#xff0c;也称为SQL数据库&#xff0c;引用结构化查询语句。但是近年来&#xff0c;面向文档和键值的数据库&#xff0c;非正式的统称为NoSQ…

一文教你玩转 Apache Doris 分区分桶新功能|新版本揭秘

数据分片&#xff08;Sharding&#xff09;是分布式数据库分而治之 (Divide And Conquer) 这一设计思想的体现。过去的单机数据库在大数据量下往往面临存储和 IO 的限制&#xff0c;而分布式数据库则通过数据划分的规则&#xff0c;将数据打散分布至不同的机器或节点上&#xf…

全局组件和局部组件

全局组件第一种定义方法&#xff1a;A、创建自己的组件&#xff1a;Loading.vueB、在main.js文件中引入组件并注册import Vue from vue import App from ./App.vue import * as filters from ./filterimport quanjuzujian from ./components/quanjuzujian.vueVue.component(qua…

PowerJob容器的今生,容器是如何部署到Worker上,并正常运行的

这仅仅是一篇PowerJob源码分析的文章&#xff0c;但是也有一些java基础知识&#xff0c;在实践中学习效果更好&#xff0c;感兴趣就留下来交流一下吧。 上回书说到&#xff0c;这个powerjob容器是如何生成模板&#xff0c;如何上传到服务器上去&#xff0c;本回主要总结的是&am…

【踩坑指南】Stable Diffusion 服务器端部署笔记

文章目录下载github文件配置环境ckpt文件权重下载生成图像NSFW检查&#xff08;瑟图过滤&#xff09;下载github文件 https://github.com/CompVis/stable-diffusion 这个网址&#xff0c;下载压缩包解压&#xff0c;也可以用git clone下载 配置环境 这一步坑最多&#xff0c…

day32 多线程(上)

文章目录相关概念codeThreadTest01ThreadTest02 编写一个类&#xff0c;直接继承java.lang.Thread&#xff0c;重写run方法ThreadTest03 实现线程的第二种方法ThreadTest04 采用匿名内部类的方式ThreadTest05 获取线程名字ThreadTest06 sleep方法sleep面试题ThreadTest08 终止线…

游戏专用蓝牙耳机哪个牌子好?最好的游戏蓝牙耳机品牌排行

近年来&#xff0c;随着越来越多手机取消3.5mm耳机孔&#xff0c;真无线耳机也逐渐流行起来&#xff0c;随着国内的手机品牌越来越多&#xff0c;真无线耳机的品类逐渐增多&#xff0c;面向游戏用户的游戏模式也出现了&#xff0c;下面我们来看看以下几款游戏专用的蓝牙耳机。 …

10 种主数据模型设计示例分享,推荐收藏

主数据模型是主数据管理的基础&#xff0c;一个完整的、可扩展的、相对稳定的主数据模型对于主数据管理的成功起着重要的作用。规划、创建主数据模型的过程&#xff0c;是梳理主数据管理体系的过程&#xff0c;目的是建立一个良好的资源目录结构&#xff0c;划分合理的资源粒度…

Leetcode力扣秋招刷题路-0088

从0开始的秋招刷题路&#xff0c;记录下所刷每道题的题解&#xff0c;帮助自己回顾总结 88. 合并两个有序数组 给你两个按 非递减顺序 排列的整数数组 nums1 和 nums2&#xff0c;另有两个整数 m 和 n &#xff0c;分别表示 nums1 和 nums2 中的元素数目。 请你 合并 nums2 …

我说我为什么抽不到SSR,原来是这段代码在作祟...

本文是龚国玮所写&#xff0c;熊哥有所新增修改删减&#xff0c;原文见文末。 我说我为什么抽不到SSR&#xff0c;原来是加权随机算法在作祟 阅读本文需要做好心理准备&#xff0c;建议带着深究到底的决心和毅力进行学习&#xff01; 灵魂拷问 为什么有 50% 的几率获得金币&a…

同一局域网的不同主机使用共享文件夹通信(仅限于不同Windows主机之间的通信)

1、新建共享文件夹 我们新建一个文件夹 Server-Share&#xff0c;右键点击“ 属性 ” 选择“everyone”&#xff0c;即允许当前局域网下的所有用户访问这个共享文件夹 此时该文件夹面向当前局域网是公开的。 2、服务器访问共享文件夹 (1) 查看当前电脑的IP IP地址可以唯一标…

企业为什么需要数据可视化报表

数据可视化报表是在商业环境、市场环境已经改变之后&#xff0c;发展出来为当前企业提供替代解决办法的重要方案。而且信息化、数字化时代&#xff0c;很多企业已经进行了初步的信息化建设&#xff0c;沉淀了大量业务数据&#xff0c;这些数据作为企业的资产&#xff0c;是需要…

园区数字化转型必不可少的助推器:快鲸智慧园区系统

数字化浪潮下&#xff0c;园区数字化转型已成必然趋势。可大多数人在讨论智慧园区的时候&#xff0c;更多聚焦在技术上&#xff0c;却忽略了一个关键点&#xff0c;就是打造智慧园区最终的结果导向是提高业务信息化水平&#xff0c;进而达到集约高效、提质增效、节能降耗的可持…

干货复试详细教程——从联系导师→自我介绍的复试教程

文章目录联系导师联系之前的准备联系导师注意自我介绍教育技术领域通用的复试准备其他补充联系导师 确定出分和自己能进复试以后联系。 分两类 科研技能型 低调&#xff0c;如实介绍&#xff0c;不吹不水。就算你很牛啥都会手握核心期刊论文也不太狂 学霸高分型 不要自卑&…

STM32-CAN配置与库函数解析,实现环回模式通信

STM32-CAN配置与库函数解析 CAN总线介绍&#xff1a;https://blog.csdn.net/weixin_46251230/article/details/129147612 STM32-CAN控制器介绍&#xff1a;https://blog.csdn.net/weixin_46251230/article/details/129150872 STM32CubeMx配置 因为bxCAN是挂载在APB1总线上的…

【学习总结】相机与IMU标定一:Kalibr论文

论文&#xff1a;2013IROS论文&#xff0c;Unified Temporal and Spatial Calibration for Multi-Sensor Systems&#xff0c;是Kalibr工具的参考论文之一。介绍了如何进行IMU与相机标定。 参考的一篇资料&#xff1a;知乎&#xff1a;超全汇总&#xff01;多传感器离线/在线时…

新建微服务模块Maven子工程

gitegg-cloud是微服务框架&#xff0c;整体功能是非业务相关的基础功能&#xff0c;在实际业务开发过程中需要新建微服务的业务模块&#xff0c;根据业务的整体规划&#xff0c;设计新建Maven子工程。   下面以常用的电商项目举例新建Maven子工程&#xff0c;电商项目一般包含…

VIIRS-NPP夜间灯光遥感数据下载和预处理

VIIRS-NPP夜间灯光遥感数据下载和预处理 月和年合成产品下载网站 日数据下载网站 一、下载shp掩膜文件 下载好月合成产品后&#xff0c;在这个网站上下载矢量地图&#xff0c; 点击复制按钮&#xff0c;来到这个网站&#xff0c;ctrl v粘贴 点击右上角Export&#xff0c;…