线程池解析

dubbo性能调优 dubbo:protocol threadpool 和 threads threadpool 表示线程池类型。其值可以是 “fixed” 或 “cached”。默认值:fixed fixed 表示线程池启动时就创建了固定大小的线程数,不做任何伸缩。 其创建原理与java内置的 Executors.newFixedThreadPool() 相同。 相关dubbo代码:com.alibaba.dubb... 阅读详情

线程池解析

java线程对应内核线程,创建与销毁需要切换到内核态,是系统开销较大的操作,因此需要设法复用线程。线程池就是一个线程缓存,负责对线程统一分配,调优与监控,线程池的优势:

提高性能:重用线程减少线程创建消亡的开销
提高响应速度:任务到达时可以立即执行,不需要再去创建线程
方便管理线程:便于统一分配、调控和监控

执行Runnable任务流程

简单说处理任务的优先级为:核心线程、任务队列、最大线程,如果三者都满了,使用handler处理被拒绝的任务。

请添加图片描述

具体如何实现的呢?

/**
* 在将来某个时间执行该任务Runnable,可能是新建一个线程或者复用线程池已有的线程。
* 如果一个任务不能提交执行,要么是因为线程池shutdown了,要么是容量达到上限,此时执行拒绝策略
*/
public void execute(Runnable command) {
    if (command == null)
        throw new NullPointerException();
    // AtomicInteger原子变量,前3位表示状态,后29位表示线程数
    int c = ctl.get();
    if (workerCountOf(c) < corePoolSize) {
        // 1. addWorker方法里面会原子检查当前running State和工作线程数目,防止并发时错误的多创建,
        // 也就是如果有别的线程添加任务达到corePoolSize,addWorker会返回false表示创建失败
        if (addWorker(command, true))
            return;
        c = ctl.get();
    }
    
    if (isRunning(c) && workQueue.offer(command)) {
        // 2. 即使任务成功的入队,仍然需要再次检查是否应该增加线程,因为此时从last checking可能线程都死亡或者线程池shutdown
        int recheck = ctl.get();
        // 所以我们需要再次检查线程池状态,如果已经shutdown需要移除任务并拒绝该任务
        if (! isRunning(recheck) && remove(command))
            reject(command);
        // 如果线程都已死亡就新建一个
        else if (workerCountOf(recheck) == 0)
            addWorker(null, false);
       
     // 如果入队失败,再尝试新建线程。如果失败说明线程池shutdown,或者饱和达到maximumPoolSize,应该执行拒绝策略
    }else if (!addWorker(command, false))
        reject(command);
}

另外很重要的方法addWorker(),注意调用此方法是用来新建线程

/**
* 检查是否可以根据当前池状态和给定界限(corePoolSize或maximumPoolSize)添加新的工作线程。
* 如果可能,将创建并启动一个worker,将firstTask作为其第一个任务运行。
* 如果池已stopped或shutdown中,或者threadfactory创建线程失败,则此方法返回 false。 
* 如果线程创建失败,或者threadfactory返回null,或者由于异常(通常是Thread.start()中的 OutOfMemoryError),我们会干净利落地回滚。
*
* @firstTask – 新线程应该首先运行的任务(如果没有,则为null)。当线程少于corePoolSize时(在这种情况下我们总是启动一个),
* 或者当队列已满(在这种情况下我们必须绕过队列)时,使用初始的第一个任务(在方法 execute() 中创建工作线程.
* 最初空闲线程通常通过 prestartCoreThread 创建或替换其他垂死的工人。
*/
private boolean addWorker(Runnable firstTask, boolean core) {
    retry:
    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()))
            return false;

        for (;;) {
            int wc = workerCountOf(c);
            if (wc >= CAPACITY ||
                // check线程数是否超过限制
                wc >= (core ? corePoolSize : maximumPoolSize))
                return false;
            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 {
        // 创建新的线程
        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();
                    // 添加worker
                    workers.add(w);
                    // 更新 池的最大值	
                    int s = workers.size();
                    if (s > largestPoolSize)
                        largestPoolSize = s;
                    workerAdded = true;
                }
            } finally {
                mainLock.unlock();
            }
            if (workerAdded) {
                // 添加成功就启动线程
                t.start();
                workerStarted = true;
            }
        }
    } finally {
        if (! workerStarted)
            addWorkerFailed(w);
    }
    return workerStarted;
}

