Java-线程池专题 (美团面试题)

并发编程面试题 单例模式中的懒汉机制中,就存在一个这样的问题。懒汉为了保证线程安全,一般会采用DCL的方式。但是单单用DCL,依然会有几率出现问题。线程可能会拿到初始化一半的对象去操作,极有可能出现NullPointException。(初始化对象三部,开辟空间,初始化内部属性,指针指向引用)在Java编译.java为.class时,会基于JIT做优化,将指令的顺序做调整,从而提升执行效率。在CPU层面,也会对一些执行进行重新排序,从而提升执行效率。这种指令的调整,在一些特殊的操作上,会导致出现问题。 阅读详情

Java-线程池专题 (美团面试题)

去美团面试,问到了什么是线程池,如何使用,为什么要用,以下做个总结

1、什么是线程池:  java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池

多线程技术主要解决处理器单元内多个线程执行的问题,它可以显著减少处理器单元的闲置时间,增加处理器单元的吞吐能力。    
    假设一个服务器完成一项任务所需时间为:T1 创建线程时间,T2 在线程中执行任务的时间,T3 销毁线程时间。

   如果:T1 + T3 远大于 T2,则可以采用线程池,以提高服务器性能。

     一个线程池包括以下四个基本组成部分:
                1、线程池管理器(ThreadPool):用于创建并管理线程池,包括 创建线程池,销毁线程池,添加新任务;
                2、工作线程(PoolWorker):线程池中线程,在没有任务时处于等待状态,可以循环的执行任务;
                3、任务接口(Task):每个任务必须实现的接口,以供工作线程调度任务的执行,它主要规定了任务的入口,任务执行完后的收尾工作,任务的执行状态等;
                4、任务队列(taskQueue):用于存放没有处理的任务。提供一种缓冲机制。

   线程池技术正是关注如何缩短或调整T1,T3时间的技术,从而提高服务器程序性能的。它把T1,T3分别安排在服务器程序的启动和结束的时间段或者一些空闲的时间段,这样在服务器程序处理客户请求时,不会有T1,T3的开销了。
    线程池不仅调整T1,T3产生的时间段,而且它还显著减少了创建线程的数目,看一个例子:
    假设一个服务器一天要处理50000个请求,并且每个请求需要一个单独的线程完成。在线程池中,线程数一般是固定的,所以产生线程总数不会超过线程池中线程的数目,而如果服务器不利用线程池来处理这些请求则线程总数为50000。一般线程池大小是远小于50000。所以利用线程池的服务器程序不会为了创建50000而在处理请求时浪费时间,从而提高效率。

 

 

2.常见线程池
①newSingleThreadExecutor
单个线程的线程池,即线程池中每次只有一个线程工作,单线程串行执行任务
②newFixedThreadExecutor(n)
固定数量的线程池,没提交一个任务就是一个线程,直到达到线程池的最大数量,然后后面进入等待队列直到前面的任务完成才继续执行
③newCacheThreadExecutor(推荐使用)
可缓存线程池, 当线程池大小超过了处理任务所需的线程,那么就会回收部分空闲(一般是60秒无执行)的线程,当有任务来时,又智能的添加新线程来执行。
④newScheduleThreadExecutor
大小无限制的线程池,支持定时和周期性的执行线程

 

  java提供的线程池更加强大,相信理解线程池的工作原理,看类库中的线程池就不会感到陌生了。

 

文章2:

 

Java线程池使用说明

一简介

线程的使用在java中占有极其重要的地位,在jdk1.4极其之前的jdk版本中,关于线程池的使用是极其简陋的。在jdk1.5之后这一情况有了很大的改观。Jdk1.5之后加入了java.util.concurrent包,这个包中主要介绍java中线程以及线程池的使用。为我们在开发中处理线程的问题提供了非常大的帮助。

二:线程池

线程池的作用:

线程池作用就是限制系统中执行线程的数量。
     根据系统的环境情况,可以自动或手动设置线程数量,达到运行的最佳效果;少了浪费了系统资源,多了造成系统拥挤效率不高。用线程池控制线程数量,其他线程排队等候。一个任务执行完毕,再从队列的中取最前面的任务开始执行。若队列中没有等待进程,线程池的这一资源处于等待。当一个新任务需要运行时,如果线程池中有等待的工作线程,就可以开始运行了;否则进入等待队列。

