线程与线程池的理解

2023-10-30

1,什么是线程,线程和进程的区别是什么

线程,程序执行流的最小执行单位,是行程中的实际运作单位,进程简单来说,一个应用程序的运行就可以被看做是一个进程,而线程,是运行中的实际的任务执行者,进程中包含了多个可以同时运行的线程。

2,线程的生命周期

(1)是用new Thread()的方法新建一个线程,在线程创建完成之后,线程就进入了就绪(Runnable)状态,此时创建出来的线程进入抢占CPU资源的状态,(2)当线程抢到了CPU的执行权之后,线程就进入了运行状态(Running),(3)当该线程的任务执行完成之后或者是非常态的调用的stop()方法之后,线程就进入了死亡状态。

当面对以下几种情况的时候,容易造成线程阻塞,(1)当线程主动调用了sleep()方法时,线程会进入则阻塞状态,(2)当线程中主动调用了阻塞时的IO方法时,这个方法有一个返回参数,当参数返回之前,线程也会进入阻塞状态,(3)当线程进入正在等待某个通知时wait(),会进入阻塞状态。那么,为什么会有阻塞状态出现呢?我们都知道,CPU的资源是十分宝贵的,所以,当线程正在进行某种不确定时长的任务时,Java就会收回CPU的执行权,从而合理应用CPU的资源。我们根据图可以看出,线程在阻塞过程结束之后,会重新进入就绪状态,重新抢夺CPU资源。这时候,如何跳出阻塞过程呢?又以上几种可能造成线程阻塞的情况来看,都是存在一个时间限制的,当sleep()方法的睡眠时长过去后,线程就自动跳出了阻塞状态,第二种和第三种则是在返回了一个参数之后,在获取到了等待的通知时,就自动跳出了线程的阻塞过程

3,什么是单线程和多线程?

单线程,即只有一条线程在执行任务。多线程,创建多条线程同时执行任务。需要理解并行与并发 ,并行:多个处理器或多核处理器同时处理多个任务。并发:多个任务在同一个 CPU 核上,按细分的时间片轮流(交替)执行,从逻 辑上来看那些任务是同时执行,其实不是同时进行的,而是交替进行的,而由于CPU的运算速度非常的快,会造成我们的一种错觉,就是在同一时间内进行了多种事情,这也是我们重点要学习的地方——高并发

 

 并发 = 两个队列和一台咖啡机。并行 = 两个队列和两台咖啡机

4,线程的常用方法

  1. 停止线程:stop()、destory()、但是不推荐使用,建议使用一个标志位停止下来flag=false
  2. 线程休眠:Thread.sleep()、指定当前线程阻塞多少毫秒,不会释放锁,存在InterruptedExceptiony中断异常,到时会自动进入就绪状态,给其他线程运行机会时不考虑线程的优先级。
  3. 线程礼让:Thread.yield(),让当前执行的线程转为就绪状态,不会阻塞,礼让不一定成功,给相同优先级或更高优先级的线程以运行的机会
  4. 线程合并:join(),可以理解成插队,待此线程执行完后在执行其他线程,其他线程阻塞
  5. 线程优先级:setPriority(int xx),getPriority(),Java提供一个线程调度器来监控程序中启动后进入就绪状态的所有线程,线程调度器按照优先级决定应该调度哪个线程来执行。优先级范围(1~10)默认是5,优先级低只是意味着获得调度的概率低.并不是优先级低就不会被调用了.这都是看CPU的调度
  6. 守护线程:setDaemon(boolean),线程分守护线程(虚拟机不用等待执行完毕比如GC线程)与用户线程(虚拟机必须确保用户线程执行完毕比如main方法),
  7. 线程通信:wait(),线程一直等待,直到其他线程通知,与sleep不同他会释放锁给其他线程去竞争。notify(),唤醒一个处于等待状态的线程,由JVM确定唤醒哪个线程,而且与优先级无关。notifyAll(),唤醒同一个对象上所有调用wait()方法的线程,优先级别高的线程优先调度。这三个方法均是Object类的方法,都只能在同步方法或者同步代码块中使用,否则会抛出异常legalMonitorStateException

5,线程池技术 

在一个应用程序中,我们需要多次使用线程,也就意味着,我们需要多次创建并销毁线程。而创建并销毁线程的过程势必会消耗内存。而在Java中,内存资源是及其宝贵的,所以,我们就提出了线程池的概念那么,我们应该如何创建一个线程池那?Java中已经提供了创建线程池的一个工具类:Executors专门创建线程池,下面来看下常见的四种线程池

