幼儿饰品瑜伽美体用品微软
投稿投诉
微软创意
爱情通信
用品婚姻
爱好看病
美体软件
影音星座
瑜伽周边
星座办公
饰品塑形
搞笑减肥
幼儿两性
智家潮品

面试官问在项目中用过多线程吗?你就把这个案例讲给他听

  在面试当中,有时候会问到你在项目中用过多线程么?
  对于普通的应届生或者工作时间不长的初级开发???crud仔流下了没有技术的眼泪。
  这里整理了项目中用到了多线程的一个简单的实例,希望能对你有所启发。多线程开发实例应用背景
  应用的背景非常简单,博主做的项目是一个审核类的项目,审核的数据需要推送给第三方监管系统,这只是一个很简单的对接,但是存在一个问题。
  我们需要推送的数据大概三十万条,但是第三方监管提供的接口只支持单条推送(别问为什么不支持批量,问就是没讨撕论比好过)。可以估算一下,三十万条数据,一条数据按3秒算,大概需要250(为什么恰好会是这个数)个小时。
  所以就考虑到引入多线程来进行并发操作,降低数据推送的时间,提高数据推送的实时性。
  设计要点防止重复
  我们推送给第三方的数据肯定是不能重复推送的,必须要有一个机制保证各个线程推送数据的隔离。
  这里有两个思路:将所有数据取到集合(内存)中,然后进行切割,每个线程推送不同段的数据利用数据库分页的方式,每个线程取〔start,limit〕区间的数据推送,我们需要保证start的一致性
  这里采用了第二种方式,因为考虑到可能数据量后续会继续增加,把所有数据都加载到内存中,可能会有比较大的内存占用。失败机制
  我们还得考虑到线程推送数据失败的情况。
  如果是自己的系统,我们可以把多线程调用的方法抽出来加一个事务,一个线程异常,整体回滚。
  但是是和第三方的对接,我们都没法做事务的,所以,我们采用了直接在数据库记录失败状态的方法,可以在后面用其它方式处理失败的数据。线程池选择
  在实际使用中,我们肯定是要用到线程池来管理线程,关于线程池,我们常用ThreadPoolExecutor提供的线程池服务,SpringBoot中同样也提供了线程池异步的方式,虽然SprignBoot异步可能更方便一点,但是使用ThreadPoolExecutor更加直观地控制线程池,所以我们直接使用ThreadPoolExecutor构造方法创建线程池。
  大概的技术设计示意图:
  核心代码
  上面叭叭了一堆,到了showyoucode的环节了。我将项目里的代码抽取出来,简化出了一个示例。
  核心代码如下:Author三分恶Date202135DescriptionServicepublicclassPushProcessServiceImplimplementsPushProcessService{AutowiredprivatePushUtilpushUtil;AutowiredprivatePushProcessMapperpushProcessMapper;privatefinalstaticLoggerloggerLoggerFactory。getLogger(PushProcessServiceImpl。class);每个线程每次查询的条数privatestaticfinalIntegerLIMIT5000;起的线程数privatestaticfinalIntegerTHREADNUM5;创建线程池ThreadPoolExecutorpoolnewThreadPoolExecutor(THREADNUM,THREADNUM2,0,TimeUnit。SECONDS,newLinkedBlockingQueue(100));OverridepublicvoidpushData()throwsExecutionException,InterruptedException{计数器,保证一次只能被一个请求调用,防止数据重复推送intcount0;未推送数据总数IntegertotalpushProcessMapper。countPushRecordsByState(0);logger。info(未推送数据条数:{},total);计算需要多少轮intnumtotal(LIMITTHREADNUM)1;logger。info(要经过的轮数:{},num);统计总共推送成功的数据条数inttotalSuccessCount0;for(inti0;inum;i){接收线程返回结果ListFutureIntegerfutureListnewArrayList(32);起THREADNUM个线程并行查询更新库,加锁for(intj0;jTHREADNUM;j){synchronized(PushProcessServiceImpl。class){intstartcountLIMIT;count;提交线程,用数据起始位置标识线程FutureIntegerfuturepool。submit(newPushDataTask(start,LIMIT,start));先不取值,防止阻塞,放进集合futureList。add(future);}}统计本轮推送成功数据for(Futuref:futureList){totalSuccessCounttotalSuccessCount(int)f。get();}}更新推送标志pushProcessMapper。updateAllState(1);logger。info(推送数据完成,需推送数据:{},推送成功:{},total,totalSuccessCount);}推送数据线程类classPushDataTaskimplementsCallableInteger{intstart;intlimit;intthreadNo;线程编号PushDataTask(intstart,intlimit,intthreadNo){this。startstart;this。limitlimit;this。threadNothreadNo;}OverridepublicIntegercall()throwsException{intcount0;推送的数据ListPushProcesspushProcessListpushProcessMapper。findPushRecordsByStateLimit(0,start,limit);if(CollectionUtils。isEmpty(pushProcessList)){returncount;}logger。info(线程{}开始推送数据,threadNo);for(PushProcessprocess:pushProcessList){booleanisSuccesspushUtil。sendRecord(process);if(isSuccess){推送成功更新推送标识pushProcessMapper。updateFlagById(process。getId(),1);count;}else{推送失败pushProcessMapper。updateFlagById(process。getId(),2);}}logger。info(线程{}推送成功{}条,threadNo,count);returncount;}}}复制代码
  代码很长,我们简单说一下关键的地方:线程创建:线程内部类选择了实现Callable接口,这样方便获取线程任务执行的结果,在示例里用于统计线程推送成功的数量classPushDataTaskimplementsCallableInteger{复制代码使用ThreadPoolExecutor创建线程池,创建线程池ThreadPoolExecutorpoolnewThreadPoolExecutor(THREADNUM,THREADNUM2,0,TimeUnit。SECONDS,newLinkedBlockingQueue(100));复制代码
  主要构造参数如下:
  corePoolSize:线程核心参数选择了5
  maximumPoolSize:最大线程数选择了核心线程数2倍数
  keepAliveTime:非核心闲置线程存活时间直接置为0
  unit:非核心线程保持存活的时间选择了TimeUnit。SECONDS秒
  workQueue:线程池等待队列,使用容量初始为100的LinkedBlockingQueue阻塞队列
  这里还有没写出来的线程池拒绝策略,采用了默认AbortPolicy:直接丢弃任务,抛出异常。使用synchronized来加锁,保证推送的操作只能被一个请求调用synchronized(PushProcessServiceImpl。class){复制代码使用集合来接收线程的运行结果,防止阻塞ListFutureIntegerfutureListnewArrayList(32);复制代码
  好了,主要的代码和简单的解析就到这里了。
  关于这个简单的demo,这里只是简单地做推送数据处理。考虑一下,这个实例是不是可以用在你项目的某些地方。例如监管系统的数据校验、审计系统的数据统计、电商系统的数据分析等等,只要是有大量数据处理的地方,都可以把这个例子结合到你的项目里,这样你就有了多线程开发的经验。
  完整代码仓库地址在文章底部对线面试官面试官:小伙子,不错,你这个整挺好。老三:那是自然。面试官:呦,小伙子,挺自信,那我得好好考考你。老三:放马过来,但考无妨。
  面试官:先从最简单的开始,说说什么是线程吧
  要说线程,必先说进程。
  进程是程序的次执过程,是系统运程序的基本单位,因此进程是动态的。系统运个程序即是个进程从创建,运到消亡的过程。
  线程与进程相似,但线程是个进程更的执单位。个进程在其执的过程中可以产多个线程。与进程不同的是同类的多个线程共享进程的堆和法区资源,但每个线程有的程序计数器、虚拟机栈和本地法栈,所以系统在产个线程,或是在各个线程之间作切换作时,负担要进程得多,也正因为如此,线程也被称为轻量级进程。面试官:说说Java里怎么创建线程吧
  Java里创建线程主要有三种方式:继承Thread类:Thread类本质上是实现了Runnable接口的一个实例,代表一个线程的实例。启动线程的唯一方法就是通过Thread类的start()实例方法。start()方法是一个native方法,它将启动一个新线程,并执行run()方法。实现Runnable接口:如果自己的类已经extends另一个类,就无法直接extendsThread,此时,可以实现一个Runnable接口。实现Callable接口:实现Callable接口,重写call()方法,可以返回一个Future类型的返回值。我在上面的例子里就是用到了这种方式。面试官:说说线程的生命周期和状态
  在Java中,线程共有六种状态:
  状态
  说明
  NEW
  初始状态:线程被创建,但还没有调用start()方法
  RUNNABLE
  运行状态:Java线程将操作系统中的就绪和运行两种状态笼统的称作运行
  BLOCKED
  阻塞状态:表示线程阻塞于锁
  WAITING
  等待状态:表示线程进入等待状态,进入该状态表示当前线程需要等待其他线程做出一些特定动作(通知或中断)
  TIMEWAITING
  超时等待状态:该状态不同于WAITIND,它是可以在指定的时间自行返回的
  TERMINATED
  终止状态:表示当前线程已经执行完毕
  线程在自身的生命周期中,并不是固定地处于某个状态,而是随着代码的执行在不同的状态之间进行切换,Java线程状态变化如图示:
  面试官:我看你提到了线程阻塞,那你再说说线程死锁吧
  线程死锁描述的是这样种情况:多个线程同时被阻塞,它们中的个或者全部都在等待某个资源被释放。由于线程被限期地阻塞,因此程序不可能正常终。
  如下图所示,线程A持有资源2,线程B持有资源1,他们同时都想申请对的资源,所以这两个线程就会互相等待进死锁状态。
  产生死锁必须满足四个条件:互斥条件:该资源任意个时刻只由个线程占。请求与保持条件:个进程因请求资源阻塞时,对已获得的资源保持不放。不剥夺条件:线程已获得的资源在末使完之前不能被其他线程强剥夺,只有使完毕后才释放资源。循环等待条件:若进程之间形成种头尾相接的循环等待资源关系。面试官:怎么避免死锁呢?
  我上说了产死锁的四个必要条件,为了避免死锁,我们只要破坏产死锁的四个条件中的其中个就可以了。破坏互斥条件:这个条件我们没有办法破坏,因为我们锁本来就是想让他们互斥的(临界资源需要互斥访问)。破坏请求与保持条件:次性申请所有的资源。破坏不剥夺条件:占部分资源的线程进步申请其他资源时,如果申请不到,可以主动释放它占有的资源。破坏循环等待条件:靠按序申请资源来预防。按某顺序申请资源,释放资源则反序释放。破坏循环等待条件。面试官:我看你的例子里用到了synchronized,说说synchronized的用法吧
  synchronized关键字最主要的三种使式:
  1。修饰实例法:作于当前对象实例加锁,进同步代码前要获得当前对象实例的锁synchronizedvoidmethod(){业务代码}复制代码
  2。修饰静态法:也就是给当前类加锁,会作于类的所有对象实例,进同步代码前要获得当前class的锁。因为静态成员不属于任何个实例对象,是类成员(static表明这是该类的个静态资源,不管new了多少个对象,只有份)。所以,如果个线程A调个实例对象的静态synchronized法,线程B需要调这个实例对象所属类的静态synchronized法,是允许的,不会发互斥现象,因为访问静态synchronized法占的锁是当前类的锁,访问静态synchronized法占的锁是当前实例对象锁。synchronizedvoidstaicmethod(){业务代码}复制代码
  3。修饰代码块:指定加锁对象,对给定对象类加锁。synchronized(thisobject)表示进同步代码库前要获得给定对象的锁。synchronized(类。class)表示进同步代码前要获得当前class的锁synchronized(this){业务代码}复制代码
  在我的例子里使用synchronized修饰代码块,给PushProcessServiceImpl类加锁,进同步代码前要获得当前class的锁,防止PushProcessServiceImpl类的对象在控制层调用推送数据的方法。面试官:除了使用synchronized,还有什么办法来加锁吗?详细说一下
  可以使用juc包提供的锁。Lock接口主要相关的类和接口如下。
  Lock中的主要方法:lock:用来获取锁,如果锁被其他线程获取,进入等待状态。lockInterruptibly:通过这个方法去获取锁时,如果线程正在等待获取锁,则这个线程能够响应中断,即中断线程的等待状态。tryLock:tryLock方法是有返回值的,它表示用来尝试获取锁,如果获取成功,则返回true,如果获取失败(即锁已被其他线程获取),则返回false。tryLock(long,TimeUnit):与tryLock类似,只不过是有等待时间,在等待时间内获取到锁返回true,超时返回false。unlock:释放锁。
  其它接口和类:ReetrantLock(可重入锁):实现了Lock接口,可重入锁,内部定义了公平锁与非公平锁。可以完成synchronized所能完成的所有工作。ReadWriteLock(读写锁):publicinterfaceReadWriteLock{LockreadLock();获取读锁LockwriteLock();获取写锁}复制代码
  一个用来获取读锁,一个用来获取写锁。也就是说将文件的读写操作分开,分成2个锁来分配给线程,从而使得多个线程可以同时进行读操作。ReetrantReadWriteLock(可重入读写锁):ReetrantReadWriteLock同样支持公平性选择,支持重进入,锁降级。面试官:说说synchronized和Lock的区别
  类别
  synchronized
  Lock
  存在层次
  Java的关键字,在jvm层面上
  是一个接口,api级别
  锁的释放
  1、以获取锁的线程执行完同步代码,释放锁2、线程执行发生异常,jvm会让线程释放锁
  在finally中必须释放锁,不然容易造成线程死锁
  锁的获取
  假设A线程获得锁,B线程等待。如果A线程阻塞,B线程会一直等待
  分情况而定,Lock有多个锁获取的方式,具体下面会说道,大致就是可以尝试获得锁,线程可以不用一直等待
  锁状态
  无法判断
  可以判断
  锁类型
  可重入不可中断非公平
  可重入可判断可公平(两者皆可)
  性能
  少量同步
  大量同步面试官:你提到了synchronized基于jvm层面,对这个有了解吗?
  synchronized是利用java提供的原性内置锁(monitor对象),每个对象中都内置了个ObjectMonitor对象。这种内置的并且使者看不到的锁也被称为监视器锁。
  同步语句块
  synchronized同步语句块的实现使的是monitorenter和monitorexit指令,其中monitorenter指令指向同步代码块的开始位置monitorexit指令则指明同步代码块的结束位置。
  执monitorenter指令时会尝试获取内置锁,如果对象没有被锁定或者已经获得了锁,锁的计数器1。此时其他竞争锁的线程则会进等待队列中。
  执monitorexit指令时则会把计数器1,当计数器值为0时,则锁释放,处于等待队列中的线程再继续竞争锁。
  synchronized修饰法
  synchronized修饰的法并没有monitorenter指令和monitorexit指令,取得代之的确实是ACCSYNCHRONIZED标识,该标识指明了该法是个同步法。JVM通过该ACCSYNCHRONIZED访问标志来辨别个法是否声明为同步法,从执相应的同步调。
  当然,二者细节略有不同,但本质上都是获取原子性内置锁。
  再深入一点,synchronized实际上有两个队列waitSet和entryList。当多个线程进同步代码块时,先进entryList有个线程获取到monitor锁后,就赋值给当前线程,并且计数器1如果线程调wait法,将释放锁,当前线程置为null,计数器1,同时进waitSet等待被唤醒,调notify或者notifyAll之后会进entryList竞争锁如果线程执完毕,同样释放锁,计数器1,当前线程置为null
  synchronized的优化能说一说吗?
  从JDK1。6版本之后,synchronized本身也在不断优化锁的机制,有些情况下他并不会是个很重量级的锁。优化机制包括适应锁、旋锁、锁消除、锁粗化、偏向锁、轻量级锁。
  锁的状态从低到依次为锁偏向锁轻量级锁重量级锁,升级的过程就是从低到。
  自旋锁:由于部分时候,锁被占的时间很短,共享变量的锁定时间也很短,所有没有必要挂起线程,户态和内核态的来回上下切换严重影响性能。旋的概念就是让线程执个忙循环,可以理解为就是啥也不,防从户态转内核态,旋锁可以通过设置XX:UseSpining来开启,旋的默认次数是10次,可以使XX:PreBlockSpin设置。
  自适应锁:自适应锁就是自适应的自旋锁,自旋锁的时间不是固定时间,而是由前次在同个锁上的旋时间和锁的持有者状态来决定。
  锁消除:锁消除指的是JVM检测到些同步的代码块,完全不存在数据竞争的场景,也就是不需要加锁,就会进锁消除。
  锁粗化:锁粗化指的是有很多操作都是对同个对象进加锁,就会把锁的同步范围扩展到整个操作序列之外。
  偏向锁:当线程访问同步块获取锁时,会在对象头和栈帧中的锁记录存储偏向锁的线程ID,之后这个线程再次进同步块时都不需要CAS来加锁和解锁了,偏向锁会永远偏向第个获得锁的线程,如果后续没有其他线程获得过这个锁,持有锁的线程就永远不需要进同步,反之,当有其他线程竞争偏向锁时,持有偏向锁的线程就会释放偏向锁。可以过设置XX:UseBiasedLocking开启偏向锁。
  轻量级锁:JVM的对象的对象头中包含有些锁的标志位,代码进同步块的时候,JVM将会使CAS式来尝试获取锁,如果更新成功则会把对象头中的状态位标记为轻量级锁,如果更新失败,当前线程就尝试旋来获得锁。
  锁升级的过程非常复杂,简单点说,偏向锁就是通过对象头的偏向线程ID来对,甚都不需要CAS了,轻量级锁主要就是通过CAS修改对象头锁记录和旋来实现,重量级锁则是除了拥有锁的线程其他全部阻塞。
  面试官:说一下CAS
  CAS(CompareAndSwapSet)比较并交换,CAS算法的过程是这样:它包含3个参数CAS(V,E,N)。V表示要更新的变量(内存值),E表示预期值(旧的),N表示新值。当且仅当V值等于E值时,才会将V的值设为N,如果V值和E值不同,则说明已经有其他线程做了更新,则当前线程什么都不做。最后,CAS返回当前V的真实值。
  CAS是一种乐观锁,它总是认为自己可以成功完成操作。当多个线程同时使用CAS操作一个变量时,只有一个会胜出,并成功更新,其余均会失败。失败的线程不会被挂起,仅是被告知失败,并且允许再次尝试,当然也允许失败的线程放弃操作。基于这样的原理,CAS操作即使没有锁,也可以发现其他线程对当前线程的干扰,并进行恰当的处理。
  java。util。concurrent。atomic包下的类大多是使用CAS操作来实现的(AtomicInteger,AtomicBoolean,AtomicLong)。面试官:CAS会导致什么问题?ABA问题:
  比如说一个线程one从内存位置V中取出A,这时候另一个线程two也从内存中取出A,并且two进行了一些操作变成了B,然后two又将V位置的数据变成A,这时候线程one进行CAS操作发现内存中仍然是A,然后one操作成功。尽管线程one的CAS操作成功,但可能存在潜藏的问题。从Java1。5开始JDK的atomic包里提供了一个类AtomicStampedReference来解决ABA问题。循环时间长开销大:
  对于资源竞争严重(线程冲突严重)的情况,CAS自旋的概率会比较大,从而浪费更多的CPU资源,效率低于synchronized。只能保证一个共享变量的原子操作:
  当对一个共享变量执行操作时,我们可以使用循环CAS的方式来保证原子操作,但是对多个共享变量操作时,循环CAS就无法保证操作的原子性,这个时候就可以用锁。面试官:能说一下说下ReentrantLock原理吗
  ReentrantLock是基于Lock实现的可重入锁,所有的Lock都是基于AQS实现的,AQS和Condition各自维护不同的对象,在使用Lock和Condition时,其实就是两个队列的互相移动。它所提供的共享锁、互斥锁都是基于对state的操作。面试官:能说一下AQS吗
  AbstractQueuedSynchronizer,抽象的队列式的同步器,AQS定义了一套多线程访问共享资源的同步器框架,许多同步类实现都依赖于它,如常用的
  ReentrantLockSemaphoreCountDownLatch。
  AQS核思想是,如果被请求的共享资源空闲,则将当前请求资源的线程设置为有效的作线程,并且将共享资源设置为锁定状态。如果被请求的共享资源被占,那么就需要套线程阻塞等待以及被唤醒时锁分配的机制,这个机制AQS是CLH队列锁实现的,即将暂时获取不到锁的线程加到队列中。
  看个AQS原理图:
  AQS使个int成员变量来表示同步状态,通过内置的FIFO队列来完成获取资源线程的排队作。AQS使CAS对该同步状态进原操作实现对其值的修改。privatevolatileintstate;共享变量,使volatile修饰保证线程可性复制代码
  状态信息通过protected类型的getState,setState,compareAndSetState进操作返回同步状态的当前值protectedfinalintgetState(){returnstate;}设置同步状态的值protectedfinalvoidsetState(intnewState){statenewState;}原地(CAS操作)将同步状态值设置为给定值update如果当前同步状态的值等于expect(期望值)protectedfinalbooleancompareAndSetState(intexpect,intupdate){returnunsafe。compareAndSwapInt(this,stateOffset,expect,update);}复制代码
  尝试加锁的时候通过CAS(CompareAndSwap)修改值,如果成功设置为1,并且把当前线程ID赋值,则代表加锁成功,旦获取到锁,其他的线程将会被阻塞进阻塞队列旋,获得锁的线程释放锁的时候将会唤醒阻塞队列中的线程,释放锁的时候则会把state重新置为0,同时当前线程ID置为空。
  面试官:能说一下SemaphoreCountDownLatchCyclicBarrier吗Semaphore(信号量)允许多个线程同时访问:synchronized和ReentrantLock都是一次只允许一个线程访问某个资源,Semaphore(信号量)可以指定多个线程同时访问某个资源。CountDownLatch(倒计时器):CountDownLatch是一个同步工具类,用来协调多个线程之间的同步。这个工具通常用来控制线程等待,它可以让某一个线程等待直到倒计时结束,再开始执行。CyclicBarrier(循环栅栏):CyclicBarrier和CountDownLatch非常类似,它也可以实现线程间的技术等待,但是它的功能比CountDownLatch更加复杂和强大。主要应用场景和CountDownLatch类似。CyclicBarrier的字面意思是可循环使用(Cyclic)的屏障(Barrier)。它要做的事情是,让一组线程到达一个屏障(也可以叫同步点)时被阻塞,直到最后一个线程到达屏障时,屏障才会开门,所有被屏障拦截的线程才会继续干活。CyclicBarrier默认的构造方法是CyclicBarrier(intparties),其参数表示屏障拦截的线程数量,每个线程调用await()方法告诉CyclicBarrier我已经到达了屏障,然后当前线程被阻塞。volatile原理知道吗?
  相synchronized的加锁式来解决共享变量的内存可性问题,volatile就是更轻量的选择,他没有上下切换的额外开销成本。使volatile声明的变量,可以确保值被更新的时候对其他线程刻可。
  volatile使内存屏障来保证不会发指令重排,解决了内存可性的问题。
  我们知道,线程都是从主内存中读取共享变量到作内存来操作,完成之后再把结果写会主内存,但是这样就会带来可性问题。举个例,假设现在我们是两级缓存的双核CPU架构,包含L1、L2两级缓存。
  那么,如果X变量volatile修饰的话,当线程A再次读取变量X的话,CPU就会根据缓存致性协议强制线程A重新从主内存加载最新的值到的作内存,不是直接缓存中的值。
  再来说内存屏障的问题,volatile修饰之后会加不同的内存屏障来保证可性的问题能正确执。这写的屏障基于书中提供的内容,但是实际上由于CPU架构不同,重排序的策略不同,提供的内存屏障也不样,如x86平台上,只有StoreLoad种内存屏障。StoreStore屏障,保证上的普通写不和volatile写发重排序StoreLoad屏障,保证volatile写与后可能的volatile读写不发重排序LoadLoad屏障,禁volatile读与后的普通读重排序LoadStore屏障,禁volatile读和后的普通写重排序
  面试官:说说你对Java内存模型(JMM)的理解,为什么要用JMM
  本身随着CPU和内存的发展速度差异的问题,导致CPU的速度远快于内存,所以现在的CPU加了速缓存,速缓存般可以分为L1、L2、L3三级缓存。基于上的例我们知道了这导致了缓存致性的问题,所以加了缓存致性协议,同时导致了内存可性的问题,编译器和CPU的重排序导致了原性和有序性的问题,JMM内存模型正是对多线程操作下的系列规范约束,通过JMM我们才屏蔽了不同硬件和操作系统内存的访问差异,这样保证了Java程序在不同的平台下达到致的内存访问效果,同时也是保证在效并发的时候程序能够正确执。
  面试官:看你用到了线程池,能说说为什么吗提高线程的利用率,降低资源的消耗。提高响应速度,线程的创建时间为T1,执行时间T2,销毁时间T3,用线程池可以免去T1和T3的时间。便于统一管理线程对象可控制最大并发数面试官:能说一下线程池的核心参数吗?
  来看一ThreadPoolExecutor的构造方法:publicThreadPoolExecutor(intcorePoolSize,intmaximumPoolSize,longkeepAliveTime,TimeUnitunit,BlockingQueueRunnableworkQueue,ThreadFactorythreadFactory,RejectedExecutionHandlerhandler)复制代码核线程数corePoolSize:此值是用来初始化线程池中核心线程数,当线程池中线程池数corePoolSize时,系统默认是添加一个任务才创建一个线程池。可以通过调用prestartAllCoreThreads方法一次性的启动corePoolSize个数的线程。当线程数corePoolSize时,新任务会追加到workQueue中。允许的最大线程数maximumPoolSize:maximumPoolSize表示允许的最大线程数(非核心线程数核心线程数),当BlockingQueue也满了,但线程池中总线程数maximumPoolSize时候就会再次创建新的线程。活跃时间keepAliveTime:非核心线程(maximumPoolSizecorePoolSize),非核心线程闲置下来不干活最多存活时间。保持存活时间unit:线程池中非核心线程保持存活的时间等待队列workQueue:线程池等待队列,维护着等待执行的Runnable对象。当运行当线程数corePoolSize时,新的任务会被添加到workQueue中,如果workQueue也满了则尝试用非核心线程执行任务线程工厂threadFactory:创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。拒绝策略RejectedExecutionHandler:corePoolSize、workQueue、maximumPoolSize都不可用的时候执行的饱和策略。面试官:完整说一下线程池的工作流程线程池刚创建时,里面没有一个线程。任务队列是作为参数传进来的。不过,就算队列里面有任务,线程池也不会马上执行它们。当调用execute()方法添加一个任务时,线程池会做如下判断:a)如果正在运行的线程数量小于corePoolSize,那么马上创建线程运行这个任务;b)如果正在运行的线程数量大于或等于corePoolSize,那么将这个任务放入队列;c)如果这时候队列满了,而且正在运行的线程数量小于maximumPoolSize,那么还是要创建非核心线程立刻运行这个任务;d)如果队列满了,而且正在运行的线程数量大于或等于maximumPoolSize,那么线程池会根据拒绝策略来对应处理。当一个线程完成任务时,它会从队列中取下一个任务来执行。当一个线程无事可做,超过一定的时间(keepAliveTime)时,线程池会判断,如果当前运行的线程数大于corePoolSize,那么这个线程就被停掉。所以线程池的所有任务完成后,它最终会收缩到corePoolSize的大小。
  面试官:拒绝策略有哪些
  主要有4种拒绝策略:AbortPolicy:直接丢弃任务,抛出异常,这是默认策略CallerRunsPolicy:只调者所在的线程来处理任务DiscardOldestPolicy:丢弃等待队列中最旧的任务,并执当前任务DiscardPolicy:直接丢弃任务,也不抛出异常面试官:说一下你的核心线程数是怎么选的
  线程在Java中属于稀缺资源,线程池不是越大越好也不是越小越好。任务分为计算密集型、IO密集型、混合型。计算密集型一般推荐线程池不要过大,一般是CPU数1,1是因为可能存在页缺失(就是可能存在有些数据在硬盘中需要多来一个线程将数据读入内存)。如果线程池数太大,可能会频繁的进行线程上下文切换跟任务调度。获得当前CPU核心数代码如下:Runtime。getRuntime()。availableProcessors();复制代码IO密集型:线程数适当大一点,机器的Cpu核心数2。混合型:如果密集型站大头则拆分的必要性不大,如果IO型占据不少有必要,Mark下。面试官:说一下有哪些常见阻塞队列ArrayBlockingQueue:由数组结构组成的有界阻塞队列。LinkedBlockingQueue:由链表结构组成的有界阻塞队列。PriorityBlockingQueue:支持优先级排序的无界阻塞队列。DelayQueue:使用优先级队列实现的无界阻塞队列。SynchronousQueue:不存储元素的阻塞队列。LinkedTransferQueue:由链表结构组成的无界阻塞队列。LinkedBlockingDeque:由链表结构组成的双向阻塞队列面试官:说一下有哪几种常见的线程池吧
  在上面我们直接用到了ThreadPoolExecutor的构造方法创建线程池,还有另一种方式,通过Executors创建线程。
  需要注意的是,阿里巴巴Java开发手册强制禁止使用Executors创建线程
  比较典型常见的四种线程池包括:newFixedThreadPool、newSingleThreadExecutor、newCachedThreadPool、
  newScheduledThreadPool。
  FixedThreadPool定长的线程池,有核心线程,核心线程的即为最大的线程数量,没有非核心线程。使用的无界的等待队列是LinkedBlockingQueue。使用时候有堵满等待队列的风险。
  SingleThreadPool
  只有一条线程来执行任务,适用于有顺序的任务的应用场景,也是用的无界等待队列
  CachedThreadPool
  可缓存的线程池,该线程池中没有核心线程,非核心线程的数量为Integer。maxvalue,就是无限大,当有需要时创建线程来执行任务,没有需要时回收线程,适用于耗时少,任务量大的情况。任务队列用的是SynchronousQueue如果生产多快消费慢,则会导致创建很多线程需注意。
  ScheduledThreadPoolExecutor
  周期性执行任务的线程池,按照某种特定的计划执行线程中的任务,有核心线程,但也有非核心线程,非核心线程的大小也为无限大。适用于执行周期性的任务。
  看构造函数:调用的还是ThreadPoolExecutor构造函数,区别不同点在于任务队列是用的DelayedWorkQueue。
  面试官:这些题都能回答出来,很好,小伙子,很有精神!老三:谢谢。那面试官老师,你看这一轮面试面试官:虽然你答的很好,但你的项目数据量只有十万级,不符合我们的要求。所以,面试不能让你过。
  老三上去就是一个左刺拳,再接一个右正蹬面试官:啊年轻人不讲武德,来偷袭
  好了,通过本文,相信你对多线程的应用和原理都有了一定的了解。