为什么要用线程池:

1.减少了创建和销毁线程的次数,每个工作线程都可以被重复利用,可执行多个任务。

2.可以根据系统的承受能力,调整线程池中工作线线程的数目,防止因为消耗过多的内存,而把服务器累趴下(每个线程需要大约1MB内存,线程开的越多,消耗的内存也就越大,最后死机)。

Java里面线程池的顶级接口是Executor,但是严格意义上讲Executor并不是一个线程池,而只是一个执行线程的工具。真正的线程池接口是ExecutorService。

比较重要的几个类:

ExecutorService

真正的线程池接口。

ScheduledExecutorService

能和Timer/TimerTask类似,解决那些需要任务重复执行的问题。

ThreadPoolExecutor

ExecutorService的默认实现。

ScheduledThreadPoolExecutor

继承ThreadPoolExecutor的ScheduledExecutorService接口实现,周期性任务调度的类实现。

 

要配置一个线程池是比较复杂的,尤其是对于线程池的原理不是很清楚的情况下,很有可能配置的线程池不是较优的,因此在Executors类里面提供了一些静态工厂,生成一些常用的线程池。

1. newSingleThreadExecutor

创建一个单线程的线程池。这个线程池只有一个线程在工作,也就是相当于单线程串行执行所有任务。如果这个唯一的线程因为异常结束,那么会有一个新的线程来替代它。此线程池保证所有任务的执行顺序按照任务的提交顺序执行。

2.newFixedThreadPool

创建固定大小的线程池。每次提交一个任务就创建一个线程,直到线程达到线程池的最大大小。线程池的大小一旦达到最大值就会保持不变,如果某个线程因为执行异常而结束,那么线程池会补充一个新线程。

3. newCachedThreadPool

创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,

那么就会回收部分空闲(60秒不执行任务)的线程,当任务数增加时,此线程池又可以智能的添加新线程来处理任务。此线程池不会对线程池大小做限制,线程池大小完全依赖于操作系统(或者说JVM)能够创建的最大线程大小。

4.newScheduledThreadPool

创建一个大小无限的线程池。此线程池支持定时以及周期性执行任务的需求。

实例

1:newSingleThreadExecutor

复制代码
package com.thread;
/*
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * 
http://blog.youkuaiyun.com/qq_31753145/article/details/50899119

 * */

public class MyThread extends Thread { 

    @Override
    public void run() {
        // TODO Auto-generated method stub
//        super.run();
    System.out.println(Thread.currentThread().getName()+"正在执行....");
    } 
}
复制代码
复制代码
package com.thread;

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

/*
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * 
http://blog.youkuaiyun.com/qq_31753145/article/details/50899119

 * */

public class singleThreadExecutorTest{
     

    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newSingleThreadExecutor();
        
        //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
        
        
        Thread t1=new MyThread();
        
        Thread t2=new MyThread();
        
        Thread t3=new MyThread();
        
        Thread t4=new MyThread();
        
        Thread t5=new MyThread();
        
        //将线程放到池中执行;
        
        pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5);
        
        //关闭线程池
        
       pool.shutdown();
        
     

    }

}
复制代码

结果:

pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....

2newFixedThreadPool

复制代码
package com.thread;

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

/*
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * 
http://blog.youkuaiyun.com/qq_31753145/article/details/50899119

 * */

public class fixedThreadExecutorTest{
     

    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newFixedThreadPool(2);
        
        //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
        
        
        Thread t1=new MyThread();
        
        Thread t2=new MyThread();
        
        Thread t3=new MyThread();
        
        Thread t4=new MyThread();
        
        Thread t5=new MyThread();
        
        //将线程放到池中执行;
        
        pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5);
        
        //关闭线程池
        
       pool.shutdown();
        
     

    }

}
复制代码

结果:

pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-1正在执行....
pool-1-thread-2正在执行....

3、newCachedThreadPool

复制代码
package com.thread;

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