如何循环取任务执行的?

注意Worker实现了AQS,防止正在等待任务的线程被中断

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,只要从队列中能取getTask()就会不停的循环
        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();
                } 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 {
        // 没有任务了就退出逻辑:停止线程,但会保证维持corePoolSize
        processWorkerExit(w, completedAbruptly);
    }
}

线程池状态

线程池状态是通过一个AtomicInteger的前3位表示的
请添加图片描述

  • Running:表示能接受新任务及处理队列中的任务
  • Shutdown:表示不接受新任务,但是会处理队列中的任务
  • stop:表示不接受新任务,不处理队列中的任务,并且中断正在处理的任务
  • Tidying:表示所有的任务已经终止,ctl记录的“workerCount”为0,将会执行terminated()的钩子方法
  • Terminated:terminated()方法执行完毕

转换过程如下:

SHUTDOWN -> TIDYING:线程池线程数为空,工作队列为空

STOP -> TIDYING:只要线程池线程数为空即可

TIDYING -> TERMINATED:terminated() 的钩子方法执行完后
请添加图片描述

线程池中的阻塞工作队列

阻塞队列是队列的常见应用。常见的BlockingQueue主要有三种实现:

SynchronousQueue是不缓存任务,相当于一个中转站

ArrayBlockingQueue是一个用数组实现的有界阻塞队列,必须设置容量。

LinkedBlockingQueue是一个用链表实现的阻塞队列,容量可以选择进行设置,不设置的话最大长度为Integer.MAX_VALUE,将是一个无边界的阻塞队列。对于一个无边界队列来说,是可以不断的向队列中加入任务的,这种情况下就有可能因为任务过多而导致内存溢出问题。newFixedThreadPool中创建的就是未指定容量的LinkedBlockingQueue。

任务拒绝策略

当最大线程满后,会使用handler来处理被拒绝的任务,先看下 JDK 定义的 拒绝策略接口

public interface RejectedExecutionHandler {
    void rejectedExecution(Runnable r, ThreadPoolExecutor executor);
}

接口定义很明确,当触发拒绝策略时,线程池会调用你设置的具体的策略,将当前提交的任务以及线程池实例本身传递给你处理,具体作何处理,不同场景会有不同的考虑,下面看 JDK 为我们内置了默认的四种处理策略为:

  • AbortPolicy 抛出异常。
public static class AbortPolicy implements RejectedExecutionHandler {
    public AbortPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        throw new RejectedExecutionException("Task " + r.toString() +
                                             " rejected from " +
                                             e.toString());
    }
}

不执行此任务,而且直接抛出一个运行时异常 RejectedExecutionException,为java线程池默认的阻塞策略。切记会中断调用者的处理过程,因此需要try catch,否则程序会直接退出。

  • DiscardPolicy 直接静悄悄的丢弃这个任务,不触发任何动作。这个策略基本不会使用
public static class DiscardPolicy implements RejectedExecutionHandler {
    public DiscardPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
    }
}
  • DiscardOldestPolicy 丢弃队列最前面(最旧)的任务,然后重新尝试执行任务(重复此过程)。
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
    public DiscardOldestPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            e.getQueue().poll();
            e.execute(r);
        }
    }
}
  • CallerRunsPolicy 由调用线程处理该任务 。
public static class CallerRunsPolicy implements RejectedExecutionHandler {
    public CallerRunsPolicy() { }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        if (!e.isShutdown()) {
            r.run();
        }
    }
}

