线程池原理及使用

news2024/11/29 12:54:49

线程池继承关系

1.为什么使用线程池?

        1.反复创建线程开销大;

        2.过多线程会占用太多内存(执行任务易出现“内存溢出”);

        3.加快程序响应速度;

        4.合理利用CPU和内存;

        5.统一管理线程;

2.创建和停止线程池

2.1.线程池参数解释

1.keppAliveTime

        如果线程池当中的线程数量大于“corePoolSize”,当这些多余的线程空闲时间超过keepAliveTime时它们就会被回收;

2.threadFactory

        新的线程是由threadFactory创建的默认使用Executors.defaultThreadFactory(),创建出来的线程都是用户线程且属于同一个线程组优先级都默认为5,如果自己指定threadFactory那么就可以改变线程名、线程组、优先级、是否是守护线程等信息;

3.workQueue

        常见三种队列类型:

        1.直接交换队列(SynchronousQueue)

                即队列不存储任务,直接将任务发往线程池执行;

        2.无界队列(LinkedBlockingQueue)

                即队列存储任务数量没有上限(有内存溢出风险);

        3.有界队列(ArrayBlockingQueue)

                即队列中指定存储多少个任务;

2.2.线程池添加线程规则

        1.如果线程数小于corePoolSize就创建一个新线程去运行新任务;

        2.如果线程数大于等于corePoolSize但小于最大线程数(maxPoolSize)则将任务放入队列;

        3.如果队列中任务已满,并且线程数小于maxPoolSize则创建一个新线程来执行任务(队列塞满才尝试去扩容);

        4.如果队列已满且线程数大于maxPoolSize则拒绝该任务添加到线程池;

2.3.使用工具类创建线程池的四种方式

如下四种创建线程池各参数情况:

        1.newFixedThreadPool

                原理:

                        corePoolSize = maxPoolSize = 传入线程个数;

                        使用队列:LinkedBlockingQueue(无界队列容量无上限);

                固定长度线程池,newFixedThreadPool由于使用的工作队列是LinkedBlockingQueue是没有容量上限的,所以当请求越来越多并且来不及处理时会出现请求堆积造成大量内存被占用出现“内存溢出”问题;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * 演示使用newFixedThreadPool
 *      该线程池使用的是LinkedBlockingQueue(无界队列,即队列中可以存放任意多任务有“内存溢出”风险)
 */
public class FixedThreadPool {
    public static void main(String[] args) {
        // 创建固定数量线程池
        ExecutorService pool = Executors.newFixedThreadPool(4);
        // 假设需执行1000个任务,每个任务休眠200ms再打印出线程名
        for (int i = 1; i <= 1000 ; i++) {
            pool.execute(new Task());
        }
    }

    // 任务类
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:" + Thread.currentThread().getName());
        }
    }
}

       

        2.newSingleThreadExecutor

                原理:

                        corePoolSize = maxPoolSize = 1

                        使用队列:LinkedBlockingQueue(无界队列容量无上限)

                单线程线程池(该线程池只有一个线程)其原理和newFixedThreadPool线程池相同,当请求堆积且来不及处理时易出现“内存溢出”问题;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * 演示使用newSingleThreadExecutor(单线程线程池)
 * 该线程池使用的是LinkedBlockingQueue(无界队列,即队列中可以存放任意多任务有“内存溢出”风险)
 */
public class SingleThreadPool {
    public static void main(String[] args) {
        // 创建单线程线程池
        ExecutorService pool = Executors.newSingleThreadExecutor();
        // 假设需执行1000个任务,每个任务休眠200ms再打印出线程名
        for (int i = 1; i <= 1000 ; i++) {
            pool.execute(new SingleThreadPool.Task());
        }
    }

    // 任务类
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:" + Thread.currentThread().getName());
        }
    }
}

        3.CachedThreadPool

                可缓存线程池使用直接交换队列,队列中不存储任务当任务来了判断当前线程数是否小于Integer.MAX_VALUE,若小于则直接交给线程池去执行,CachedThreadPool具有回收多余线程的功能(默认情况下空闲60s的线程会被自动回收),CachedThreadPool的弊端在于maxPoolSize的值为Integer.MAX_VALUE,这可能会创建非常多的线程可能会导致“内存溢出”问题;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * 演示使用CachedThreadPool(可缓存线程池)
 *      该线程池使用的是SynchronousQueue(直接交换队列,即队列中不存储任务当任务来了判断当前线程数是否小于Integer.MAX_VALUE,若小于则直接交给线程池去执行)
 */