/*
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * 
http://blog.youkuaiyun.com/qq_31753145/article/details/50899119

 * */

public class cachedThreadExecutorTest{
     

    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
        //创建一个可重用固定线程数的线程池
        ExecutorService pool=Executors.newCachedThreadPool();
        
        //创建实现了Runnable接口对象,Thread对象当然也实现了Runnable接口;
        
        
        Thread t1=new MyThread();
        
        Thread t2=new MyThread();
        
        Thread t3=new MyThread();
        
        Thread t4=new MyThread();
        
        Thread t5=new MyThread();
        
        //将线程放到池中执行;
        
        pool.execute(t1);

        pool.execute(t2);

        pool.execute(t3);

        pool.execute(t4);

        pool.execute(t5);
        
        //关闭线程池
        
       pool.shutdown();
        
     

    }

}
复制代码

结果:

pool-1-thread-2正在执行....
pool-1-thread-1正在执行....
pool-1-thread-3正在执行....
pool-1-thread-4正在执行....
pool-1-thread-5正在执行....

4、newScheduledThreadPool

复制代码
package com.thread;

import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/*
 * 通过实现Runnable接口,实现多线程
 * Runnable类是有run()方法的;
 * 但是没有start方法
 * 参考:
 * 
http://blog.youkuaiyun.com/qq_31753145/article/details/50899119

 * */

public class scheduledThreadExecutorTest{
     

    public static void main(String[] args) {
        // TODO Auto-generated method stub
 
       ScheduledThreadPoolExecutor exec =new ScheduledThreadPoolExecutor(1);
       exec.scheduleAtFixedRate(new Runnable(){//每隔一段时间就触发异常

        @Override
        public void run() {
            // TODO Auto-generated method stub
            //throw new RuntimeException();
            System.out.println("===================");
            
        }}, 1000, 5000, TimeUnit.MILLISECONDS);  
       
       exec.scheduleAtFixedRate(new Runnable(){//每隔一段时间打印系统时间,证明两者是互不影响的

        @Override
        public void run() {
            // TODO Auto-generated method stub
            System.out.println(System.nanoTime());
            
        }}, 1000, 2000, TimeUnit.MILLISECONDS);
        
     
        
     

    }

}
复制代码

结果:

复制代码
===================
23119318857491
23121319071841
23123319007891
===================
23125318176937
23127318190359
===================
23129318176148
23131318344312
23133318465896
===================
23135319645812
复制代码

三:ThreadPoolExecutor详解

ThreadPoolExecutor的完整构造方法的签名是:ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueue<Runnable> workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler) .

corePoolSize - 池中所保存的线程数,包括空闲线程。

maximumPoolSize-池中允许的最大线程数。

keepAliveTime - 当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

unit - keepAliveTime 参数的时间单位。

workQueue - 执行前用于保持任务的队列。此队列仅保持由 execute方法提交的 Runnable任务。

threadFactory - 执行程序创建新线程时使用的工厂。

handler - 由于超出线程范围和队列容量而使执行被阻塞时所使用的处理程序。

ThreadPoolExecutor是Executors类的底层实现。

在JDK帮助文档中,有如此一段话:

“强烈建议程序员使用较为方便的Executors工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)Executors.newSingleThreadExecutor()(单个后台线程)

它们均为大多数使用场景预定义了设置。”

下面介绍一下几个类的源码:

ExecutorService  newFixedThreadPool (int nThreads):固定大小线程池。

可以看到,corePoolSize和maximumPoolSize的大小是一样的(实际上,后面会介绍,如果使用无界queue的话maximumPoolSize参数是没有意义的),keepAliveTime和unit的设值表名什么?-就是该实现不想keep alive!最后的BlockingQueue选择了LinkedBlockingQueue,该queue有一个特点,他是无界的。

复制代码
 public static ExecutorService newFixedThreadPool(int nThreads) {   

           return new ThreadPoolExecutor(nThreads, nThreads,   

                                        0L, TimeUnit.MILLISECONDS,   

                                        new LinkedBlockingQueue<Runnable>());   

      }
复制代码

ExecutorService  newSingleThreadExecutor():单线程

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

 

