并发编程系列——6线程池核心原理分析

2023-11-07

学习目标

  1. 线程池的作用

  2. jdk给我们提供了哪几种常用线程池

  3. 线程池有哪几大核心参数

  4. 线程池的拒绝策略有哪些

  5. 线程中阻塞队列的作用

  6. 线程池的工作流程

  7. 线程池的设计思维

  8. 线程池中的阻塞队列如果用默认的,会有哪些问题

  9. 线程池的工作状态有哪些

  10. 线程池中核心线程数量大小怎么设置?

第1章 线程池简介

1.1 线程的问题

  1. 线程执行完run发放自动被销毁了,且任务与线程绑定在了一起,所以当任务多的时候,会频繁的创建和销毁线程,这给我们CPU和内存带来了很大的开销。

  2. 线程一多了,无法实现统一管理。

1.2 线程池的概念及作用

  1. 他是池化技术的一种应用

  2. 他实现了线程的重复利用

  3. 实现了对线程资源的管理控制

1.3 常见线程池

  1. newFixedThreadPool:该方法返回一个固定数量的线程池,线程数不变,当有一个任务提交时,若线程池中空闲,则立即执行,若没有,则会被暂缓在一个任务队列中,等待有空闲的线程去执行。

  2. newSingleThreadExecutor: 创建一个线程的线程池,若空闲则执行,若没有空闲线程则暂缓在任务队列中。

  3. newCachedThreadPool:返回一个可根据实际情况调整线程个数的线程池,不限制最大线程数量,若用空闲的线程则执行任务,若无任务则不创建线程。并且每一个空闲线程会在60秒后自动回收

  4. newScheduledThreadPool: 创建一个可以指定线程的数量的线程池,但是这个线程池还带有延迟和周期性执行任务的功能,类似定时器。

  5. newWorkStealingPool:适合使用在很耗时的操作,但是newWorkStealingPool不是ThreadPoolExecutor的扩展,它是新的线程池类ForkJoinPool的扩展,但是都是在统一的一个Executors类中实现,由于能够合理的使用CPU进行对任务操作(并行操作),所以适合使用在很耗时的任务中

第2章 线程池原理分析

2.1 初始化

我们先看下初始化5个参数

public ThreadPoolExecutor(int corePoolSize,  
                          int maximumPoolSize,    
                          long keepAliveTime,
                          TimeUnit unit, 
                          BlockingQueue<Runnable> workQueue) {  
    this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
         Executors.defaultThreadFactory(), defaultHandler);
}

    public ThreadPoolExecutor(int corePoolSize,  //主线程数
                              int maximumPoolSize,  //最大线程数
                              long keepAliveTime,    //线程存活时间   (除主线程外,其他的线程在没有任务执行的时候需要回收,多久后回收)
                              TimeUnit unit,  //存活时间的时间单位
                              BlockingQueue<Runnable> workQueue,  //阻塞队列,我们需要执行的task都在该队列
                              ThreadFactory threadFactory,  //生成thread的工厂
                              RejectedExecutionHandler handler) {  //拒绝饱和策略,当队列满了并且线程个数达到 maximunPoolSize 后采取的策略
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.acc = System.getSecurityManager() == null ?
                null :
                AccessController.getContext();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

2.2 execute方法

614e501dd3f34f9788f16a7de66a64d3.png

 

public void execute(Runnable command) {
    if (command == null)  //如果要执行的任务是空的,异常
        throw new NullPointerException();
    /*
     * Proceed in 3 steps:
     *
     * 1. If fewer than corePoolSize threads are running, try to
     * start a new thread with the given command as its first
     * task.  The call to addWorker atomically checks runState and
     * workerCount, and so prevents false alarms that would add
     * threads when it shouldn't, by returning false.
     *
     * 2. If a task can be successfully queued, then we still need
     * to double-check whether we should have added a thread
     * (because existing ones died since last checking) or that
     * the pool shut down since entry into this method. So we
     * recheck state and if necessary roll back the enqueuing if
     * stopped, or start a new thread if there are none.
     *
     * 3. If we cannot queue task, then we try to add a new
     * thread.  If it fails, we know we are shut down or saturated
     * and so reject the task.
     */
    int c = ctl.get();//111000...000
    //高三位代表线程池的状态,低29位代表线程池中的线程数量
    //如果线程数小于主线程数,添加线程
    if (workerCountOf(c) < corePoolSize) {
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    //如果超过主线程数,将任务添加至workqueue 阻塞队列
    if (isRunning(c) && workQueue.offer(command)) {
        int recheck = ctl.get();
        //再判断一次运行状态,如果线程池不处于running状态,则把刚加进队列的任务移除,如果移除成功则往下走进行拒绝
        if (! isRunning(recheck) && remove(command))
            reject(command);
        //接着上一个条件,如果移除失败则判断是否有工作线程,如果当前线程池线程空,则添加一个线程
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
    }
    //如果超过主线程数且添加阻塞队列失败,则增加非核心线程,如果添加非核心线程也失败,则拒绝
    else if (!addWorker(command, false))
        reject(command);
}
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));//111000...000
private static final int COUNT_BITS = Integer.SIZE - 3;//29
private static final int CAPACITY   = (1 << COUNT_BITS) - 1;//00011111 11111111 11111111 11111111
//00000000 00000000 00000000 00000001  << 29 =
//00100000 00000000 00000000 00000000 -1 = 
//00011111 11111111 11111111 11111111

