• Java多线程详解


     🐠 🐠 欢迎来到茶色岛的神秘岛屿,在这里你将收获Java多线程的奥秘👍👍

     活动地址:CSDN21天学习挑战赛

     ​学习的最大理由是想摆脱平庸,早一天就多一份人生的精彩;迟一天就多一天平庸的困扰。各位小伙伴,如果您:
    想系统/深入学习某技术知识点…
    一个人摸索学习很难坚持,想组团高效学习…
    想写博客但无从下手,急需写作干货注入能量…
    热爱写作,愿意让自己成为更好的人…


    欢迎参与CSDN学习挑战赛,成为更好的自己,请参考活动中各位优质专栏博主的免费高质量专栏资源(这部分优质资源是活动限时免费开放喔~),按照自身的学习领域和学习进度学习并记录自己的学习过程。您可以从以下3个方面任选其一着手(不强制),或者按照自己的理解发布专栏学习作品,参考如下:


    前言

    Hello,大家好✋✋,我是茶色岛,一位已经在csdn呆了将近一年的博主,本篇文章将为大家讲解Java多线程,希望会对你们有所帮助,同时也希望可以收获大家的喜爱,点个关注🎉🎉,谢谢大家。

    一、基本概念:程序、进程、线程

    1.程序(program):

    为完成特定任务、用某种语言编写的一组指令的集合。

    即指一段静态的代码,静态对象

    2.进程(process)

    是程序的一次执行过程,或是正在运行的一个程序。

    是一个动态的过程:有它自身的产生、存在和消亡的过程

    3.线程(thread)

    进程可进一步细化为线程,是一个程序内部的一条执行路径

    • 若一个进程同一时间 并行执行多个线程,就是支持多线程的
    • 一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。
    • 单核CPU,其实是一种假的多线程,如果是多核的话,才能更好的发挥多线程的效率
    • 并行与并发
       1.并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。
       2.并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事

    何时需要多线程

    • 程序需要同时执行两个或多个任务。
    • 程序需要实现一些需要等待的任务时,如用户输入、文件读写操作、网络操作、搜索等。
    • 需要一些后台运行的程序时。

    二、线程的创建和使用

    1.Thread类

    程序运行多个线程通过java.lang.Thread类来体现

    • 每个线程都是通过某个特定Thread对象的run()方法来完成操作的,经常把run()方法的主体称为线程体
    • 通过该Thread对象的start()方法来启动这个线程,而非直接调用run()

     构造器

    • Thread() :创建新的Thread对象
    • Thread(String threadname): :创建线程并指定线程实例名
    • Thread(Runnable target) :指定创建线程的目标对象,它实现了Runnable接口中的run方法
    • Thread(Runnable target, String name) :创建新的Thread对象

    2.API 中创建 线程的两种方式

    方式一: 继承Thread类
    1) 定义子类继承Thread类。
    2) 子类中重写Thread类中的run方法。
    3) 创建Thread子类对象,即创建了线程对象。
    4) 调用线程对象start方法:启动线程,调用run方法。

     方式二:实现Runnable 接口
    1) 定义子类,实现Runnable接口。
    2) 子类中重写Runnable接口中的run方法。
    3) 通过Thread类含参构造器创建线程对象。
    4) 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。
    5) 调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

    Thread 类的有关方法

    • void start(): 启动线程,并执行对象的run()方法
    • run(): 线程在被调度时执行的操作
    • String getName(): 返回线程的名称
    • void setName(String name):设置该线程名称
    • static Thread currentThread(): 返回当前线程。在Thread子类中就
    • 是this,通常用于主线程和Runnable实现类
    • static void yield(): : 线程让步(暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法)
    • join() : 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止(低优先级的线程也可以获得执行)
    • static void sleep(long millis) :(指定时间:毫秒)令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。抛出InterruptedException异常
    • stop(): 强制线程生命期结束,不推荐使用
    • boolean isAlive(): : 返回boolean,判断线程是否还活着

    三、线程的生命周期

    四、线程的同步

    1.问题的原因:

    当多条语句在操作同一个线程共享数据时,一个线程对多条语句只执行了一部分,还没有执行完,另一个线程参与进来执行。导致共享数据的错误。

    2. 解决办法:

    对多条操作共享数据的语句,只能让一个线程都执行完,在执行过程中,其他线程不可以
    参与执行。

    3.具体实现方法:

    1.使用Synchronized维持线程同步

    1. 同步代码块 :
    synchronized ( 对象){
    // 需要被同步的代码;
    }
    2. synchronized 还可以放在方法声明中,表示 整个 方法为 同步方法 。
    例如:
    public synchronized void show (String name){
    ….
    }

    2.使用 Lock( 锁)维持线程同步
     

    1. class A{
    2. private final ReentrantLock lock = new ReenTrantLock();
    3. public void m(){
    4. lock.lock();
    5. try{
    6. // 保证线程安全的代码;
    7. }
    8. finally{
    9. lock.unlock();
    10. }
    11. }
    12. }

    synchronized 与 Lock 的对比

    1.Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是
    隐式锁,出了作用域自动释放
    2. Lock只有代码块锁,synchronized有代码块锁和方法锁
    3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有
    更好的扩展性(提供更多的子类)

    优先使用顺序:
    Lock -> 同步代码块(已经进入了方法体,分配了相应资源) ->同步方法(在方法体之外)

    4.同步机制中的锁

    •  必须确保使用同一个资源的 多个线程共用一把锁
    •  一个线程类中的所有静态方法共用同一把锁(类名.class),所有非静态方法共用同一把锁(this),同步代码块(指定需谨慎)

    5.释放锁的操作

    • 当前线程的同步方法、同步代码块执行结束。
    • 当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行。
    • 当前线程在同步代码块、同步方法中出现了未处理的Error或Exception,导致异常结束。
    • 当前线程在同步代码块、同步方法中执行了线程对象的wait()方法,当前线程暂停,并释放锁

    6.不会释放锁的操作

    • 线程执行同步代码块或同步方法时,程序调用Thread.sleep()、Thread.yield()方法暂停当前线程的执行
    • 线程执行同步代码块时,其他线程调用了该线程的suspend()方法将该线程挂起,该线程不会释放锁(同步监视器)。应尽量避免使用suspend()和resume()来控制线程

    五、线程的通信

    执行wait() 与 与 notify() 和 和 notifyAll()

    • 这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会java.lang.IllegalMonitorStateException异常。
    • 因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。

    wait() 方法

    • 在当前线程中调用方法: 对象名.wait()
    • 使当前线程进入等待(某对象)状态 ,直到另一线程对该对象发出 notify(或notifyAll) 为止。
    • 调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)
    • 调用此方法后,当前线程将释放对象监控权 ,然后进入等待
    • 在当前线程被notify后,要重新获得监控权,然后从断点处继续代码的执行

    notify()/notifyAll()

    • 在当前线程中调用方法: 对象名.notify()
    • 功能:唤醒等待该对象监控权的一个/所有线程。
    • 调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

    六、JDK5.0新增线程创建方式

    1.新增方式一:实现Callable 接口

    • 相比run()方法,可以有返回值
    • 方法可以抛出异常
    • 支持泛型的返回值
    • 需要借助FutureTask类,比如获取返回结果

    2.新增方式二:使用线程池

    线程池相关API:ExecutorService 和 Executors

    总结


    🐠 🐠 以上就是今天要讲的java线程,本文仅仅简单介绍了线程的基础知识。

    长路漫漫,希望我们一起努力。

    让天下没有难学的技术。

  • 相关阅读:
    数字经济发展,助力企业数字化转型升级
    IDEA中如何使用debug调试项目 一步一步详细教程
    手动下载新版的TCGA数据也是可以用TCGAbiolinks包整理的
    技术变现整体流程
    从零开始学WEB前端——HTML理论讲解
    【React】unmountComponentAtNode卸载组件
    2022第8届中国大学生程序设计竞赛CCPC桂林站, 签到题4题
    【NLP】使用递归神经网络对序列数据进行建模 (Pytorch)
    SnakeYaml反序列化分析
    springboot整合tkmybatis和tkmybatis 逆向工程
  • 原文地址:https://blog.csdn.net/weixin_62275996/article/details/126329038