ExecutorService newCachedThreadPool():无界线程池,可以进行自动线程回收

这个实现就有意思了。首先是无界的线程池,所以我们可以发现maximumPoolSize为big big。其次BlockingQueue的选择上使用SynchronousQueue。可能对于该BlockingQueue有些陌生,简单说:该QUEUE中,每个插入操作必须等待另一个线程的对应移除操作。

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

 

先从BlockingQueue<Runnable> workQueue这个入参开始说起。在JDK中,其实已经说得很清楚了,一共有三种类型的queue。

所有BlockingQueue 都可用于传输和保持提交的任务。可以使用此队列与池大小进行交互:

如果运行的线程少于 corePoolSize,则 Executor始终首选添加新的线程,而不进行排队。(如果当前运行的线程小于corePoolSize,则任务根本不会存放,添加到queue中,而是直接抄家伙(thread)开始运行)

如果运行的线程等于或多于 corePoolSize,则 Executor始终首选将请求加入队列,而不添加新的线程。

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。

queue上的三种类型。

 

排队有三种通用策略:

直接提交。工作队列的默认选项是 SynchronousQueue,它将任务直接提交给线程而不保持它们。在此,如果不存在可用于立即运行任务的线程,则试图把任务加入队列将失败,因此会构造一个新的线程。此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。直接提交通常要求无界 maximumPoolSizes 以避免拒绝新提交的任务。当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

无界队列。使用无界队列(例如,不具有预定义容量的 LinkedBlockingQueue)将导致在所有corePoolSize 线程都忙时新任务在队列中等待。这样,创建的线程就不会超过 corePoolSize。(因此,maximumPoolSize的值也就无效了。)当每个任务完全独立于其他任务,即任务执行互不影响时,适合于使用无界队列;例如,在 Web页服务器中。这种排队可用于处理瞬态突发请求,当命令以超过队列所能处理的平均数连续到达时,此策略允许无界线程具有增长的可能性。

有界队列。当使用有限的 maximumPoolSizes时,有界队列(如 ArrayBlockingQueue)有助于防止资源耗尽,但是可能较难调整和控制。队列大小和最大池大小可能需要相互折衷:使用大型队列和小型池可以最大限度地降低 CPU 使用率、操作系统资源和上下文切换开销,但是可能导致人工降低吞吐量。如果任务频繁阻塞(例如,如果它们是 I/O边界),则系统可能为超过您许可的更多线程安排时间。使用小型队列通常要求较大的池大小,CPU使用率较高,但是可能遇到不可接受的调度开销,这样也会降低吞吐量。  

BlockingQueue的选择。

例子一:使用直接提交策略,也即SynchronousQueue。

首先SynchronousQueue是无界的,也就是说他存数任务的能力是没有限制的,但是由于该Queue本身的特性,在某次添加元素后必须等待其他线程取走后才能继续添加。在这里不是核心线程便是新创建的线程,但是我们试想一样下,下面的场景。

我们使用一下参数构造ThreadPoolExecutor:

复制代码
    new ThreadPoolExecutor(   

               2, 3, 30, TimeUnit.SECONDS,    

               new  SynchronousQueue<Runnable>(),    

               new RecorderThreadFactory("CookieRecorderPool"),    

            new ThreadPoolExecutor.CallerRunsPolicy());  
复制代码

 

 当核心线程已经有2个正在运行.

  1. 此时继续来了一个任务(A),根据前面介绍的“如果运行的线程等于或多于 corePoolSize,则Executor始终首选将请求加入队列,而不添加新的线程。”,所以A被添加到queue中。
  2. 又来了一个任务(B),且核心2个线程还没有忙完,OK,接下来首先尝试1中描述,但是由于使用的SynchronousQueue,所以一定无法加入进去。
  3. 此时便满足了上面提到的“如果无法将请求加入队列,则创建新的线程,除非创建此线程超出maximumPoolSize,在这种情况下,任务将被拒绝。”,所以必然会新建一个线程来运行这个任务。
  4. 暂时还可以,但是如果这三个任务都还没完成,连续来了两个任务,第一个添加入queue中,后一个呢?queue中无法插入,而线程数达到了maximumPoolSize,所以只好执行异常策略了。