功能:当触发拒绝策略时,只要线程池没有关闭,就由提交任务的当前线程处理。

使用场景:一般在不允许失败的、对性能要求不高、并发量较小的场景下使用,因为线程池一般情况下不会关闭,也就是提交的任务一定会被运行,但是由于是调用者线程自己执行的,当多次提交任务时,就会阻塞后续任务执行,性能和效率自然就慢了。

下面我们看下第三方框架中都有哪些拒绝策略

Dubbo 中的线程拒绝策略

public class AbortPolicyWithReport extends ThreadPoolExecutor.AbortPolicy {

    protected static final Logger logger = LoggerFactory.getLogger(AbortPolicyWithReport.class);

    private final String threadName;

    private final URL url;

    private static volatile long lastPrintTime = 0;

    private static Semaphore guard = new Semaphore(1);

    public AbortPolicyWithReport(String threadName, URL url) {
        this.threadName = threadName;
        this.url = url;
    }

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
        String msg = String.format("Thread pool is EXHAUSTED!" +
                        " Thread Name: %s, Pool Size: %d (active: %d, core: %d, max: %d, largest: %d), Task: %d (completed: %d)," +
                        " Executor status:(isShutdown:%s, isTerminated:%s, isTerminating:%s), in %s://%s:%d!",
                threadName, e.getPoolSize(), e.getActiveCount(), e.getCorePoolSize(), e.getMaximumPoolSize(), e.getLargestPoolSize(),
                e.getTaskCount(), e.getCompletedTaskCount(), e.isShutdown(), e.isTerminated(), e.isTerminating(),
                url.getProtocol(), url.getIp(), url.getPort());
        logger.warn(msg);
        dumpJStack();
        throw new RejectedExecutionException(msg);
    }

    private void dumpJStack() {
       //省略实现
    }
}

可以看到,当dubbo的工作线程触发了线程拒绝后,主要做了三个事情,原则就是尽量让使用者清楚触发线程拒绝策略的真实原因

  • 输出了一条警告级别的日志,日志内容为线程池的详细设置参数,以及线程池当前的状态,还有当前拒绝任务的一些详细信息。可以说,这条日志,使用dubbo的有过生产运维经验的或多或少是见过的,这个日志简直就是日志打印的典范,其他的日志打印的典范还有spring。得益于这么详细的日志,可以很容易定位到问题所在
  • 输出当前线程堆栈详情,这个太有用了,当你通过上面的日志信息还不能定位问题时,案发现场的dump线程上下文信息就是你发现问题的救命稻草
  • 继续抛出拒绝执行异常,使本次任务失败,这个继承了JDK默认拒绝策略的特性

Netty 中的线程池拒绝策略

private static final class NewThreadRunsPolicy implements RejectedExecutionHandler {
    NewThreadRunsPolicy() {
        super();
    }

    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        try {
            final Thread t = new Thread(r, "Temporary task executor");
            t.start();
        } catch (Throwable e) {
            throw new RejectedExecutionException(
                "Failed to start a new thread", e);
        }
    }
}

Netty新建了一个线程来处理的。所以,Netty的实现相较于调用者执行策略的使用面就可以扩展到支持高效率高性能的场景了。但是也要注意一点,Netty的实现里,在创建线程时未做任何的判断约束,也就是说只要系统还有资源就会创建新的线程来处理,直到new不出新的线程了,才会抛创建线程失败的异常

PinPoint 中的线程池拒绝策略

public class RejectedExecutionHandlerChain implements RejectedExecutionHandler {
    private final RejectedExecutionHandler[] handlerChain;

	//通过静态方法来返回一个对象
    public static RejectedExecutionHandler build(List<RejectedExecutionHandler> chain) {
        Objects.requireNonNull(chain, "handlerChain must not be null");
        RejectedExecutionHandler[] handlerChain = chain.toArray(new RejectedExecutionHandler[0]);
        return new RejectedExecutionHandlerChain(handlerChain);
    }