public class CachedThreadPool {
    public static void main(String[] args) {
        ExecutorService pool = Executors.newCachedThreadPool();
        for (int i = 1; i <= 1000 ; i++) {
            pool.execute(new Task());
        }
    }

    // 任务类
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:" + Thread.currentThread().getName());
        }
    }
}

        4.ScheduledThreadPool

                支持定期及周期性任务执行的线程池(内部使用了延迟队列);

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
 * 演示使用ScheduledThreadPool(周期性执行线程池)
 */
public class ScheduledThreadPool {
    public static void main(String[] args) {
        ScheduledExecutorService pool = Executors.newScheduledThreadPool(10);
        // 用法一(延迟三秒后执行)
        pool.schedule(new Task(),3,TimeUnit.SECONDS);

        System.out.println("====================    分割线    ====================");

        // 用法二(周期性执行(首次执行延迟一秒后执行,后面每间隔3秒周期性执行))
        pool.scheduleAtFixedRate(new Task(),1,3,TimeUnit.SECONDS);
    }

    // 任务类
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:" + Thread.currentThread().getName());
        }
    }
}

2.4.如何设置线程池线程数量

        1.CPU密集型:例如:加密,计算等高耗CPU的操作,最佳线程数为CPU核心数的1-2倍左右;

        2.耗时IO型:例如:读写数据库,文件,网络读写操作,最佳线程数一般大于CPU核心数的很多倍,以JVM线程监控显示繁忙情况为依据确保线程空闲能够衔接上;

        3.公式:CPU核心数 * (1 + 平均等待时间/平均工作时间);

2.5.停止线程池相关方法

        1.shutdown

       shutdown命令用于停止线程池,线程池调用shutdown命令并不是立即去停止线程池的,使用shutdown命令线程池会等到正在执行的任务和队列中的任务都执行完毕后才会停止,当线程池调用shutdown命令后对于新提交过来的任务线程池会抛出“RejectedExecutionException”异常去拒绝接收新任务;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * 演示停止线程池方法 - shutdown命令
 */
public class shutDown {
    public static void main(String[] args) throws InterruptedException {

        ExecutorService pool = Executors.newFixedThreadPool(10);
        // 提交1000个任务
        for (int i = 1; i <= 1000 ; i++) {
            pool.execute(new Task());
        }

        // 休眠三秒让1000个任务全部已经提交给线程池
        TimeUnit.SECONDS.sleep(3);
        pool.shutdown();
        boolean shutdown = pool.isShutdown();
        System.out.println("isShutdown()用于判断当前线程池是否执行了shutdown或shutdownNow命令:" + shutdown);
        boolean terminated = pool.isTerminated();
        System.out.println("isTerminated()用于判断当前线程池是否真正停止(正在执行的任务 + 队列中任务)都执行完毕且已调用shutdown或shutdowNow命令: = " + terminated);
    }

    // 任务
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

        2.shutdownNow

      shutdownNow命令用于停止线程池,当线程池调用shutdownNow命令时会立即停止线程池执行shutdownNow命令会做两件事;

        1.线程池会对正在执行的线程发出中断信号去中断当前正在执行的线程;

        2.对于在队列中等待执行的任务shutdownNow命令返回值会返回在队列中的所有任务,一般执行shutdownNow命令时我们需要对返回值做处理(比如将这些队列中的任务存储到数据库中或使用其它线程池去执行队列中的任务);

import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

public class ShutdownNow {
    public static void main(String[] args) throws InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(10);

        // 提交100个任务
        for (int i = 1; i <= 100 ; i++) {
            pool.execute(new Task());
        }
        // 休眠2秒让100个任务全部添加到线程池中去
        TimeUnit.SECONDS.sleep(2);

        // 队列中剩余任务个数
        List<Runnable> taskQueue = pool.shutdownNow();
        System.out.println("队列中剩余任务个数: " + taskQueue.size());
    }

    // 任务
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                System.out.println("线程池已执行shutdownNow命令,当前线程:" + Thread.currentThread().getName() + " 被中断了...");
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

        3.isShutdown

判断线程池是否已停止,当调用shutdown()或shutdownNow() 方法后则此方法返回true;           

具体使用见本章shutdown命令演示代码;

        4.isTerminated

      判断线程池是否完全停止,当调用shutdown()或shutdownNow() 方法后且线程池中正在执行的任务及队列中任务都已经执行完毕则此方法返回true;

具体使用见本章shutdown命令演示代码;

        5.awaitTermination