所以在使用SynchronousQueue通常要求maximumPoolSize是无界的,这样就可以避免上述情况发生(如果希望限制就直接使用有界队列)。对于使用SynchronousQueue的作用jdk中写的很清楚:此策略可以避免在处理可能具有内部依赖性的请求集时出现锁。

什么意思?如果你的任务A1,A2有内部关联,A1需要先运行,那么先提交A1,再提交A2,当使用SynchronousQueue我们可以保证,A1必定先被执行,在A1么有被执行前,A2不可能添加入queue中。

例子二:使用无界队列策略,即LinkedBlockingQueue

这个就拿newFixedThreadPool来说,根据前文提到的规则:

如果运行的线程少于 corePoolSize,则 Executor 始终首选添加新的线程,而不进行排队。那么当任务继续增加,会发生什么呢?

如果运行的线程等于或多于 corePoolSize,则 Executor 始终首选将请求加入队列,而不添加新的线程。OK,此时任务变加入队列之中了,那什么时候才会添加新线程呢?

如果无法将请求加入队列,则创建新的线程,除非创建此线程超出 maximumPoolSize,在这种情况下,任务将被拒绝。这里就很有意思了,可能会出现无法加入队列吗?不像SynchronousQueue那样有其自身的特点,对于无界队列来说,总是可以加入的(资源耗尽,当然另当别论)。换句说,永远也不会触发产生新的线程!corePoolSize大小的线程数会一直运行,忙完当前的,就从队列中拿任务开始运行。所以要防止任务疯长,比如任务运行的实行比较长,而添加任务的速度远远超过处理任务的时间,而且还不断增加,不一会儿就爆了。

例子三:有界队列,使用ArrayBlockingQueue。

这个是最为复杂的使用,所以JDK不推荐使用也有些道理。与上面的相比,最大的特点便是可以防止资源耗尽的情况发生。

举例来说,请看如下构造方法:

复制代码
   new ThreadPoolExecutor(   

                2, 4, 30, TimeUnit.SECONDS,    

              new ArrayBlockingQueue<Runnable>(2),    
                new RecorderThreadFactory("CookieRecorderPool"),    

               new ThreadPoolExecutor.CallerRunsPolicy());  
复制代码

假设,所有的任务都永远无法执行完。

对于首先来的A,B来说直接运行,接下来,如果来了C,D,他们会被放到queue中,如果接下来再来E,F,则增加线程运行E,F。但是如果再来任务,队列无法再接受了,线程数也到达最大的限制了,所以就会使用拒绝策略来处理。

keepAliveTime

jdk中的解释是:当线程数大于核心时,此为终止前多余的空闲线程等待新任务的最长时间。

有点拗口,其实这个不难理解,在使用了“池”的应用中,大多都有类似的参数需要配置。比如数据库连接池,DBCP中的maxIdle,minIdle参数。

什么意思?接着上面的解释,后来向老板派来的工人始终是“借来的”,俗话说“有借就有还”,但这里的问题就是什么时候还了,如果借来的工人刚完成一个任务就还回去,后来发现任务还有,那岂不是又要去借?这一来一往,老板肯定头也大死了。

 

合理的策略:既然借了,那就多借一会儿。直到“某一段”时间后,发现再也用不到这些工人时,便可以还回去了。这里的某一段时间便是keepAliveTime的含义,TimeUnit为keepAliveTime值的度量。

 

RejectedExecutionHandler

另一种情况便是,即使向老板借了工人,但是任务还是继续过来,还是忙不过来,这时整个队伍只好拒绝接受了。

RejectedExecutionHandler接口提供了对于拒绝任务的处理的自定方法的机会。在ThreadPoolExecutor中已经默认包含了4中策略,因为源码非常简单,这里直接贴出来。

CallerRunsPolicy:线程调用运行该任务的 execute 本身。此策略提供简单的反馈控制机制,能够减缓新任务的提交速度。

 

复制代码
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

           if (!e.isShutdown()) {

               r.run();

           }

       }
复制代码

 