    private RejectedExecutionHandlerChain(RejectedExecutionHandler[] handlerChain) {
        this.handlerChain = Objects.requireNonNull(handlerChain, "handlerChain must not be null");
    }

    @Override
    public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
        for (RejectedExecutionHandler rejectedExecutionHandler : handlerChain) {
            rejectedExecutionHandler.rejectedExecution(r, executor);
        }
    }
}

pinpoint的拒绝策略实现很有特点,和其他的实现都不同。他定义了一个拒绝策略链,包装了一个拒绝策略列表,当触发拒绝策略时,会将策略链中的rejectedExecution依次执行一遍

ActiveMQ 中的线程池拒绝策略

new RejectedExecutionHandler() {
    @Override
    public void rejectedExecution(final Runnable r, final ThreadPoolExecutor executor) {
        try {
            executor.getQueue().offer(r, 60, TimeUnit.SECONDS);
        } catch (InterruptedException e) {
            throw new RejectedExecutionException("Interrupted waiting for BrokerService.worker");
        }
        throw new RejectedExecutionException("Timed Out while attempting to enqueue Task.");
    }
});

activeMq中的策略属于最大努力执行任务型,当触发拒绝策略时,在尝试一分钟的时间重新将任务塞进任务队列,当一分钟超时还没成功时,就抛出异常。这种方式感觉可以用于这样的场景,比如接受设备上报的消息大部分情况下都是比较少能处理的过来,每个任务处理时间不长,只有很小概率消息任务数量增加,就可以阻塞一段时间,等待其他任务完成。

BlockingQueue的核心方法:
放入数据:
  offer(anObject):表示如果可能的话,将anObject加到BlockingQueue里,即如果BlockingQueue可以容纳,
    则返回true,否则返回false.(本方法不阻塞当前执行方法的线程)
  offer(E o, long timeout, TimeUnit unit),可以设定等待的时间,如果在指定的时间内,还不能往队列中
    加入BlockingQueue,则返回失败。
  put(anObject):把anObject加到BlockingQueue里,如果BlockQueue没有空间,则调用此方法的线程被阻塞
    直到BlockingQueue里面有空间再继续.
获取数据:
  poll(time):取走BlockingQueue里排在首位的对象,若不能立即取出,则可以等time参数规定的时间,
    取不到时返回null;
  poll(long timeout, TimeUnit unit):从BlockingQueue取出一个队首的对象,如果在指定时间内,
    队列一旦有数据可取,则立即返回队列中的数据。否则直到时间超时还没有数据可取,返回失败。
  take():取走BlockingQueue里排在首位的对象,若BlockingQueue为空,阻断进入等待状态直到
    BlockingQueue有新的数据被加入;
  drainTo():一次性从BlockingQueue获取所有可用的数据对象(还可以指定获取数据的个数),
    通过该方法,可以提升获取数据效率;不需要多次分批加锁或释放锁。

Java提供的几种线程池