     等待指定时间后检测当前线程池是否已关闭,一般情况下会和shutdown方法组合使用;

import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
/**
 * 演示:awaitTermination命令(等待指定时间后检测当前线程池是否已关闭)
 */
public class AwaitTermination {

    public static void main(String[] args) throws InterruptedException {
        ExecutorService pool = Executors.newFixedThreadPool(10);

        for (int i = 1; i <= 1000 ; i++) {
            pool.execute(new Task());
        }

        // 等待指定时间后检测当前线程池是否已关闭
        boolean flag = pool.awaitTermination(3, TimeUnit.SECONDS);
        System.err.println(flag);
    }

    // 任务
    static class Task implements Runnable{
        @Override
        public void run() {
            try {
                TimeUnit.MILLISECONDS.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName());
        }
    }
}

2.6.线程池拒绝策略

1.线程池何时拒绝新任务

        1.1.线程池调用shutdown或shutdownNow关闭线程池方法后还有新任务提交过来,此时线程池会抛出“RejectedExecutionException”异常去拒绝新任务;

        1.2.线程池最大线程数和工作队列到达上限时会拒绝新任务;

2.线程池的四种拒绝策略

        1.AbortPolicy

                当有新任务想要添加到线程池时直接抛出异常;

        2.DiscardPolicy

                当有新任务想要添加到线程池时不处理也不添加任务到工作队列(什么也不做);

        3.DiscardOldestPolicy

                当有新任务想要添加到线程池时抛弃存放在工作队列中最久的任务并添加新任务到队列;

        4.CallerRunsPolicy        

                当有新任务想要添加到线程池时将新任务交给当前线程去执行(即主线程去执行)。这样做的好处在于不丢弃已有工作队列中的任务,同时给线程池处理已有工作队列中任务的时间,若这段时间线程池处理完一些任务后续再往线程池中添加新任务时就能成功添加);

2.7.线程池的“钩子”方法

        通过线程池的钩子方法我们可以在每个线程执行的前后做一些事;

import java.util.concurrent.*;
/**
 * 演示每个线程池任务执行前后执行自定义的钩子函数
 *       可用于线程的日志搜集;
 */
public class ThreadPoolHookMethod  extends ThreadPoolExecutor {

    public ThreadPoolHookMethod(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue);
    }

    public ThreadPoolHookMethod(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory);
    }

    public ThreadPoolHookMethod(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, handler);
    }

    public ThreadPoolHookMethod(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) {
        super(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory, handler);
    }


    /**
     *  钩子方法一:在线程池执行之前做某些事
     * @param t 将要执行任务的线程
     * @param r 将要执行的任务
     */
    @Override
    protected void beforeExecute(Thread t, Runnable r) {
        super.beforeExecute(t, r);
        System.out.println("===============  我是线程池的钩子方法beforeExecute(在线程池执行任务前我执行了) 当前线程池名称:" + t.getName() + " ===============");
    }


    /**
     * 钩子方法二:在线程池执行之后做某些事
     * @param r 执行的任务
     * @param t 异常信息
     */
    @Override
    protected void afterExecute(Runnable r, Throwable t) {
        super.afterExecute(r, t);
        System.out.println("===============  我是线程池的钩子方法afterExecute(在线程池执行任务后我执行了)  ===============");
    }


    public static void main(String[] args) {
        ThreadPoolHookMethod pool = new ThreadPoolHookMethod(5,5,0L,TimeUnit.MILLISECONDS,new LinkedBlockingQueue<>());
        pool.execute(new Task());
    }

    // 任务
    static class Task implements Runnable{
        @Override
        public void run() {
            System.out.println("我是线程:" + Thread.currentThread().getName() +" 正在执行run方法中任务...");
        }
    }
}

3.线程池实现原理

        1.线程池组成部分

                1.线程池管理器;

                        用于管理线程池创建或停止线程池;

                2.工作线程;

                        线程池创建出来用于执行任务的那些线程;

                3.任务队列;

                        用于存储提交到线程池的任务;

                4.任务接口(Task);

                        线程池具体的执行任务;

        2.线程池实现任务复用的原理

                相同线程执行不同任务。在线程池内部循环检测能否拿取到工作队列中任务,如果能拿取到任务则调用其任务的run方法去执行任务,此时直接调用run方法并没有去新开线程而是线程池中的当前线程去执行run方法所以实现了线程的复用;