// runState is stored in the high-order bits
private static final int RUNNING    = -1 << COUNT_BITS; //11100000 ... 000
//-1 原码: 10000000 00000000 00000000 00000001
//-1 反码: 11111111 11111111 11111111 11111110
//-1 补码: 11111111 11111111 11111111 11111111 <<29=
//        11100000 0000000 00000000 00000000

private static final int SHUTDOWN   =  0 << COUNT_BITS;//00000000 ... 000
private static final int STOP       =  1 << COUNT_BITS;//001 0000 ... 000
private static final int TIDYING    =  2 << COUNT_BITS;//010 0000 ... 000
private static final int TERMINATED =  3 << COUNT_BITS;//011 0000 ... 000
1、RUNNING
(1) 状态说明:线程池处在RUNNING状态时,能够接收新任务,以及对已添加的任务进行处理。 
(02) 状态切换:线程池的初始化状态是RUNNING。换句话说,线程池被一旦被创建,就处于RUNNING状态,并且线程池中的任务数为0!
private final AtomicInteger ctl = new AtomicInteger(ctlOf(RUNNING, 0));
2、 SHUTDOWN
(1) 状态说明:线程池处在SHUTDOWN状态时,不接收新任务,但能处理已添加的任务。 
(2) 状态切换:调用线程池的shutdown()接口时,线程池由RUNNING -> SHUTDOWN。
3、STOP
(1) 状态说明:线程池处在STOP状态时,不接收新任务,不处理已添加的任务,并且会中断正在处理的任务。 
(2) 状态切换:调用线程池的shutdownNow()接口时,线程池由(RUNNING or SHUTDOWN ) -> STOP。
4、TIDYING
(1) 状态说明:当所有的任务已终止,ctl记录的”任务数量”为0,线程池会变为TIDYING状态。当线程池变为TIDYING状态时,会执行钩子函数terminated()。terminated()在ThreadPoolExecutor类中是空的,若用户想在线程池变为TIDYING时,进行相应的处理;可以通过重载terminated()函数来实现。 
(2) 状态切换:当线程池在SHUTDOWN状态下,阻塞队列为空并且线程池中执行的任务也为空时,就会由 SHUTDOWN -> TIDYING。 
当线程池在STOP状态下,线程池中执行的任务为空时,就会由STOP -> TIDYING。
5、 TERMINATED
(1) 状态说明:线程池彻底终止,就变成TERMINATED状态。 
(2) 状态切换:线程池处在TIDYING状态时,执行完terminated()之后,就会由 TIDYING -> TERMINATED。
private static int runStateOf(int c)     { return c & ~CAPACITY; }
private static int workerCountOf(int c)  { return c & CAPACITY; }//CAPACITY:000111...111
private static int ctlOf(int rs, int wc) { return rs | wc; }