请添加图片描述

  • FixedThreadPool(int n)

    new ThreadPoolExecutor(nThreads, nThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>());核心线程和最大线程都是n,使用LinkedBlockingQueue无界队列。所以当请求越来越多无法及时处理时,就会不断堆积,容易造成内存溢出OOM。

  • SingleThreadExecutor 单个工作线程的线程池

    new ThreadPoolExecutor(1, 1, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>())等同于FixedThreadPool(1),只有一个工作线程。保证提交的任务按顺序执行,所以可以用来解决面试经常问到的如何控制多个线程顺序打印’ABCABC’问题如何控制多个线程的执行顺序

  • CachedThreadPool

    底层是`return new ThreadPoolExecutor(0, Integer.MAX_VALUE, 60L, TimeUnit.SECONDS, new SynchronousQueue());,synchronousQueue是直接交换队列,不缓存任务。这个线程池会利用已有的线程,没有就新建,适合用来处理处理时间较短的异步任务,但是如果任务很多,会不断创建线程直到超出系统限制(如发生线程内存分配不足)。如果线程超过60s没有被使用,会被回收。

  • ScheduledThreadPool(int corePoolSize)

    可以用来执行一些简单的延迟或按固定频率的任务。 延迟5秒后执行任务scheduledExecutorService.schedule(thread,5, TimeUnit.SECONDS);
    在1s后执行一次,然后每隔3秒执行任务#scheduledExecutorService.scheduleAtFixedRate(thread,1,3,TimeUnit.SECONDS);

  • WorkStealingPool

    return new ForkJoinPool(parallelism, ForkJoinPool.defaultForkJoinWorkerThreadFactory, null, true);,JDK8加入的,返回一个工作窃取线程池ForkJoinPool,会维护足够的线程以支持给定的并行度级别,并且可以使用多个队列来减少争用。 并行度级别对应于主动参与或可用于参与任务处理的最大线程数。 线程的实际数量可能会动态增长和收缩。 工作窃取池不保证提交任务的执行顺序

    适合线程会产生子任务的,线程会把产生的子任务放进自己的工作队列,其他线程可以帮助这个线程执行。

Spring提供的线程池ThreadPoolTaskExecutor,方便我们自定义线程池:

@Configuration
@EnableAsync//开启异步任务的支持
public class TaskExecutorConfig {

    @Bean(value = "businessEventProcessTaskExecutor", destroyMethod = "destroy")
    public ThreadPoolTaskExecutor getAsyncExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 获取JAVA虚拟机的可用处理器数量。IO密集型建议核心线程数是该值2倍;计算密集型建议核心线程数是该值1倍
        int processorNum = Runtime.getRuntime().availableProcessors();

        // 设置核心线程数量。若池中的实际线程数小于该值,无论其中是否有空闲的线程,都会产生新的线程
        executor.setCorePoolSize(processorNum * 2);

        // 设置最大线程数量
        executor.setMaxPoolSize(processorNum * 4);

        // 设置阻塞任务队列大小
        executor.setQueueCapacity(100);

        // 线程名称前缀
        executor.setThreadNamePrefix("demo-");
        
        // 设置线程池中任务的等待时间,若超过等待时间仍未销毁则强制销毁,以确保应用最后能够被关闭,而不是阻塞住
        executor.setAwaitTerminationSeconds(AWAIT_TERMINATION_SECONDS);

        // 设置拒绝策略,当线程池阻塞队列已满时对新任务的处理。调节机制,即饱和时回退主线程执行
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}

线程池如何设置线程数:
耗时IO密集型:如读写数据库、文件、网络通信了,建议核心线程数是处理器数量的数倍,因为IO操作相对于CPU来说比较慢,为了不让CPU过多的等待空耗;
计算密集型:建议核心线程数是处理器数量的1-2倍,因为CPU基本一直在工作。

源码使用举例

【Rust线程池】如何构建Rust线程池、Rayon线程池用法详细解析 Rayon 是 Rust 中的一个并行计算库,它可以让你更容易地编写并行代码,以充分利用多核处理器。 Rayon 提供了一种简单的 API,允许你将迭代操作并行化,从而加速处理大规模数据集的能力。 除了这些核心功能外,它还提供构建线程池的能力。 阅读详情

相关推荐

Java 线程池全面解析

corePoolSize, // 核心线程数maximumPoolSize, // 最大线程数keepAliveTime, // 空闲线程存活时间TimeUnit.MILLISECONDS, // 时间单位new LinkedBlockingQueue<>(capacity), // 工作队列Executors.defaultThreadFactory(), // 线程工厂new ThreadPoolExecutor.AbortPolicy() // 拒绝策略优点可精确控制所有参数。

huing的博客 1770

重拾Java基础知识:线程池

线程池(英语:thread pool):一种线程使用模式。线程过多会带来调度开销,进而影响缓存局部性和整体性能。而线程池维护着多个线程,等待着监督管理者分配可并发执行的任务。这避免了在处理短时间任务时创建与销毁线程的代价。线程池不仅能够保证内核的充分利用,还能防止过分调度。可用线程数量应该取决于可用的并发处理器、处理器内核、内存、网络sockets等的数量。

码农的艺术 554

Java线程池原理解析

线程的消耗 1、其实 Java 中的线程模型是基于操作系统原生线程模型实现的,也就是说 Java 中的线程其实是基于内核线程实现的,线程的创建,析构与同步都需要进行系统调用,而系统调用需要在用户态与内核中来回切换,代价相对较高,线程的生命周期耗时包括「线程创建时间」,「线程执行任务时间」,「线程销毁时间」,创建和销毁都需要导致系统调用; 2、每个 Thread 都需要有一个内核线程的支持,也就意味着每个 Thread 都需要消耗一定的内核资源(如内核线程的栈空间),因此能创建的 Thread 是有限的; 3

renzhongrui的博客 505

java线程池

Java并发编程:线程池的使用   在前面的文章中,我们使用线程的时候就去创建一个线程,这样实现起来非常简便,但是就会有一个问题:   如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样频繁创建线程就会大大降低系统的效率,因为频繁创建线程和销毁线程需要时间。   那么有没有一种办法使得线程可以复用,就是执行完一个任务,并不被销毁,而是可以继续执行其他的任务?   

HGodsun的博客 526

从原理图到PCB:基于Allegro的8槽PXIe背板高速设计实战

本文详细介绍了基于Allegro软件进行8槽PXIe背板高速设计的完整实战流程。从理解规范、原理图设计、PCB布局规划,到高速PCIe差分对与精密时钟的布线艺术,再到电源与地处理以及最终的设计验证与生产文件输出,系统性地阐述了确保背板信号完整性、电源完整性和系统稳定性的关键技术与工程实践。

weixin_28312391的博客 202

Sentinel2 哨兵2数据下载方法(USGS)-史上最全讲解

本文讲述了从USGS官网下载Sentinel2的方法。主要包括USGS官网的注册和下载流程,以及选定研究区中所出现的问题、解决办法。

xiatiandexia123的博客 6万+

Java 多线程

文章目录线程RunnableThreadCallable线程池ThreadPoolExecutor基本状态提交任务基本流程rejectedExecutionHandler 拒绝策略ScheduledThreadPoolExecutorForkJoinPoolExecutor 线程 线程是轻量级子进程,是最小的处理单元 线程只能启动一次,第二次会IllegalThreadStateException start()而不是run() Runnable 实现Runnable 实现run()方法 Thre

刘斌的博客 627

yersinia -G打不开图形化界面解决方案(曲线救国,实操可行)

附Kali 2021 VMware懒人版,已完成汉化,更新源,并已经安装yersinia。ping 114.114.114.114也不通,接着ifconfig发现 IP地址有的,然后手动配置了IP地址,发现还是Ping不通114.11.4.114.114,然后输入。更新源后,报错源无法解析,同时ping www.baidu.com也不通,那可能是DNS存在问题,更新一下DNS服务器。百度找了很久,有个回答比较接近,但一一尝试了几遍,还是无解,解决不了呀!•(2)Yersinia编译的时候,要选择gtk。

weilandeyongheng的专栏 4167

Java线程池详解及四种线程池用法介绍

            Executor框架是一种将线程的创建和执行分离的机制。它基于Executor和ExecutorService接口,及这两个接口的实现类ThreadPoolExecutor展开,Executor有一个内部线程池,并提供了将任务传递到池中线程以获得执行的方法,可传递的任务有如下两种:通过Runnable接口实现的任务和通过Callable接口实现的任务。在这两种情况下,只需要...

__静禅__ 1014

从零到一:KUKA机械臂与ROS的工业级通讯协议深度解析

本文深度解析KUKA机械臂与ROS的工业级通讯协议,重点对比EKI与RSI两种核心接口的技术特点与应用场景。详细介绍了网络架构设计、数据包解析机制及ROS-I集成方法,并提供实时性能优化与故障排除策略,为工程师实现高可靠性工业自动化集成提供完整解决方案。

2a4s6d8f0g的博客 296

YOLOv10改进目录一览 | 涉及卷积层、轻量化、注意力、损失函数、Backbone、SPPF、Neck、检测头等全方位改进

如何寻找创新点?为什么要使用这个模块?如何才能提升模型的精度?这是贯穿我们研究始终的问题。创新点在这个专栏中我已经整理好了,这已经省去了大部分时间,但是当我们使用这些新的模块去优化已有的模型,如何才能提升模型的精度,才是我们要达到的最终目标。当然我们可以使用传统的A+B+C的方法去堆积模块,然后是进行大量的实验去排列组合以实现最终的精度提升,这无可厚非。

Limiiiing的博客 1万+

什么是线程池?从底层源码入手,深度解析线程池的工作原理

本文从底层源码入手,深度解析ThreadPoolExecutor底层源码,包括其核心字段、内部类和重要方法,另外对Executors工具类下的四种自带线程池源码进行解释。 阅读本文后,可以对线程池的工作原理、七大参数、生命周期、拒绝策略等内容拥有更深入的认识。

种一棵树最好的时间是十年前,其次是现在 2255

基于Cesium的简易淹没分析效果

淹没分析是通过模拟水体扩散过程来评估洪水、海平面上升等情景下可能影响的区域,其核心作用是为防灾减灾、规划设计和风险管理提供科学依据。预测淹没的范围、水深和持续时间,识别高风险区域,帮助政府制定应急预案和疏散路线,减少人员伤亡和财产损失。

星云数海的博客,一个专注GIS十多年的GISer人。 531

python3 线程池源码解析_python线程池源码解析

本篇主要讲下ThreadPoolExecutor的实现。由于业务量不大,且一直使用框架进行编程,对线程的理解一直很模糊,基本处于不想阻塞程序执行,起一个线程启动任务的阶段。总感觉自己好像会线程一样,实则一直处于一种懵懂状态,通过一段时间查看一些别人写的源码,终于有所悟,也记录下自己的学习过程吧,由于本人水平限制理解有限,抛砖引玉一下。关于线程的源码没什么好说的,大家可以可以自行查看,应该是平常编程...

weixin_39850697的博客 179

074.多任务学习:在YOLO基础上同时进行分割、关键点检测

本文探讨了在YOLOv5模型中实现检测、分割和关键点多任务的技术方案。通过分层特征抽取策略(检测用深层、分割用浅深融合、关键点用中层)解决特征共享问题;采用不确定性加权方法自动平衡多任务损失;设计了支持三种标签的数据流处理方式;并分享了推理部署时的后处理技巧。实践表明,合理的多任务设计可以提升主任务性能,但需权衡标注成本和推理效率。最终方案选择在训练时引入分割作为辅助任务,推理时仅保留检测和关键点,实现了检测mAP提升2.3%的优化效果。

2603_95664987的博客 44

深度解析:企业AI效能度量框架的核心指标与落地路径,收藏级干货

资源成本:GPU利用率从31%提升到75%,云费用减少了50万元/年;工程效率:模型迭代周期从2周缩短到3天,部署成功率从70%提升到95%;模型性能:CTR从8%提升到12%,CVR从3%提升到4.5%;业务价值:增量收入达200万元/月,ROI达180%;伦理风险:推荐结果的差异影响比从0.7提升到0.9,用户投诉率下降了30%。现在,当CEO再问「AI项目值多少钱?

AI天才研究院 1807

线程池源码解析

1. 线程池 Java引入Excutor框架将任务的提交和执行进行解耦,只需要定义好任务,然后提交给线程池即可。 1.1 线程池的3个好处 降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。 提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 1.2 线程池实现类ThreadPoolExecutor的构造方法参数 构造方法

qq_41159942的博客 909

蓝牙模块HM-10手册

蓝牙模块HM-10手册,该手册对蓝牙模块HM-10进行了详细的介绍和使用方法以及一些AT指令。

java 线程池源码解析

java中的线程池通常指的是ThreadPoolExecutor,本文主要解析线程池执行过程中的核心代码。 先来说一下什么是线程池以及线程池是做什么的吧。我们平常所写的那些java代码(不含Thread,Runnable的)都是单线程的,比如这样 public static void main(String[] args) throws InterruptedException { // T...

nayi_224的博客 591

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

04-Java 线程池原理与源码全解析

本篇将深入剖析 Java 线程池的核心设计思想、工作原理及源码实现,涵盖 `ThreadPoolExecutor` 关键参数、任务调度流程、拒绝策略等核心机制,并结合实际场景给出最佳实践建议。

無言的博客 1351

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

线程池及Executor的源码解析

线程池的继承结构: 首先,我们为什么要使用线程池?因为使用线程池好处多多: 1、降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 2、提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。 3、提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。 然后,我们来看看线程池的继承结...

haoyuegongzi的博客 350

Java之线程池源码解析

线程池 文章目录线程池线程池概述创建一个线程池并提交线程任务线程池源码解析参数认识构造方法提交任务addWorker执行任务关闭线程池 线程池概述 什么是线程池 为什么使用线程池 线程池的优势 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。 第二:提高响应速度。当任务到达时,任务可以不需要的等到线程创建就能立即执行。 第三:提高线程的可管理性。线...

https://gitee.com/micai-code 492

java线程池JVM源码解析

java线程池JVM源码解析 概述 对于java的线程池,刚开始接触的时候可能就知道使用Executors.newFix……之类的方法,java线程池有三种; 固定大小的 无限伸展的 单个的single 还有就是大于核心池子怎么样。其实际上这个三个池子都是基于java提供的最基本的 ThreadPoolExecutor 进行开发的,只是传递了不同的参数而已。 线程池出现的背景...

sam_java_xp的博客 426

java线程池详解及源码解析

线程池详解 文章目录线程池详解线程池参数常用的六种线程池FixedThreadPoolCachedThreadPoolScheduledThreadPoolSingleThreadExecutorSingleThreadScheduledExecutor几种线程池参数对比ForkJoinPool核心问题拒绝策略拒绝时机AbortPolicyDiscardPolicyDiscardOldestPolicyCallerRunsPolicy常用几种阻塞队列LinkedBlockingQueueSynchronou

yetaoii的博客 503

Java线程池核心源码解析

线程池中的Worker是什么,和线程是什么关系线程池是如何保持线程不被回收的线程池中核心线程和非核心线程是如何区分。

qq_33841411的博客 1272

Java线程池解析

Java线程池解析1、线程池概念2、线程池的创建3、任务执行3.1 线程池执行流程,即对应execute()方法:3.2 四种拒绝策略3.3 为了形象描述线程池执行,我打个比喻:4、线程池异常处理4.1 当提交新任务时,异常如何处理?4.2 线程池exec.submit(runnable)的执行流程4.3 其他两种处理线程池异常方案4.4 因此,被问到线程池异常处理,如何回答?5、线程池的工作队列5.1 ArrayBlockingQueue5.2 LinkedBlockingQueue5.3 DelayQu

爱是与世界平行 811
上一篇: spring-boot-maven-plugin插件找不到含有main的主类
余生之君
博客等级 码龄10年 246粉丝 98原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值