4.线程池状态

        1.RUNNING

                接收新任务并处理工作队列中待处理的任务;

        2.SHUTDOWN

                不接收新任务但处理工作队列中待处理的任务;

        3.STOP

                不接收新任务,也不处理工作队列中待处理的任务,并且向正在执行的任务发出“中断”信号;

        4.TIDYING

                所有任务(正在执行的任务 + 工作队列中待处理的任务)都已终止,并将运行terminate()方法;

        5.TERMINATED

                运行完成;

5.使用线程池注意点

        1.避免任务堆积(响应效率慢且可能出现“内存溢出”问题);

        2.避免线程数过度增加(可能出现资源耗尽等问题);

        3.排查线程泄露(线程泄露指线程执行完毕后不能被回收);

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Vulnhub 靶场】【Hms?: 1】【简单】【20210728】

1、环境介绍 靶场介绍&#xff1a;https://www.vulnhub.com/entry/hms-1,728/ 靶场下载&#xff1a;https://download.vulnhub.com/hms/niveK.ova 靶场难度&#xff1a;简单 发布日期&#xff1a;2021年07月28日 文件大小&#xff1a;2.9 GB 靶场作者&#xff1a;niveK 靶场系…

使用react+vite开发项目时候,部署上线后刷新页面无法访问解决办法

说一下我这边的环境和使用的路由模式&#xff1a;vitereactBrowserRouter路由模式&#xff0c;所以如果你和我一样的话&#xff0c;可以试试我的这种解决办法&#xff0c;我是将项目打包后直接丢到服务器上的目录里面&#xff0c;然后配置nginx直接访问根目录。 我的nginx配置…

总埋怨内娱没有舞台,但打歌节目为什么没人看?

内娱又一档打歌节目停播了&#xff0c;优酷的《朝阳打歌中心》还是没能挺过2023年。 12月24日最后一期节目播出之后&#xff0c;《朝阳打歌中心第二季》就正式收官了&#xff0c;原定将录制到明年2月份却突然提前停播&#xff0c;无论是参演歌手还是观众都感到无比惊讶和不舍。…

设计高手的秘密武器:7款卓越的PPT工具推荐

1.即时设计 使用模板制作ppt可以帮助设计师节省工作时间&#xff0c;减轻工作压力。在即时设计资源广场为设计师提供各种精美的设计材料&#xff0c;可以有效地帮助设计师节省时间和高效地完成工作任务。此外&#xff0c;设计师还可以重新更改这些设计材料&#xff0c;以满足设…

vite前端项目根据不同环境切换不同的请求域名,5分钟搞定

有能力的可以直接看vite官方文档&#xff1a;环境变量和模式 | Vite 官方中文文档 简单操作步骤&#xff1a;创建不同环境的配置文件&#xff0c;在配置文件中声明VITE_开头的变量并赋值&#xff0c;然后在项目中引入这个变量并使用。 创建配置文件 一般分为开发模式和生产模…

PyTorch 进阶指南,10个必须知道的原则

PyTorch 是一种流行的深度学习框架&#xff0c;它提供了强大的工具和灵活的接口&#xff0c;使得开发者能够搭建和训练各种神经网络模型。这份指南旨在为开发者提供一些有用的原则&#xff0c;以帮助他们在PyTorch中编写高效、可维护和可扩展的代码。 如果你对 Pytorch 还处于…

【ES】es介绍

倒排索引&#xff08;Inverted Index&#xff09;和正排索引&#xff08;Forward Index&#xff09; 正排索引是一种以文档为单位的索引结构&#xff0c;它将文档中的每个单词或词组与其所在的文档进行映射关系的建立。正排索引通常用于快速检索指定文档的内容&#xff0c;可以…

华为鸿蒙(HarmonyOS)介绍

华为鸿蒙&#xff08;HarmonyOS&#xff09;介绍 华为鸿蒙&#xff08;HarmonyOS&#xff09;是一款由华为自主研发的操作系统&#xff0c;旨在为各种智能设备提供一种统一、高效、安全的解决方案。鸿蒙系统基于微内核架构&#xff0c;可以应用于多种类型的设备&#xff0c;鸿…

32阵元 MVDR和DREC DOA估计波束方向图对比

32阵元 MVDR和DREC DOA估计波束方向图对比 一、原理 MVDR原理&#xff1a;https://zhuanlan.zhihu.com/p/457528114 DREC原理&#xff08;无失真响应特征干扰相消器&#xff09;&#xff1a;http://radarst.ijournal.cn/html/2019/3/201903018.html 主要参数&#xff1a; 阵…

高效Java开发分析:JProfiler 14 for Mac中文