这个策略显然不想放弃执行任务。但是由于池中已经没有任何资源了,那么就直接使用调用该execute的线程本身来执行。

AbortPolicy:处理程序遭到拒绝将抛出运行时RejectedExecutionException

 

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

           throw new RejectedExecutionException();

       }

 

 这种策略直接抛出异常,丢弃任务。

DiscardPolicy:不能执行的任务将被删除

 

public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

       }

 

 这种策略和AbortPolicy几乎一样,也是丢弃任务,只不过他不抛出异常。

DiscardOldestPolicy:如果执行程序尚未关闭,则位于工作队列头部的任务将被删除,然后重试执行程序(如果再次失败,则重复此过程)

 

复制代码
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {

           if (!e.isShutdown()) {

               e.getQueue().poll();

               e.execute(r);

           }

       }
复制代码

 

该策略就稍微复杂一些,在pool没有关闭的前提下首先丢掉缓存在队列中的最早的任务,然后重新尝试运行该任务。这个策略需要适当小心。

设想:如果其他线程都还在运行,那么新来任务踢掉旧任务,缓存在queue中,再来一个任务又会踢掉queue中最老任务。

总结:

keepAliveTime和maximumPoolSize及BlockingQueue的类型均有关系。如果BlockingQueue是无界的,那么永远不会触发maximumPoolSize,自然keepAliveTime也就没有了意义。

反之,如果核心数较小,有界BlockingQueue数值又较小,同时keepAliveTime又设的很小,如果任务频繁,那么系统就会频繁的申请回收线程。

复制代码
public static ExecutorService newFixedThreadPool(int nThreads) {

       return new ThreadPoolExecutor(nThreads, nThreads,

                                     0L, TimeUnit.MILLISECONDS,

                                     new LinkedBlockingQueue<Runnable>());

   }

 
复制代码

 

参考:线程池的原理及实现

 

电商数据分析19——数据分析在电商平台价格弹性研究中的应用 价格弹性是一个衡量产品价格变化对销量影响的经济学指标,对于电商平台而言,深入理解和有效运用价格弹性,对于制定竞争策略、优化产品定价具有重要意义。随着大数据时代的到来,通过数据分析研究产品的价格弹性,成为了电商企业制定价格策略、调整市场定位、优化促销活动的重要依据。 阅读详情

相关推荐

PSCAD学习笔记(1)Project Settings(重点snapshot设置)

若不勾选,则横坐标为实际时间2-7s,故障显示在第3s发生。图片中时间坐标轴显示为0-5s,故障时刻显示为第1s,即0-1s展示系统稳态,1-5s展示系统发生故障后的暂态响应,如何通过快照文件实现?勾选该项后,使用快照文件启动仿真会将仿真起始时间强制设置为0s,反之不勾选则依据快照文件保存的时间轴继续(即坐标为实际时间)。如果已有的快照文件保存的是0s至5s的仿真内容,则仿真运行时间从5s开始,到10s结束。如果已有的快照文件保存的是0s至2s的仿真内容,则仿真运行时间从2s开始,到7s结束;

gwenh的博客 7342

Java-线程池专题 (美团)

实现多线程的三种方式,继承Thread,实现Runnable 和 实现 Executor接口 ,具体参考:Java 多线程 三种实现方式 去美团,问到了什么是线程池,如何使用,为什么要用,以下做个总结 1、什么是线程池: java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor接口的实现用于创建线程池 多线...

6039

英飞凌TC377的Flash管理、UCB配置与HSM安全机制解析

本文深入解析了英飞凌TC377微控制器的Flash存储结构、UCB配置机制与HSM安全模块。详细阐述了带Cache与不带Cache的PFlash地址访问差异、利用SOTA与AB Swap实现无感OTA更新的原理,以及如何通过UCB安全配置启动流程与写保护。重点剖析了HSM硬件安全模块在密钥管理、安全启动与安全OTA中的核心作用,为开发高可靠性嵌入式系统提供实战指南。

weixin_29170327的博客 523

线程池是什么?线程池(ThreadPoolExecutor)使用详解