2.3 addWorker方法

private boolean addWorker(Runnable firstTask, boolean core) {
    retry:  //goto语句  叫demo
    //自旋检查线程池的状态。阻塞队列是否为空等判断
    for (;;) {
        int c = ctl.get();
        int rs = runStateOf(c);

        // Check if queue empty only if necessary.
        if (rs >= SHUTDOWN &&
            ! (rs == SHUTDOWN &&
               firstTask == null &&
               ! workQueue.isEmpty()))//如果线程池的运行状态是running的话直接跳过该条件语句往下走,如果是>=SHUTDOWN的话就往后判断(为什么不直接返回false不让他创建worker呢,因为在shutdown状态是可以创建线程去处理阻塞队列里的任务的)
            //此时因为rs>=SHTDOWN了,所以会先判断是否等于SHUTDOWN,如果不等于就直接返回false不让创建worker,如果等于的话接着往下判断
            //如果当前任务不为空直接返回false不让创建worker,(这里为什么当前任务为空就直接不让创建worker呢,就是因为shutdown状态不能再接收新任务。
            //如果当前任务为空则判断阻塞队列是否为空,如果为空则返回false,不让创建worker,如果不为空就不走这个条件,接着往下走
            return false;

        //自旋
        for (;;) {
            int wc = workerCountOf(c);
            //如果现有线程数大于最大值,或者大于等于最大线程数(主线程数)
            if (wc >= CAPACITY ||
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            //cas添加线程
            if (compareAndIncrementWorkerCount(c))
                break retry;
            c = ctl.get();  // Re-read ctl
            //如果失败了,继续外层循环判断
            if (runStateOf(c) != rs)
                continue retry;
            // else CAS failed due to workerCount change; retry inner loop
        }
    }

    boolean workerStarted = false;
    boolean workerAdded = false;
    Worker w = null;
    try {
        //开启一个线程,Worker实现了runnable接口
        w = new Worker(firstTask);
        final Thread t = w.thread;
        if (t != null) {
            final ReentrantLock mainLock = this.mainLock;
            mainLock.lock();
            try {
                // Recheck while holding lock.
                // Back out on ThreadFactory failure or if
                // shut down before lock acquired.
                int rs = runStateOf(ctl.get());

                if (rs < SHUTDOWN ||
                    (rs == SHUTDOWN && firstTask == null)) {
                    if (t.isAlive()) // precheck that t is startable
                        throw new IllegalThreadStateException();
                    //添加至wokers
                    workers.add(w);
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            //添加成功
            if (workerAdded) {
                t.start();   //启动线程,会调用我们线程的run接口,也就是我们worker的run
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

2.4 goto语句demo

    retry:
        for (int i = 0; i < 3; i++) {
            for (int j = 3; j < 10; j++) {
//                if (j == 4) {
//                    break retry;  //跳出外面循环
//                }
                if (j == 7) {
                    continue retry;  //继续外面循环
                }
                System.out.println(i+":"+j);
            }

        }
Worker(Runnable firstTask) {
    setState(-1); // inhibit interrupts until runWorker   禁止中断,直到runWorker
    this.firstTask = firstTask;
    this.thread = getThreadFactory().newThread(this);
}

2.5 worker.run方法

final void runWorker(Worker w) {    
    Thread wt = Thread.currentThread();    
    Runnable task = w.firstTask;    
    w.firstTask = null;    
    w.unlock(); // allow interrupts      
    boolean completedAbruptly = true;    
    try {        //只要一直能获取到task,就一直会执行,不会关闭,所以线程也不会销毁,线程销毁只有当task为null        
        while (task != null || (task = getTask()) != null) {            
            w.lock();            
            // If pool is stopping, ensure thread is interrupted;            
            // if not, ensure thread is not interrupted.  This            
            // requires a recheck in second case to deal with            
            // shutdownNow race while clearing interrupt            
            if ((runStateAtLeast(ctl.get(), STOP) ||                 
                 (Thread.interrupted() &&                  
                  runStateAtLeast(ctl.get(), STOP))) &&                
                !wt.isInterrupted())                
                wt.interrupt();            
            try {                
                //调用线程方法之前执行                
                beforeExecute(wt, task);                
                Throwable thrown = null;                
                try {                    
                    //调用task的run方法                    
                    task.run();                
                } catch (RuntimeException x) {                    
                    thrown = x; throw x;                
                } catch (Error x) {                    
                    thrown = x; throw x;                
                } catch (Throwable x) {                    
                    thrown = x; throw new Error(x);                
                } finally {                    
                    //调用线程方法之后执行                    
                    afterExecute(task, thrown);                
                }            
            } finally {                
                task = null;                
                w.completedTasks++;                
                w.unlock();            
            }        
        }        
        completedAbruptly = false;    
    } finally {        
        processWorkerExit(w, completedAbruptly);    
    }
}

2.6 getTask()方法

private Runnable getTask() {    
    boolean timedOut = false; // Did the last poll() time out?	//自旋获取    
    
    for (;;) {        
        int c = ctl.get();        
        int rs = runStateOf(c);        
        
        // Check if queue empty only if necessary. 必要时检查空,状态是否停止或者shutdown        
        if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {            
            decrementWorkerCount();            
            return null;        
        }        
        
        //获取线程数量        
        int wc = workerCountOf(c);        
        
        // Are workers subject to culling?        
        //线程数大于主线程数时,或者allowCoreThreadTimeOut参数为true  allowCoreThreadTimeOut默认为false        
        boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;        
        //超过最大线程,或者timed为true ,&& wc大于1个,并且任务队列为空的时候        
        if ((wc > maximumPoolSize || (timed && timedOut))            
            && (wc > 1 || workQueue.isEmpty())) {            
            //线程数-1,并且返回null,该线程结束            
            if (compareAndDecrementWorkerCount(c))                
                return null;            
            continue;        
        }        
        
        try {            
            //如果time是true,超过时间不阻塞,不然一直阻塞,不回收            
            Runnable r = timed ?                
                workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) : 
            //移除并返回队列头部的元素,如果为空,超过时间返回null                
            workQueue.take();
            //移除并返回队列头部的元素,如果为空,一直阻塞            
            if (r != null)                
                return r;            
            timedOut = true;        
        } catch (InterruptedException retry) {            
            timedOut = false;        
        }    
    }
}

 

 

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

并发编程系列——6线程池核心原理分析 的相关文章

  • 获取jdbc中表依赖顺序

    我在 MySQL 数据库中有一组表 A B C D 依赖关系如下 B gt C gt A 和 D gt A 也就是说 A 有一个 PrimaryKey C 有一个外键指向 A 的主键 B 有一个外键指向 C 的主键 类似地 D 有一个外键指
  • 如何防止在 CXF Web 服务客户端中生成 JAXBElement

    我正在尝试使用 CXF 创建一个 Web 服务客户端来使用 WCF Web 服务 当我使用 wsdl2java 时 它生成具有 JAXBElement 类型而不是 String 的对象 我读到有关使用 jaxb bindings xml 文
  • 使用 JAXB 编组 LocalDate

    我正在构建一系列链接类 我希望能够将其实例编组到 XML 以便我可以将它们保存到文件中并稍后再次读取它们 目前我使用以下代码作为测试用例 import javax xml bind annotation import javax xml b
  • 空 EntityManager/EJB 注入 MDB

    我有一个消息驱动 bean MDB 部署到 WebLogic 12 1 3 我尝试使用 PersistenceContext 注释将实体管理器注入 MDB 但实体管理器为空 我还尝试注入一个简单的无状态会话 bean 它也是空的 但是 Me
  • 如果按下 Esc 则中断循环

    我用 JAVA 语言编写了一个程序 它使用 Scanner 类接受来自控制台的输入 现在我想将此功能添加到我的代码中 以便在用户按下 Esc 按钮时存在循环 while 到目前为止 我认为键盘类可以帮助我 但它就像扫描仪一样 我尝试使用事件
  • FFmpeg 不适用于 android 10,直接进入 onFailure(String message) 并显示空消息

    我在我的一个项目中使用 FFmpeg 进行视频压缩 在 Android 10 Google Pixel 3a 上 对于发送执行的任何命令 它会直接进入 onFailure String message 并显示空消息 所以我在我的应用程序 g
  • 为什么一个线程会中断另一个线程[重复]

    这个问题在这里已经有答案了 在Java多线程应用程序中 我们处理InterruptedThreadException 如果另一个线程中断当前线程 则会抛出此异常 现在 当另一个线程知道它将导致异常时 它可能想要中断当前线程的原因是什么 很多
  • 更改 JTextPane 的大小

    我是Java新手 刚刚在StackOverflow中找到了这段代码 ResizeTextArea https stackoverflow com questions 9370561 enabling scroll bars when jte
  • 用于防止滥用的 Servlet 过滤器? (DoS、垃圾邮件等)

    我正在寻找一个 Servlet 过滤器库 它可以帮助我保护我们的 Web 服务免受未经授权的使用和 DDoS 攻击 我们的网络服务有 授权客户 因此理想情况下 过滤器将帮助检测未经授权或行为不当的客户 或检测使用同一帐户的多个人 此外 我们
  • Scala(或 Java)中泛型函数的特化

    是否可以在 Scala 中专门化泛型函数 或类 例如 我想编写一个将数据写入 ByteBuffer 的通用函数 def writeData T buffer ByteBuffer data T buffer put data 但由于 put
  • MessageDigest MD5 算法未返回我期望的结果

    我脑后的某个东西告诉我 我在这里遗漏了一些明显的东西 我正在将现有的 java 项目与第三方 api 集成 该第三方 api 使用 api 密钥的 md5 哈希进行身份验证 它对我不起作用 在调试过程中我意识到我生成的哈希值与他们提供的示例
  • 尝试在空对象引用上调用虚拟方法“java.lang.String org.jsoup.nodes.Element.ownText()”

    我正在使用下面的代码来获取版本名称 from 应用商店通过使用 jsoup 我正在获取详细信息 但它引发了一些异常 我的代码是 public class ForceUpdateAsync extends AsyncTask
  • 如何列出所有可用的 LookAndFeel 主题?

    如何列出所有可用的 LookAndFeel 主题 我想在 JComboBox 中显示以供用户选择 这真的很简单 public static UIManager LookAndFeelInfo getInstalledLookAndFeels
  • C++ 中的 Java ArrayList [重复]

    这个问题在这里已经有答案了 在Java中我可以做 List
  • 在方法内声明类 - Final 关键字 [重复]

    这个问题在这里已经有答案了 给定方法中的以下内部类 IsSomething public class InnerMethod private int x public class Something private int y public
  • 如何使用 Hibernate Session.doWork(...) 进行保存点/嵌套事务?

    我正在使用 JavaEE JPA 托管事务与 Oracle DB 和 Hibernate 并且需要实现某种嵌套事务 据我所知 此类事情不受开箱即用的支持 但我应该能够为此目的使用保存点 正如建议的https stackoverflow co
  • 如何将任务添加到 gradle 中的主要“构建”任务

    当我尝试使用以下代码将任务添加到主构建任务时 rootProject tasks getByName build dependsOn mytask 当我跑步时它抱怨gradle w build输出 Where Build file line
  • Jenkins 管道和 java.nio.file.* 方法的问题

    我正在尝试使用 java nio file 中的方法在 Jenkins 管道中执行一些基本文件操作 无论代码存在于哪个节点块中 代码都在主节点上执行 在管道中 我已经验证了各个节点块都是正确的 它们唯一地标识了特定的节点 但是 pathEx
  • JPA - 非主键字段上的 @OneToOne 关系不起作用

    我有一个 Spring Data JPA 后端 使用 Hibernate 作为 ORM 实现 这是模型 Person MailConfig id PK uid PK FK Person uid uid Entity
  • java数据结构模拟数据树

    我需要帮助定义使用什么方法 我有一个 SOAP 响应 给我一个 xml 文件 我需要在屏幕上显示 3 个相关列表 当您在第一个列表中选择一个项目时 相应的选择将出现在第二个列表中 依此类推 我只对从 xml 流中提取数据后如何有效地组织数据

随机推荐

  • Vagrant快速入门教程

    之前学习Docker的时候 发现了Vagrant 感觉这也是一个挺方便的技术 但是我下载安装完Vagrant的时候 发现恰好VirtualBox发了新版本 Vagrant还没兼容 所以这篇文章一直拖到了现在 昨天正好Vagrant更新了版本
  • function函数

    一 第一个function函数 1 在代码中书写的function函数默认情况下是不执行的 2 function函数只有在调用的时候才能被执行 函数是使用函数名来进行调用的 并且函数名的后面必须带有一对括号 3 可以多次调用函数 可以使用循
  • top命令详解

    一 参数 参数 意义 使用示例 hv 显示版本和帮助 top h top v top hv d 每隔多长时间刷新一次 单位是秒 默认5s top d 3 n 最多刷新几次退出 top n 5 u U 展示指定用户的信息 top u root
  • Windows10下实现输出到屏幕并且保存在文件中

    日期 2018 06 23 问题描述 有时 我们需要将执行后的输出不仅要显示在屏幕上 还想要将其保存一份文件 以便日后查看 这里 主要利用Windows10下PowerShell实现该功能 Windows PowerShell可以认为是命令
  • C语言《数据结构》——图的概念和创建,遍历

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 前言 例如 随着计算机网络的发展 编程成了一种很常见且重要的职业 学好编程就要学好数据结构 下面将介绍数据结构中的图结构 一 什么是 图 二 图的基础知识和表示 1
  • 这三个 Go 水平自测题,你手写不出来还是先老实上班吧,过来看看

    现在技术文章特别卷 啥啥底层都能给你分析的头头是道 但是分析的对不对要看作者水平 很有可能一个错 抄他的那些人也跟着错 因为我以前看源码的时候就经常感觉自己在两种状态下切换 懂了 娘咧漏看了 这个函数干啥的 八股文这个事儿 其实也特别考验面
  • 如何去除WinRAR弹窗广告

    WinRAR 作为知名的老牌压缩软件 二十余年来始终风靡全球 经久不衰 但对于中国用户 其简体中文的个人免费版安装后会有 评估版本 的标记 而且每次启动时会有代理商的弹窗广告 本文教你如何去除标记和弹窗广告 让你的 WinRAR 恢复纯净体
  • 最优控制的理解

    最优控制 在满足一定约束的情况下 寻求最优控制策略 使得性能指标取极大值或极小值 对一个受控的动力学系统 从一类允许的控制方案中寻找一个最优的控制方案 使得系统的运动从由某个初始状态转移到目标状态的同时 其性能指标值为最优 性质 在一般的情
  • Cartoon头像 InsCode Stable Diffusion 美图活动一期

    一 简单介绍和活动地址 简单介绍 试用Stable Diffusion 模型生成优质人物好图 更简单地炼丹 InsCode是一个集成了在线IDE 在线AI编程 在线算力租赁 在线项目部署以及在线SD 模型使用的综合代码开发平台 不论你是初级
  • 基础算法题——找筷子(位的异或)

    找筷子 题目描述 经过一段时间的紧张筹备 电脑小组的 RP 餐厅 终于开业了 这天 经理 LXC 接到了一个定餐大单 可把大家乐坏了 员工们齐心协力按要求准备好了套餐正准备派送时 突然碰到一个棘手的问题 筷子 CX 小朋友找出了餐厅中所有的
  • VS+Qt应用开发,设置软件图标Icon

    VS Qt应用开发 设置软件图标Icon 前言 一 索然无味的默认icon图标 二 如何设置自己喜欢的icon图标 1 选择自己喜欢的图标 2 设置可执行文件 exe 图标 3 设置标题栏和任务栏图标 三 效果 四 工程源码 前言 VS版本
  • 深度学习系列笔记(一)——深度学习简介与反向传播机制

    深度学习简介 深度学习的发展趋势 Fully Connect Feedforward Network举例 反向传播 符号表示 计算Loss对Params的偏导数 符号说明 过程 例子 参考文献 深度学习的发展趋势 回顾一下deep lear
  • Tablayout+viewpager+Fragment的fragment页面数据不显示

    开发中经常使用Tablayout ViewPager与Frament 联动 之前fragment 页面创建比较少 没有出现什么问题 但是当创建frament页面页面比较多的时候 fragment 页面的数据会有不显示 想了各种办法没有解决
  • 海湾汉字编码表全部_汉字编码对照表

    这里写自定义目录标题 海湾汉字编码APP 蛮好用的 在某宝里搜 海湾汉字编码App 就能找到了 作者一直在更新
  • 使用arduino Nano 自制nRF24LE1 的烧录器进行固件烧录

    也有专门usb编程器 比较贵 可以买个arduino nano自已做一个 1 下载arduino的烧录固件 https github com garcezluz nRF24LE1 Programmer 2 使用arduino编译烧录固件 3
  • 计算机毕业设计如何制作电子商务网站怎么制作购物网站计算机课程设计电子商城做什么(PHP-ASP.NET-c#-JavaWeb-SSM-SSH-J2EE-springBoot

    如果计算机毕业设计选题是 lt lt 电子商务网站 gt gt lt lt 购物网站 gt gt 这样的题目 那么灵魂问答如下 需要实现什么功能呢 怎么样挑选适合自己的编程语言 使用什么前端框架 使用什么数据库 通过本文将给您找到以上答案
  • 【linux kernel】linux内核数据结构分析之哈希表

    Linux内核中实现了一套经典的哈希表操作 定义在 include linux list h文件中 本文基于linux内核源码6 2 7 记录了其常用操作哈希表的API函数 便于在阅读linux内核源码时更好的理解程序执行的过程和细节 在L
  • 外部中断实验

    外部中断简介 前一篇详细介绍了51 单片机的中断系统 这里再简单回顾一下 当中央处理机 CPU 正在处理某件事的时候外界发生了紧急事件请求 要求 CPU 暂停当前的工作 转而去处理这个紧急事件 处理完以后 再回到原来被中断的地方 继续原来的
  • Swift set/get方法

    Swift 中重写属性的set和get方法 Swift中如何重写属性的set和get方法 set和get方法的本质是什么 set和get方法紧跟着属性后面写 很紧凑 也清楚 class Person NSObject var name st
  • 并发编程系列——6线程池核心原理分析

    学习目标 线程池的作用 jdk给我们提供了哪几种常用线程池 线程池有哪几大核心参数 线程池的拒绝策略有哪些 线程中阻塞队列的作用 线程池的工作流程 线程池的设计思维 线程池中的阻塞队列如果用默认的 会有哪些问题 线程池的工作状态有哪些 线程