//Executors工具类创建线程池
public class ThreadPoolDemo {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService singleThreadExecutor  = Executors.newSingleThreadExecutor();//创建一个单线程化的线程池
        ExecutorService fixedThreadPool  = Executors.newFixedThreadPool(5);//创建一个可重用固定个数的线程池
        try {
            for (int i = 0; i <10 ; i++) {
                //使用了线程池之后使用线程池来创建线程
                fixedThreadPool.execute(()->{
                    System.out.println(Thread.currentThread().getName());
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完 程序结束 关闭线程池
            fixedThreadPool.shutdown();
        }

        ExecutorService cachedThreadPool  = Executors.newCachedThreadPool();// 创建一个可缓存线程池,
        for (int i = 0; i < 10; i++) {
            // sleep可明显看到使用的是线程池里面以前的线程,没有创建新的线程
            Thread.sleep(1000);
            cachedThreadPool.execute(() -> {
                // 打印正在执行的缓存线程信息
                System.out.println(Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            });
        }

        //创建一个定长线程池,支持定时及周期性任务执行
        ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);
        scheduledThreadPool.scheduleAtFixedRate(()->{
                System.out.println("延迟1秒后每1秒执行一次");
        }, 1, 3, TimeUnit.SECONDS);
    }

但是工作中我们肯定是不能采用这种方式去创建线程池的,因为使用工具类创建不安全,而我们创建时,一般使用它的子类:ThreadPoolExecutor,为什么不安全,我们来看下阿里巴巴开发手册上的说明

因为工具创建的一些线程池默认值都太大,可能会导致堆溢出OOM,我们来看下通过ThreadPoolExecutor手动创建线程池需要的参数

 其实这个线程的参数与银行办理业务一模一样

 假如有一天去银行办理业务,一开始人不多,银行5个窗口只开放2个窗口处理业务,(线程的大小池),慢慢的人越来越多,就得在候客区等候(阻塞队列),这时来的人已经把候客区排慢,银行只能开放另外三个窗口(最大核心线程池大小被触发),后来人越来越多,候客区与窗口满了在来的我们就有4中拒绝策略,下面来看下我们ThreadPoolExecutor创建线程与四种拒绝策略

public class ThreadPoolDemo2 {
    public static void main(String[] args) {
        //AbortPolicy  默认的拒绝策略  队列满了还有人进来,不处理这个人,抛出异常RejectedExecutionException
        //CallerRunsPolicy  直接调用execute来执行当前任务哪条线程执行的这个方法就回那里
        //DiscardPolicy 如果队列满了,丢掉任务,不会抛出异常
        //DiscardOldestPolicy 如果队列满了,尝试去和最早的竞争,如果第一个刚好执行完他就会跟着执行,如果没有执行完则丢掉
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(2,
                5,
                3,
                TimeUnit.SECONDS,
                new LinkedBlockingDeque<>(3),
                Executors.defaultThreadFactory(),
                new ThreadPoolExecutor.DiscardOldestPolicy()
                );

        try {
            for (int i = 0; i <9 ; i++) {
                //使用了线程池之后使用线程池来创建线程
                threadPoolExecutor.execute(()->{
                    System.out.println(Thread.currentThread().getName()+"ok");
                });
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //线程池用完 程序结束 关闭线程池
            threadPoolExecutor.shutdown();
        }
    }
}

有很多人会问我们这个最大核心线程池大小怎么定义,这里就得分两种了

CPU密集型:cpu几核就是几 可以保持cpu的效率最高 用获取运行时的一个类Runtime去获取几核Runtime.getRuntime().availableProcessors()。计算公式=CPU核数 + 1

IO密集型:io非常占用资源 判断你程序十分耗io的线程的数目,假如一个程序 15个大型任务 >15就可以了  一般是两倍就可以了。计算公式=CPU核数 * 2

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

线程与线程池的理解 的相关文章

随机推荐

  • 红宝书第一章总结

    一个完整的JavaScript实现由下列三个不同的部分组成 核心 ECMAScript 文档对象模型 DOM 浏览器对象模型 BOM JavaScript ECMAScript DOM BOM
  • keepalived学习记录:对其vip漂移过程采用gdb跟踪

    对其vip漂移过程采用gdb跟踪 keepalived工具 主要功能 产生vip漂移过程两种情况 gdb调试常用命令 gdb调试时打到的函数栈 供学习参考 系列文章 keepalived学习记录 对其vip漂移过程采用gdb跟踪 Keepa
  • Akka 实践(二)- java开发demo1

    下面就Akka的一个java demo来说明Akka 如何运作的 1 首先下载 Akka的官方包 下载地址为 http akka io downloads 我下载的是 Akka的2 3 15 解压这个压缩包 准备拷贝相关的jar包到自己的工
  • poj 1953 dp(无连续1的01串个数)

    题意 问长度为n的无连续1出现的01串有多少个 思路 简单dp 思路1 dp i 0 表示以0结尾长度为i的串的个数 dp i 1 表示以1结尾长度为i的串的个数 则转移方程为dp i 0 dp i 1 0 dp i 1 1 和dp i 1
  • 算数基本定理求约数个数

    题目 最多约数问题 正整数x 的约数是能整除x的正整数 其约数的个数记为div x 例如div 10 4 设a 和b 是两个正整数 找出a 和b 之间约数个数最多的数x 的约数个数 样例输入 1 36 样例输出 9 算数基本定理 又称为正整
  • 自动化接口测试-第02天-接口用例设计思路、单接口用例、业务场景用例、postman

    更多功能测试以及全套学习路线图均在专栏 戳进去领取 系列文章目录 身为开发必知必会的Linux Linux远程连接 命令的使用 Linux命令大全 唯一以案例详解文 持续更新中 Linux命令大全以及数据库 唯一以案例详解文 已完结 Web
  • yield和join方法的使用。

    join方法用线程对象调用 如果在一个线程A中调用另一个线程B的join方法 线程A将会等待线程B执行完毕后再执行 yield可以直接用Thread类调用 yield让出CPU执行权给同等级的线程 如果没有相同级别的线程在等待CPU的执行权
  • 【笔记】sass

    sass入门 1 可以定义变量 2 嵌套 3 导入其他sass文件 最后编译为一个CSS文件 4 mixin定义一些代码片段 且可传参数 include 5 extend组合代码声明 6 运算 7 颜色函数color darken link
  • 存储卡的使用方法大全

    存储卡的使用方法大全 现在购买诺基亚手机 尤其是其智能手机的朋友是越来越多了 与其他品牌的机型相比 诺基亚的手机有一个最大的优点 就是支持储存卡内存扩充的机型比较多 这让大家在使用中不必再担心手机容量的问题 这也体现了诺基亚 科技以人为本
  • 关于 ubuntu18.04 机械革命 RTX2060 解决分屏显示问题

    本人小白 如有错漏 欢迎批评指正 同时感谢之前发表过以及以后会在CSDN上发表自己学习经验的同志们 问题背景 本人使用的是 机械革命笔记本电脑 安装双系统 win10 以及 ubuntu18 04 在使用过程中 遇到了 分屏黑屏 将鼠标移动
  • NAT穿透的工作原理

    一 引言 1 1 背景 IPv4地址短缺 引入NAT 全球IPv4地址早已不够用 因此人们发明了NAT 网络地址转换 来缓解这个问题 简单来说 大部分机器都使用私有IP地址 如果它们需要访问公网服务 那么 出向流量 需要经过一台NAT设备
  • Pytest+Unittest+Git+Jenkins企业级CICD自动化测试平台建设方案

    随着持续集成的引入 项目中的自动化测试用例越来越多 每轮执行所消耗的时间也越来越久 要提高自动化测试用例执行的效率 以下几点是需要考虑的根本点 1 公司项目的交付策略如何 首先 测试团队服务于公司项目 因此我们必须根据公司项目的交付策略做对
  • SSL证书有什么用?

    SSL证书提供了一种在互联网上身份验证的方式 是用来标识和证明双方身份的数字信息文件 使用SSL证书的网站 可以保证用户和服务器间信息交换的保密性具有不可窃听 不可更改 不可否认 不可冒充的功能 45 113 203 1 45 113 20
  • 怎么用EF框架进行增删改查

    EF框架 1 EF是什么 2 EF的框架模式 3 EF框架优缺点 4 进行增删改查 4 1 新增 4 1 1 直接新增 4 1 2 改变状态新增 4 1 3 批量新增 4 2 删除 4 2 1 删除 先查询后删除 删除ID为6的 4 2 2
  • java jsch_java 利用jsch端口转发 建立连接

    其他代码都一样 参照 使用JSch完成 ssh隧道建立 只有获取连接的部分不相同 代码如下 端口转发 链接两层 public boolean connect throws JSchException try session ssh getS
  • Android NDK C++开发注意事项总结

    相信Android开发者都喜欢用C 编写一些高效的应用 有关Android NDK的C 开发相关知识总结如下 从Android NDK r5开始支持了STL Port 在这个版本开始就可以使用部分STL库的功能了 比如说vector str
  • thumbnailator压缩图片并存至Excel单元格代码

    文章目录 依赖 压缩图片工具类 存至excel的转化器 Excel导入导出的数据类 将数据库实体转化为excel实体 依赖
  • Python 基于 Django 的学生成绩管理系统,可视化界面

    1简介 对于学生成绩管理系统 充分运用现代化的信息技术手段 对于学生成绩信息管理发展的趋势就是信息化 信息化时代下的信息管理 需要深化信息管理体制与手段的改革 充分运用信息化手段来全方位的进行学生成绩管理系统工作 构建学生成绩管理系统 实现
  • Unity 中检测射线穿过的所有的物体

    在开发中 有个需求 射线要检测所有穿过的物体 代码如下 using UnityEngine public class HitCollider MonoBehaviour public float raycastDistance Mathf
  • 线程与线程池的理解

    1 什么是线程 线程和进程的区别是什么 线程 程序执行流的最小执行单位 是行程中的实际运作单位 进程简单来说 一个应用程序的运行就可以被看做是一个进程 而线程 是运行中的实际的任务执行者 进程中包含了多个可以同时运行的线程 2 线程的生命周