精华内容
下载资源
问答
  • 线程池的底层工作原理
    2019-10-16 16:51:45

    1. 在创建了线程池后,等待提交过来的任务请求。

    2. 当调用execute方法添加一个请求任务时,线程池会做如下判断:

        2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;

        2.2 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列

        2.3 如果这时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;

        2.4 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。

    3. 当一个线程完成任务时,它会从队列中取下一个任务来执行。

    4. 当一个线程无事可做超过一定的时间(keepAliveTime)时,线程池会判断:

        如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。

        所欲线程池的所有任务完成后它最终会收缩到corePoolSize的大小。

    更多相关内容
  • 线程池底层原理

    2021-12-27 17:55:57
    使用线程池进行线程复用 线程池 提前创建一系列的线程,保存在这个线程池中。(核心线程) 有任务要执行的时候,从线程池中取出线程来执行。 没有任务的时候,线程池放回去。 Java中提供的线程池 Executors 1,...

    new Thread().start(); 1. 线程的频繁创建好销毁 2. 线程的数量过多,会造成CPU资源的开销。 上下文切换 (消耗CPU资源)

    使用线程池进行线程复用

    线程池

    提前创建一系列的线程,保存在这个线程池中。(核心线程) 有任务要执行的时候,从线程池中取出线程来执行。 没有任务的时候,线程池放回去。

    Java中提供的线程池

    Executors

    1,newFixedThreadPool 固定线程数量

    public static ExecutorService newFixedThreadPool(int nThreads) {
    		return new ThreadPoolExecutor(nThreads, nThreads,
    			0L, TimeUnit.MILLISECONDS,
    			new LinkedBlockingQueue());
         }

    2,newSingleThreadExecutor 只有一个线程的线程池

    public static ExecutorService newSingleThreadExecutor() {
        return new FinalizableDelegatedExecutorService
            (new ThreadPoolExecutor(1, 1,
                                    0L, TimeUnit.MILLISECONDS,
                                    new LinkedBlockingQueue<Runnable>()));
    }

    3,newCachedThreadPool 可以缓存的线程池 ->理论上来说,有多少请求,该线程池就可以创建多 少的线程来处理。

    public static ExecutorService newCachedThreadPool() {
        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                      60L, TimeUnit.SECONDS,
                                      new SynchronousQueue<Runnable>());
    }

    4,newScheduledThreadPool //提供了按照周期执行的线程池. ->Timer

    public static ScheduledExecutorService newScheduledThreadPool(int corePoolSize) {
        return new ScheduledThreadPoolExecutor(corePoolSize);
    }

    Executors底层都是ThreadPoolExecutor实现的

    ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize,//核心线程数
    
                              int maximumPoolSize,//最大线程数
    
                              long keepAliveTime,//存活时间
    
                              TimeUnit unit, //存活单位
    
                              BlockingQueue<Runnable> workQueue, //阻塞队列
                              ThreadFactory threadFactory, //线程工厂,用来创建工作线程的。 默认实现(自定义线程池中线程的名字)
    
                              RejectedExecutionHandler handler) {//拒绝执行策略 。默认实现
        if (corePoolSize < 0 ||
            maximumPoolSize <= 0 ||
            maximumPoolSize < corePoolSize ||
            keepAliveTime < 0)
            throw new IllegalArgumentException();
        if (workQueue == null || threadFactory == null || handler == null)
            throw new NullPointerException();
        this.corePoolSize = corePoolSize;
        this.maximumPoolSize = maximumPoolSize;
        this.workQueue = workQueue;
        this.keepAliveTime = unit.toNanos(keepAliveTime);
        this.threadFactory = threadFactory;
        this.handler = handler;
    }

    如何线程的复用. 1,[共享内存]->BlockingQueue 2,有任务来的时候,执行 3,没有任务的时候,阻塞 结论: 通过阻塞队列的方式,来实现线程池中线程的复用。 线程池的实现原理的过程推演

    queue.take()和queue.poll(timeout)都是阻塞的状态,当线程池中没有线程任务(即阻塞队列中没有线程任务)的时候,会阻塞一短时间(keepAliveTime,//存活时间),超过这个时间,说明当前线程空闲时间超过了设置的时间keepAliveTime,该线程需要回收,摧毁

    线程回收一般是回收 > corePoolSize,//核心线程以外的线程(即maximumPoolSize-corePoolSize 之间的线程 ),

    而corePoolSize线程默认是不会回收的,会一致空闲的,知道线程池关闭,核心线程才会摧毁,回收

    private Runnable getTask() {
        boolean timedOut = false; // Did the last poll() time out?
    
        for (; ; ) {
            int c = ctl.get();
            int rs = runStateOf(c);
    
            // 如果线程池已经结束状态,即线程池关闭了,直接返回null. 需要清理掉线程池中所有的工作线程
    
            if (rs >= SHUTDOWN && (rs >= STOP || workQueue.isEmpty())) {
                decrementWorkerCount();
                return null;
            }
    
            int wc = workerCountOf(c); // 当前线程池中的线程数
    
            // allowCoreThreadTimeOut 核心线程是否允许超时回收,默认是false,不回收
            // wc > corePoolSize;对于大于核心线程的非核心线程会进行超时回收
            boolean timed = allowCoreThreadTimeOut || wc > corePoolSize;
            // timedOut 为true,只有是线程超时了可以被回收了,才会是true
            if ((wc > maximumPoolSize || (timed && timedOut))
                    && (wc > 1 || workQueue.isEmpty())) {
                if (compareAndDecrementWorkerCount(c))
                    return null;
                continue;
            }
            try {
                // 下面两种情况,只在阻塞队列为空,线程池没有任务的时候,核心线程和非核心线程空闲
                // 1,timed = true代表该线程超时了可以被回收,一般是非核心线程(>corePoolSized的线程)
                // workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) 就不会一直阻塞在这,会返回r=null,
                // 所以timedOut = true;在上面该线程就会回收
                // 2,例如核心线程数(<corePoolSized的线程),不会被回收,因为
                // boolean timed = allowCoreThreadTimeOut || wc > corePoolSize; timed = false
                // workQueue.take(); 会一致被阻塞这
                Runnable r = timed ?
                        workQueue.poll(keepAliveTime, TimeUnit.NANOSECONDS) :
                        workQueue.take();
                if (r != null)
                    return r;
                timedOut = true;
            } catch (InterruptedException retry) {
                timedOut = false;
            }
        }
    }

     源码分析

    public void execute(Runnable command) {
        if (command == null)
            throw new NullPointerException();
        int c = ctl.get();
        //判断当前工作线程数是否小于核心线程数(延迟初始化)
    
        if (workerCountOf(c) < corePoolSize) {
            if (addWorker(command, true)) //添加核心工作线程的同时,执行command
    
                return;
            c = ctl.get();
        }
        //线程池中线程大于核心线程数,workQueue.offer 添加到阻塞队列,返回false说明阻塞队列满,
        // 将Worker任务添加到阻塞队列中,在runWorker()中会去从队列获取任务执行(如果当前Worker线程有任务
        // 就会执行当前线程自己的任务,如果当前线程没有任务就会从workQueue阻塞队列中获取任务执行)
        if (isRunning(c) && workQueue.offer(command)) {
            int recheck = ctl.get();
            if (! isRunning(recheck) && remove(command))
                reject(command);
            else if (workerCountOf(recheck) == 0)
                addWorker(null, false);
        }
        // 如果阻塞队列满了,则添加非核心工作线程(扩容的线程)
        else if (!addWorker(command, false))
            reject(command); // 创建非核心线程之后线程池中的线程大于最大线程数,执行拒绝策略
    }
    
    // 添加工作线程逻辑,并封装成Worker对象,去开去线程,
    // 最终调用Worker的run()在run()方法中会调用Runnable firstTask任务的run()方法,这个主动调用
    private boolean addWorker(Runnable firstTask, boolean core) {
        retry:
        for (;;) { //case1 通过原子操作来增加线程数量.
    
            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 ||
                    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
            }
        }
        //case2 初始化工作线程.
    
        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();
        		 // private final HashSet<Worker> workers = new HashSet<Worker>();
           		 // 这这个workers的set是存放线程池中的线程容器,使用set来存放线程的
                        workers.add(w); //添加到一个容器中。
                        int s = workers.size();
                        if (s > largestPoolSize) 
                            largestPoolSize = s; //重新更新largestPoolSize
                        workerAdded = true; //添加成功。
    
                    }
                } finally {
                    mainLock.unlock();
                }
                if (workerAdded) {
                    //工作线程创建完成并且成功添加到了workers线程池的容器中,就启动线程
                    // 此时,会调用到Worker对象中的run()方法,Worker在线程池中就相当与一个工作线程
                    t.start(); 
                    workerStarted = true;
                }
            }
        } finally {
            if (! workerStarted)
                addWorkerFailed(w);
        }
        return workerStarted;
    }
    
    // 工作线程worker,可以看到继承了Runnable,通过worker中的
    private final class Worker
        extends AbstractQueuedSynchronizer
        implements Runnable{
    
        private static final long serialVersionUID = 6138294804551838833L;
    
        /** Thread this worer is running in.  Null if factory fails. */
        final Thread thread;
        /** Initial task to run.  Possibly null. */
        Runnable firstTask;
        /** Per-thread task counter */
        volatile long completedTasks;
    
        /**
         * Creates with given first task and thread from ThreadFactory.
         * @param firstTask the first task (null if none)
         */
        Worker(Runnable firstTask) {
            setState(-1); // inhibit interrupts until runWorker
            this.firstTask = firstTask;
            // 将Worker本身作为线程任务,addWorker启动start()任务机会调到run()
            this.thread = getThreadFactory().newThread(this);
        }
        // 工作线程执行线程人物的入口
        public void run() {
            	// 这里会调用我们传进来的Runnable firstTask,就是我们添加到线程池的真正的任务的run()方法,
              // 这里是直接通过实例调用
        	runWorker(this);
        }
    }
    
    // 执行线程任务
    final void runWorker(Worker w) {
        Thread wt = Thread.currentThread();
        // 拿到当前线程的任务,可能为空,也就是说当前线程自身没有任务了,
        Runnable task = w.firstTask;
        w.firstTask = null;
        w.unlock(); // allow interrupts
        boolean completedAbruptly = true;
        try {
            // 1,while循环保证当前线程不结束. 直到task为null,在getTask()中从阻塞队列中获取任务的时候
            // 如果阻塞队列中没有任务会阻塞,至到线程池中非核心线程数空闲时间达到了设置的时间就会返回null,
            // 线程运行结束,线程摧毁,
            // 2,线程池中的核心线程数默认是不摧毁的,只有在线程池关闭的时候全部摧毁
            // 3,如如果当前Worker线程已经持有任务,就执行自己的任务,如果没有任务就从阻塞队列中获取任务执行
            while (task != null || (task = getTask()) != null) {
                //表示当前线程正在运行一个任务,如果其他地方要shutdown().你必须要等我执行完成。
                w.lock(); //Worker继承了AQS -> 实现了互斥锁
    
                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);
        }
    }

    线程数量设置

    1,IO密集型 CPU 2core+1

    CPU利用率不高 IO密集型,是执行的任务run()方法计算比较大,IO比较多,IO阻塞会释放CPU的执行权,所以CPU利用率不高,可以多开一些线程任务,来提高并发处理能力,所以可以设置为 = CPU核数*2 + 1 2,CPU密集型 CPU +1

    CPU密集型 是线程任务run()处理时间比较短,线程任务量比较大,所以CPU利用率比较高,所以CPU会频繁的进行上下文切换,导致系统性能降低,所以这种CPU密集型线程数不能设置过高,可以 = CPU核数 +1 CPU利用率很高,会增加上下文切换

    展开全文
  • Java并发编程-线程池底层工作原理

    千次阅读 2021-01-29 19:36:57
    关于线程池的具体介绍,可以参看ThreadPool线程池,这里主要介绍线程池底层工作原理。 1.线程池的底层工作原理 1.1.线程池的底层工作原理图 线程池的主要处理原理图和流程图如下 1.2.银行办理业务案例 为了便于...

    关于线程池的具体介绍,可以参看ThreadPool线程池,这里主要介绍线程池底层工作原理。

    1.线程池的底层工作流程

    1.1.线程池的底层工作原理图

    线程池的主要处理原理图和流程图如下
    在这里插入图片描述
    在这里插入图片描述

    1.2.银行办理业务案例

    为了便于理解,我们用银行的例子来说明银行网=线程池
    corePoolsize=今日当值窗口。今日当值窗口由两个,当银行客人来了之后,先去候客厅等着,现在来了3个即3,4,5都来了,在候客厅等着,此时候客区也满了。突然,6,7,8三个人又来了,由于候客区也满了,此时可以用到maximumPoolSize,
    它即银行的扩容窗口。此时银行经理可以商量增加窗口,即扩容。然后就增加了3个扩容窗口。从核心数扩容到最大数。此时候客区的客人3,4,5就可以去新增窗口办理服务。而6,7,8就去候客区等待。
    maximumPool包含corePool
    如果此时又来了两个客户9,10。此时当值窗口、新增窗口和候客区都已经满了,只能采用拒绝策略。经理在门口立个牌子,今日客满。9,10看到了只能走了。
    随着时间的流逝,当某些客户被处理完时,业务量也随之下降,3,4,5办理完了,6,7,8上来之后也办理完了。keepAlive指的是在多长时间以内,没有收到新的需求了,线程池就会缩过来,恢复到今日单值窗口。

    银行办理业务的流程可总结如下:

    N个窗口 : corePoolSize

    总共有K个窗口:maximumPoolSize

    候客厅 :BlockedQueue

    1 银行开门,等待顾客上门办理业务
    2 今天开N个窗口,有N个柜员提供服务。
    	2.1 如果顾客数小于等于N,可以立即为每一个顾客办理业务,不用等待。
        2.2 如果又来了M个顾客,M个顾客在候客厅坐得下,那么顾客就在候客厅等候
        2.3 如果候客厅坐满了,银行大堂经理发现窗口不够用,叫人过来加班,开启其他没有工作的窗口。
        2.4 如果候客厅满了,开启的工作窗口也是最大数K,就会启动拒绝策略。(今天人太多了,明天来吧;或者您等会再来看看,等人少的时候来)
    3 当一个顾客的业务办理完成后,叫一下个顾客来办理业务
    4 当人数很少的时候,额外加班的窗口会慢慢关闭,留下N个窗口。
    

    在这里插入图片描述
    即上面的四大步骤:当值窗口(核心线程)->候客区(阻塞队列)->扩容窗口(最大线程数)->拒绝策略。

    1.3.线程池的底层工作流程总结

    以下重要:

    1 在创建了线程池后,等待提交过来的任务请求。
    2 当调用execute()方法添加一个请求任务时,线程池会做如下判断:
      2.1 如果正在运行的线程数量小于 corePoolSize,那么马上创建马上创建线程运行这个任务。
      2.2 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列。
      2.3 如果这个时候队列满了且正在运行的线程数量还小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务。(队列出列,刚来的去队列中)
      2.4 如果队列满了且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会启动饱和拒绝策略来执行。
    3.当一个线程完成任务时,它会从队列中取下一个任务来执行。
    4.当一个线程无事可做超过一定的时间(keepAlilveTime)时,线程池会判断:
    如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。
    所以线程池的所有任务完成后它最终会收缩到corePoolSize的大小

    2.线程池用哪个?生产中如何设置合理参数

    2.1.在工作中单一的/固定数的/可变的三种创建线程池的方法哪个用的多?超级大坑

    答案时一个都不用,我们工作中只能使用自定义的
    Executors中JDK已经给你提供了?为什么不用?
    以下是阿里开发手册中的内容:

    【强制】线程资源必须通过线程池提供,不允许在应用中自行显式创建线程。
    说明:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题。

    【强制】线程池不允许使用Executors去创建,而是通过ThreadPooLExecutor的方式,这样的处理方式让写的同学更加明确线程池的运行规则,规避资源耗尽的风险

    说明:Executors返回的线程池对象的弊端如下:
    1)FixedThreadPool和SingleThreadPool:允许的请求队列长度为Integer.MAX_VALUE,可能会堆积大量的请求,从而导致OOM(out of memory)。

    2)CachedThreadPool和ScheduledThreadPool:允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量的线程,从而导致OOM。

    public static ExecutorService newCachedThreadPool() {
            return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
                                          60L, TimeUnit.SECONDS,
                                          new SynchronousQueue<Runnable>());
    }
    

    2.2.在工作中如何使用线程池,是否自定义过线程池

    public static void main(String[] args) {
    		ExecutorService threadPool=new ThreadPoolExecutor(2, 5, 2000L,TimeUnit.MILLISECONDS, 
    				new LinkedBlockingQueue<>(3),
    				Executors.defaultThreadFactory(),
    				new ThreadPoolExecutor.AbortPolicy());
    		try {
    			//模拟有6个顾客过来银行办理业务
    			for(int i=1;i<=9;i++){
    				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
    				threadPool.execute(()->{
    					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
    				});
    			}
    		} catch (Exception e) {
    			// TODO: handle exception
    		}finally{
    			threadPool.shutdown();
    		}		
    	}
    

    在这里插入图片描述

    public static void main(String[] args) {
    		ExecutorService threadPool=new ThreadPoolExecutor(2, 5, 500L,TimeUnit.MILLISECONDS, 
    				new LinkedBlockingQueue<>(3),
    				Executors.defaultThreadFactory(),
    				new ThreadPoolExecutor.AbortPolicy());
    		try {
    			//模拟有6个顾客过来银行办理业务
    			for(int i=1;i<=9;i++){
    				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
    				threadPool.execute(()->{
    					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
    				});
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			threadPool.shutdown();
    		}		
    	}
    

    在这里插入图片描述

    而执行了9次之后,则可能会报错,说明这个池子最多容纳5+3=8.
    最大容纳数=队列数+最大线程数

    报的异常是java.util.concurrent.RejectedExecutionException: 拒绝执行异常。也是默认策略报的异常。

    3.线程池的拒绝策略

    3.1.是什么

    等待队列已经排满了,再也塞不下新任务了。
    同时,
    线程池的中max线程也达到了,无法继续为新任务服务。

    这个时候我们就需要拒绝策略机制合理的处理这个问题。

    3.2.线程池的四大拒绝策略

    3.2.1.AbortPolicy(默认)拒绝策略

    直接抛出RejectedExecutionException异常阻止系统正常运行。
    在上面已经演示过

    3.2.2.CallerRunsPolicy拒绝策略

    调用者运行一种调节机制,该策略既不会抛弃任务,也不会抛出异常,而是将某些任务回退到调用者,从而降低新任务的流量。
    谁调用我你就找谁。也就是打回去,谁让你找我你就找谁。

    public static void main(String[] args) {
    		ExecutorService threadPool=new ThreadPoolExecutor(2, 5, 500L,TimeUnit.MILLISECONDS, 
    				new LinkedBlockingQueue<>(3),
    				Executors.defaultThreadFactory(),
    				new ThreadPoolExecutor.CallerRunsPolicy());
    		try {
    			//模拟有6个顾客过来银行办理业务
    			for(int i=1;i<=10;i++){
    				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
    				threadPool.execute(()->{
    					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
    				});
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			threadPool.shutdown();
    		}		
    	}
    

    在这里插入图片描述

    是main让其来找线程池的,现在线程池办理不了了,就把它回退给main函数。

    3.2.3.DiscardPolicy拒绝策略

    该策略默默地丢弃无法处理的任务,不予任何处理也不抛出异常,如果允许任务丢失,这是最好的一种策略。

    public static void main(String[] args) {
    		ExecutorService threadPool=new ThreadPoolExecutor(2, 5, 500L,TimeUnit.MILLISECONDS, 
    				new LinkedBlockingQueue<>(3),
    				Executors.defaultThreadFactory(),
    				new ThreadPoolExecutor.DiscardPolicy());
    		try {
    			//模拟有6个顾客过来银行办理业务
    			for(int i=1;i<=10;i++){
    				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
    				threadPool.execute(()->{
    					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
    				});
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			threadPool.shutdown();
    		}		
    	}
    

    在这里插入图片描述
    这里只打印出了8个,还有两个线程被丢弃掉。这就要求我们的事务满足弱一致性,允许少部分的丢失。

    3.2.4.DiscardOldestPolicy拒绝策略

    抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务。

    public static void main(String[] args) {
    		ExecutorService threadPool=new ThreadPoolExecutor(2, 5, 500L,TimeUnit.MILLISECONDS, 
    				new LinkedBlockingQueue<>(3),
    				Executors.defaultThreadFactory(),
    				new ThreadPoolExecutor.DiscardOldestPolicy());
    		try {
    			//模拟有6个顾客过来银行办理业务
    			for(int i=1;i<=10;i++){
    				//execute方法里面有一个参数,参数类型是Runnable,Runnable是函数式接口,可以用lambda表达式,并且runnable就是这10个顾客
    				threadPool.execute(()->{
    					System.out.println(Thread.currentThread().getName()+"\t 办理业务");
    				});
    			}
    		} catch (Exception e) {
    			e.printStackTrace();
    		}finally{
    			threadPool.shutdown();
    		}		
    	}
    

    在这里插入图片描述

    4.配置线程池

    Runtime.getRuntime().availableProcessors() 查看核心数

    System.out.println(Runtime.getRuntime().availableProcessors());
    

    在这里插入图片描述

    corePoolSize :1或者0

    如何设置 maximumPoolSize,分情况考虑,要看业务是CPU密集型还是IO密集型

    4.1.CPU密集型

    CPU密集的意思是该任务需要大量的运算,而没有阻塞,CPU一直全速运行。

    CPU密集任务只有在真正的多核CPU上才可能得到加速(通过多线程),而在单核CPU上,无论你开几个模拟的多线程该任务都不可能得到加速,因为CPU总的运算能力就那些。

    CPU密集型任务配置尽可能少的线程数量:

    一般公式:CPU核数+1个线程的线程池

    4.2.IO密集型

    比如:数据库读写

    由于IO密集型任务线程并不是一直在执行任务,则应配置尽可能多的线程,如CPU核数*2

    IO密集型,即该任务需要大量的IO,即大量的阻塞。

    在单线程上运行IO密集型的任务会导致浪费大量的CPU运算能力浪费在等待。

    所以在IO密集型任务中使用多线程可以大大的加速程序运行,即使在单核CPU上,这种加速主要就是利用了被浪费掉的阻塞时间。

    IO密集型时,大部分线程都阻塞,故需要多配置线程数:

    参考公式:CPU核数/1-阻塞系数阻塞系数在0.8~0.9之间比如8核CPU:8/(1-0.9)=80个线程数

    展开全文
  • 线程池的好处 降低资源消耗:通过重复利用已经创建的线程降低线程创建和销毁造成的消耗; 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能执行; 提高线程的可管理性:线程是稀缺资源,不能无限创建...
  • 当调用execute()方法添加一个请求时,线程池会做如下判断: 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务; 如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列; ...
  • 线程池底层实现原理相关面试题-(面试必问)
  • Java线程池底层原理

    2021-12-16 16:25:27
    下面三种线程池底层都是调用的ThreadpoolExecutor这个方法,ThreadpoolExecutor()构造方法包含七大参数,想要了解线程池底层原理,必须掌握这七大参数的含义 Executors.newFixedThreadPool(int) Executors....
  • 上面两张图结合起来看我们就可以知道线程池工作原理: 当调用线程池的 execute() 方法时,线程池会做出以下判断: 如果当前运行的线程小于线程池的核心线程数,那么马上创建线程完成这个任务。 如果运行中的线程...
  • 1.若正在运行的线程数量小于corePoolSize,则线程池马上创建线程执行任务 2.如果正在运行的线程数量大于等于corePoolSize,则多余的任务加入阻塞队列等待 3.如果队列满了且正在运行的线程数量小于maximumPoolSize...
  • 目录1. 谈谈什么是线程池2.... 线程池底层 ThreadPoolExecutor 底层实现原理11. 线程池队列满了,任务会丢失吗12. 线程池拒绝策略类型有哪些呢13. 线程池如何合理配置参数 1. 谈谈什么是线程池 线程池
  • 通过分析ThreadPoolExecutor了解更多线程池底层实现原理
  • 1.在创建了线程池后,等待提交过来的任务请求。 2.在调用execute()方法添加一个请求任务是,线程池会做如下判断: 2.1 如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务; 2.2 如果正在运行...
  • 线程池拒绝策略生效的条件: 1.当阻塞队列里面的线程数已经达到最大 2.当线程池的线程数量已经达到max值 满足这两个条件时,拒绝策略就会生效
  • public class ThreadPoolDemo { public static void main(String[] args) { System.out.println(Runtime.getRuntime...keepalivetime一定时间内没有收到其他请求 则会停止线程(也就是让加班的走) 线程池底层工作原理
  • 线程池相关的接口实现类关系 Exectutor是接口 Executors 是辅助工具类 ...线程池底层原理及七大参数的意义 七大参数 public ThreadPoolExecutor( int corePoolSize,//核心线程数 int maximumPoolSize,//最大线程
  • (四)深入理解线程池底层原理

    万次阅读 多人点赞 2018-08-10 16:37:11
    如何理解线程池工作机制和原理? (1)线程池是用来干嘛的,用它有什么好处,怎么能更好的去用线程池线程池是用来干嘛的? 如果并发的线程数量很多,并且每个线程都是执行一个时间很短的任务就结束了,这样...
  • 线程池底层实现原理

    千次阅读 2020-05-16 11:15:19
    文章目录newFixedThreadPool(int nThread)newSingleThreadExecutor()newCachedThreadPool()线程池的七大参数corePoolSizemaximumPoolSizekeepAliveTimeunitworkQueuethreadFactoryhandler线程池底层工作原理线程池...
  • java并发线程池底层原理详解和源码分析 上篇分析了java线程。现在来分析java线程池。在分析线程池之前,先来思考下我们的线程是创建的越多越好吗,显然不是,我们为什么要使用线程池,用下面的例子来看下 /*** * ...
  • 而使用线程池则可以减少创建和销毁线程的次数,对每个线程可以进行复用,提供使用效率,减少资源的消耗。 1. 线程池的创建 public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize,
  • 线程池底层⼯作原理 线程池内部是通过队列+线程实现的,当我们利⽤线程池执⾏任务时: 如果此时线程池中的线程数量⼩于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务...
  • 线程池相关介绍和底层原理一、为什么使用线程池二、常用的线程池FixedThreadPoolSingleThreadExecutorCachedThreadPool三、线程池的七大参数四、线程池底层原理五、线程池的拒绝策略六、线程池工作中的使用结尾 ...
  • 线程池底层原理

    2019-07-22 18:05:33
    什么是线程池? 答: 线程池是指在初始化一个多线程应用程序过程中创建一个线程集合,然后在需要执行新的任务时重用这些线程而不是新建一个线程(提高线程复用,减少性能开销)。线程池中线程的数量通常完全取决于...
  • 线程池的实现以及底层原理.docx
  • 1、线程池 1.1、什么是线程池? 线程池就是创建若干个可执行的线程放入一个池(容器)中,有任务需要处理时,会提交到线程池中的任务队列,处理完之后线程并不会被销毁,而是仍然在线程池中等待下一个任务 1.2、为...
  • 在平常的开发中,线程池是经常有所接触的。当没有使用线程池的时候,执行异步任务就需要使用new创建一个线程来执行任务,在任务完成后线程便直接销毁了,这样的操作是有很大的开销的。而线程池为了解决在大量执行...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 58,087
精华内容 23,234
关键字:

线程池的底层工作原理