JProfiler允许你对运行中的Java应用程序进行实时性能分析。它提供了丰富的统计数据、图表和概览&#xff0c;帮助你了解应用程序的CPU使用情况、内存使用情况、线程活动等。你可以追踪和识别性能瓶颈&#xff0c;并快速定位问题所在。 内存分析&#xff1a;软件提供了强大的内…

中职网络安全Server2002——Web隐藏信息获取

B-2&#xff1a;Web隐藏信息获取 任务环境说明&#xff1a; 服务器场景名&#xff1a;Server2002&#xff08;关闭链接&#xff09;服务器场景用户名&#xff1a;未知 有问题需要环境加q 通过本地PC中渗透测试平台Kali使用Nmap扫描目标靶机HTTP服务子目录&#xff0c;将扫描子…

js_常用事件演示

✨前言✨ 1.如果代码对您有帮助 欢迎点赞&#x1f44d;收藏⭐哟 后面如有问题可以私信评论哟&#x1f5d2;️ 2.博主后面将持续更新哟&#x1f618;&#x1f389;文章目录 &#x1f354;一、在JavaScript中什么是事件&#xff1f;&#x1f35f;二、为什么要使用事件&#x…

如何在数字世界复刻一个高还原、高拟真的“你”?

本文为「云端智能剪辑」实践指南第五期&#xff0c;通过阿里云智能媒体服务IMS完成数字人形象训练、人声克隆定制&#xff0c;并使用Timeline实现视频合成及创作&#xff0c;打造一个“声形俱佳”的数字分身。 橙鲤&#xff5c;作者 形象日益逼真、交互更为顺畅&#xff0c;虚…

Python搭建代理IP池实现存储IP的方法

目录 前言 1. 介绍 2. IP存储方法 2.1 存储到数据库 2.2 存储到文件 2.3 存储到内存 3. 完整代码示例 总结 前言 代理IP池是一种常用的网络爬虫技术&#xff0c;可以用于反爬虫、批量访问目标网站等场景。本文介绍了使用Python搭建代理IP池&#xff0c;并实现IP存储的…

软件开发模式

瀑布式开发 在瀑布式开发中&#xff0c;开发严格遵循预先计划的需求分析、设计、编码、集成、测试、维护的步骤进行&#xff0c;步骤的成果作为衡量进度的方法&#xff0c;例如需求规格、设计文档、测试计划和代码审阅等&#xff0c; 瀑布式开发最早强调系统开发应有完整的周期…

文档智能翻译,保留文档原有布局,版式还原

翻译能力&#xff1a; 使用讯飞的AI翻译能力&#xff1a;机器翻译 niutrans - 语音扩展 - 讯飞开放平台API&#xff1a; 机器翻译niutrans API 文档 | 讯飞开放平台文档中心 执行效果&#xff1a; 原文档&#xff1a; 翻译还原的文档&#xff1a; 源码如下&#xff1a; impor…

AOT编程

1. AOT与JIT AOT&#xff1a;Ahead-of-Time&#xff08;提前编译&#xff09;&#xff1a;程序执行前&#xff0c;全部被编译成机器码 JIT&#xff1a;Just in Time&#xff08;即时编译&#xff09;: 程序边编译&#xff0c;边运行&#xff1b; 编译&#xff1a; 源代码&a…

oled显示汉字字体 形状 使用

oled模块的工作原理 oled的上方四个接口是IIC总线 通过IIC总线可以进行数据的传输 在OLED模块背后有一个芯片叫做SSD1306 这个芯片内部有1024个字节的RAM 对应到右边的小屏幕上就有1024个字节 一个字节八个bit位 每一个bit位就对应着一个小点 我们只需要往oled的RAM上写入数据就…

TPRI-DMP平台介绍

TPRI-DMP平台介绍 TPRI-DMP平台概述 TPRI-DMP为华能集团西安热工院自主产权的工业云PaaS平台&#xff0c;已经过13年的发展和迭代&#xff0c;其具备大规模能源电力行业生产应用软件开发和运行能力。提供TPRI-DMP平台主数据管理、业务系统开发与运行、应用资源管理与运维监控…

新能源光伏行业CRM:推动绿色能源发展与高效客户管理的双重突破

随着“碳中和”计划以及传统能源价格的不断飙升&#xff0c;我国新能源光伏产业在国家“双碳”战略目标和市场需求的双重驱动下高歌猛进&#xff0c;中国光伏产业新增装机量、累计装机量连续多年位居全球首位。CRM在光伏产业中的作用也日益突出。下面为您介绍新能源光伏行业的C…