先看ThreadPoolExecutor 的构造方法重点理解一下这几个参数含义:先情景带入一下,把线程池当作公司,一类正式员工;一类实习生;1.corePoolSize:核心线程数,相当于正式员工2.maxinumPoolSize:最大线程数,相当于正式员工+实习生3.long keepAliveTime:实习生线程保持存活的时间当任务比较少的时候,整体比较空闲,实习生不是立即被辞退的,表示实习生最大的存活时间4.TimeUnit unit:单位,秒,分钟,毫秒5.

xyk:的博客 6130

Python入门基础教程(非常详细)

Python是一门开源免费、通用型的脚本编程语言,它上手简单,功能强大,坚持「极简主义」。Python类库(模块)极其丰富,这使得Python几乎无所不能,不管是传统的Web开发、PC软件开发、Linux运维,还是当下火热的机器学习、大数据分析、网络爬虫,Python都能胜任。这套Python基础教程不是教科书,不会玩弄概念,而是力求口语化和通俗化,让读者尽快入门。如果有小朋友出于兴趣学习Python,请引导他阅读本教程的前半部分,这是基础,可以降低学习成本;...

Yuki1127918的博客 34万+

ESP8266 进行HDLC解码,AX.25,APRS

/************************************************************** 函数名:aprs_wait_begin() 参 数:无 返回值:bool true=得到合法脉冲,false=未得到 说 明:等待第1个833us高脉冲,判断范围 730-930,其他忽略 ***************************************************************/ bool aprs_wait_begin(){ stati

蓝蓝的博客 1628

线程池基本概念与使用

线程池的使用场景: 线程池分类: 线程池核心: 一、什么是线程池? java.util.concurrent.Executors提供了一个java.util.concurrent.Executor接口的实现用于创建线程池。减少处理器闲置时间,增加吞吐能力。 一个服务器完成一项任务需要时间:T1创建线程的时间;T2在线程中执行任务的时间;T3销毁线程的时候。使用线程池可缩短T1和T3 线程池包含以下四个基本组成部分: 1、线程池管理器(ThreadPool):创建并管理线程池,包括:创建线程池、

David_bdqn的博客 2274

Simulink进行无人机飞行力学建模与仿真

摘要:本文详细介绍了使用MATLAB/Simulink进行四旋翼无人机飞行力学建模与仿真的步骤。内容包括准备工作、动力学方程概述、模型构建(使用State-Space或Simscape Multibody)、可选控制器设计、仿真参数配置及结果分析。教程强调理解飞行力学原理和坐标系转换的重要性,建议根据需求选择模型复杂度,并推荐查阅学术文献获取更深入的建模方法。通过本教程,读者可掌握无人机动力学建模基础,为控制系统开发和性能优化奠定基础。

amy_mhd的博客 272

Java自带的线程池Executors.newFixedThreadPool

线程池的基本思想还是一种对象池的思想,开辟一块内存空间,里面存放了众多(未死亡)的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。    在Java5之前,要实现一个线程池是相当有难度的,现在Java5为我们做好了一切,我们只需要按照提供的API来使用,即可享受线程池带来的极大便利。  ...

tengtianshan的专栏 4113

手把手教你完成基于cnn的手写数字识别系统MATLAB语言

手写数字识别是经典的 CNN 分类应用之一,常用的数据集就是 MNIST 手写数字数据集,包含 0~9 这 10 个数字的手写图片,每个数字都由 6 万幅训练图像和 1 万幅测试图像构成。为了便于直观分析,这里选择 MATLAB 工具箱提供的 DigitDataset 进行训练识别,读者也可以直接利用 MNIST 数据集或者自己设计的数据集进行实验。其中,数据集文件夹 db 如图 所示。在数据集文件夹 db 里面对每个数字都建立了子文件夹,各自包含 1000 幅 28×28 大小的二维灰度图像。

在职AI算法工程师,擅长计算机视觉,YOLO目标检测、分割等,擅长web、pyqt界面可视化,好内容持续更新中,来这里跟大家一起学习,共同进步 1368

华为OD机试 - 荒岛求生 - 栈Stack(Python/JS/C/C++ 新系统 100分)

每一题都有详细的答题思路、详细的代码注释、3个测试用例、为什么这道题采用XX算法、XX算法的适用场景,发现新题目,随时更新,全天优快云在线答疑。

学Java,找哪吒 2838

Java-线程池 ThreadPool 专题详解 (美团面试题)

转载出处:   https://www.cnblogs.com/aspirant/p/6920418.html                  Java-线程池专题 (美团面试题)去美团面试,问到了什么是线程池,如何使用,为什么要用,以下做个总结1、什么是线程池:  java.util.concurrent.Executors提供了一个 java.util.concurrent.Executor...

huakaihualuo012的博客 3023

11、iOS设备逻辑数据分析全解析

本文深入解析iOS设备中的逻辑数据,涵盖图像相关数据库(Photos.sqlite和PhotosAux.sqlite)、录音文件、iPhoto照片存储结构、多媒体文件、第三方应用程序、社交网络应用(如Twitter、Skype、Facebook等)以及苹果相关应用(如Google Mobile、Bing)的数据存储结构和取证方法。此外,还介绍了文档类应用(如Pages、Numbers、Documents To Go)的文件存储结构及恢复方法。适用于iOS取证、数据挖掘和数字调查领域。

rain6的博客 226

美团面试题:Java-线程池 ThreadPool 专题详解

【公众号回复“PDF”,送你一个特别推送】去美团面试,问到了什么是线程池,如何使用,为什么要用,以下做个总结。1、什么是线程池:java.util.concurrent.Executors...

hello world! 587

Outlook Express 6 (OE6) for Win7/Win8/Win10

英文版 Outlook Express6.0, 兼容Win7/Win10/WinXP,解压就可用。

文档:亲测可用解决vnc灰屏,黑屏,鼠标大黑叉情况

主要解决vnc-viewer灰屏,黑屏,鼠标大黑叉等现象,详细见文档。

java进销存系统源码,Java面试真题精选

面试官给出的建议是,如果代码再稍微优美一点就很好了,不过也没什么大问题。然后跟我说让我等一等,出去商量商量。哎,真的太难了~~~这一面自我感觉比一面好很多,面试官态度很和蔼,就让我没有那么的紧张,让我稍等一下,我也不知道下一面是情况,或者有没有下一面。第二面问的挺多的内容,只记得个大概,所以也只能分享一个我所能记忆到的面试内容。来了个其他部门更牛B的大佬,感觉自己要凉,我太难了~~~小编整理不易,对这份。

2401_89213278的博客 1013

计算机控制技术及应用

《计算机控制技术及应用》通过总结作者的科研成果和吸收国内外的先进理论、方法和技术,以计算机控制系统的网络化、开放化、智能化和集成化发展趋势为主线索,主要介绍计算机控制系统的基本概念和计算机控制系统的典型形式,计算机控制系统的数学描述与基本分析方法,以及数字PID控制器、最少拍控制系统、达林算法、状态变量设计法等设计方法及其各种改进算法。重点突出计算机控制系统输入输出通道接口的设计方法和典型控制系统的设计案例与实现方法等。

四面阿里斩获offer定级P7,2020最新最全阿里巴巴68道高级面试题

前言 一年之计在于春 疫情过去,五一假期之后,气温逐渐回暖,几个朋友五一聚在一起聊天的时候统一发现,新的一波招聘旺季,可能马上要开始已经要到来,2020的新的开始不太好,这次的招聘依旧竞争压力山大,作为一个开发人员,你是否面上了自己理想的公司,薪资达到心中理想的高度? 面试:如果不准备充分的面试,完全是浪费时间,更是对自己的不负责。 今天给大家分享下我整理的Java架构面试专题及答案(文末见面试答案),其中大部分都是大企业面试常问的面试题,可以对照这查漏补缺,当然了,这里所列的肯定不可能覆盖全部方式,

maxiao112的博客 260

Statistics and Machine Learning Toolbox

Statistics and Machine Learning Toolbox(带书签),MATLAB统计学习工具

上一篇: 从helloworld来看java中堆,栈和方法区的区别
下一篇: Java NIO:IO与NIO的区别
javaPie
博客等级 码龄16年 1171粉丝 43原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值