北京春日赏花时间表春季出游,和赏花徒步简直不要太搭!今天给大家分享北京赏花时间表快收藏1。桃花3月中下旬4月底赏花地点黄花水长城平谷桃花海北海公园……伊布为退役伤感?难过的应该是你们像我这样的不会再有了北京时间3月23日消息,近日40岁的伊布获得本期瑞典国家队征召,他出席了新闻发布会。其中,他谈到退役的可能性,以及自己的感受。伊布:我对于自己在未来要挂靴这件事感到有点恐……(社会)河北邢台农业产业化引领农民变身产业工人近年来,河北省邢台市南和区在推进乡村产业振兴过程中,积极培育壮大农业龙头企业、农民合作社、家庭农场等新型现代农业经营主体,吸纳当地农民变身乡村产业工人,有效促进农民实现稳定增收……11月全球动力电池市场再创新高宁德时代市占率首次突破40得益于特斯拉销量的大涨,11月宁德时代市占率首次突破40,遥遥领先独占鳌头,而比亚迪市占率却下降0。7个百分点,位居第二,LG新能源位居第三。三星SDI首次超过SKOn,……争议!林书豪被问是否支持好友王力宏?瞬间脸黑,被直接拉走近日林书豪已经正式复出,与北京首钢备战CBA常规赛第二阶段的比赛,他和部分北京首钢的队友与青年球员们相见欢,进行运球、上篮互动。不过在接受采访的时候,一些别有用心的记者却没有将……李金羽和李霄鹏没有足球上的沟通了我会延续此前的战术打法明天中超第2阶段的比赛就将开始,武汉队也将迎来李金羽麾下的首场比赛,今天他也出席了对阵沧州雄狮的赛前发布会。李金羽表示:球队的状态和精神层面,教练组都非常满意,对队员和团……从被碾压到反超中国围棋对韩国围棋就是一部围棋版的论持久战中国围棋和韩国围棋的崛起很有戏剧性,都曾是围棋水平落后地区,都在学习日本围棋理论,都诞生了本民族的围棋超级英雄,他们都将本国围棋提升到了一个历史的高度。说到中国围棋,就不……爱讲能讲敢讲的马云为什么沉默?头条创作挑战赛时代抛弃一个人,连招呼都不会打一声。近日消息传来,马云悄然离开,不再拥有蚂蚁集团控制权。网上也没有马云关于此事的只言片语。要知道,曾经的马云是一个能讲,爱讲……跟着北京冬奥看大片,9部与冰雪相关的户外影片推荐还有最后的500小时,一场与每个人有关的冰雪盛会即将开启,可你了解它吗?你知道北京冬奥会有多少竞赛分项吗?什么是北欧两项?雪车、钢架雪车、雪橇这些竞赛项目又有什么区别?相……蛋仔派对付费测试平安夜上线,碰撞预备,进入盲盒世界近两年,国内moba、吃鸡类游戏大火,就有无数厂商研发同题材的游戏,虽说这类的玩法机制的确吸引人,但类似的游戏过多,难免玩得人审美疲劳。不过在2021年即将结束之际,网易特别推……大超最近玩了荒野大镖客2很想看电影版众所周知大超亨利卡维尔是一名重度游戏玩家,在最近接受外媒采访时,卡维尔表示他最近才开始玩《荒野大镖客:救赎2》这款著名的西部游戏,但他玩的非常开心,希望有一天这款游戏能拍成一部……中国队晋级短道速滑女子3000米接力决赛曲春雨2月9日晚,北京冬奥会短道速滑比赛在首都体育馆继续进行。在女子3000米接力半决赛中,中国队以4分04秒383的成绩获得小组第二名,晋级决赛。接力一向是中国短……
辽宁男篮与上海比赛结束后,球迷建议杨鸣不进国家队,姚明下课北京时间3月15日,令人期待的CBA联赛第35轮的比赛继续进行,在这个阶段,很多球队在既定目标其实已经发生了改变,特别是对于那些志在季后赛里走得更远的球队来说,这轮比赛打完,还……伊能静儿子晒葡萄牙旅行照,穿裙子秀美背,手臂上纹兔女郎超个性3月16日是伊能静儿子的21岁生日,他在当天分享了自己的美照,戴着黑色假发,身穿羽毛裙露出香肩,搭配高跟鞋,在街头摆拍,他说这是自己的生日裙,看来是新买的,特意选择在生日这天穿……男科医生建议怎么防止前列腺炎一直复发?慢性前列腺炎是一种比较常见的疾病,要想预防,首先要做好基础的检查,其次要有良好的生活习惯。想要预防慢性前列腺炎,最好是让自己的免疫力达到最佳。记住以下几点:一、规律……白酒板块午盘走弱贵州茅台股价下滑1。15北京商报讯(记者翟枫瑞)4月6日,白酒板块午盘下滑,以2736。08元收盘,跌幅为1。45。受板块影响,除舍得酒业外,其他白酒个股午盘股价均出现不同程度下滑。贵州茅台午盘以17……与红米针尖对麦芒?真我发布GT系列新机,1999元起售新京报贝壳财经讯(记者许诺)4月3日,OPPO旗下互联网品牌realme(真我)发布了新机真我GTNeo5SE。该机型搭载了高通近期发布的第二代骁龙7移动5G平台,首发起售价格……钮文新如何破解信用倒挂急吁央行关注资本市场流动性《中国经济周刊》首席评论员钮文新货币市场已经不多见的月末效应、季末效应、年末效应再次出现,尽管它目前还没有2010年、2014年那么严重,但趋势不容忽视。3月29日,银行……两个人自驾318川藏线到底需要多少钱,如果你打算去这些应该知两人自驾一趟318川藏线需要花费多少钱?如果你正打算去西藏,那么我给您详细的算一下所需费用,心里有数。记得点赞收藏,认真看完可以帮你节省好几千,从成都出发到拉萨全长2000公里……3月手机拍照性能榜更新,第一不是华为也不是苹果,居然是荣耀我们在选择手机的时候通常会关注一些手机的参数与配置,分析各种利弊。但是随着我们的互联网发展,短视频越发流行,拍照性能怎么样,也成为我们关注的首要问题。今天我们找到了DXOMAR……非洲国家贸易便利化水平对中国电子信息产业出口是否有促进作用?中非贸易与贸易便利化水平的相关研究李慧征从一带一路的背景下出发,利用贸易便利化水平的测算研究了中非贸易合作的现状、挑战及前景,并从基础设施,智力援助和金融援助及减免债务方……赴一场春天的甜蜜约会!新区迎草莓采摘季莺飞草长,天气渐暖,红艳清甜的草莓悄悄露头,这一波春天的甜蜜攻势着实诱人。眼下,正值草莓采摘期,西海岸新区的各大草莓采摘园内硕果飘香,吸引着市民游客成群结伴前来采摘游玩,红彤彤……鹏锐技术刘鹏国产化软件打破数据孤岛数字化助力能源企业降本增效《QSH摘星者》第一期节目由千乘资本联合投资家名企探营栏目,走进千乘资本投资企业鹏锐技术。从中国制造到中国智造,工业软件产业在其中承担了重要的作用。鹏锐技术致力于为发电、……拉比奥特最后时刻还好有迈尼昂我在意甲取得了成长直播吧3月28日讯法国在本轮欧预赛10击败爱尔兰,中场大将拉比奥特在赛后接受采访谈及相关话题。拉比奥特表示:爱尔兰在本场比赛防守非常稳固,他们很有耐心。我们在比赛最后时刻……
友情链接:易事利快生活快传网聚热点七猫云快好知快百科中准网快好找文好找中准网快软网