c++ future 判断线程是否结束_java面试之SpringBoot加载Bean、voalite、线程、多线程、线程池...

5575f1c3264360c01200fa3d93350404.png

SpringBoot加载Bean的几种方式

  1. 通过@ComponentScan注解,在@SpringBootApplication里面就包含了@ComponentScan,把工程中的@Component、@Configuration等注解找到,加入Spring容器。
  2. @Bean注解
  3. @Import注解
  4. @EnableAutoConfiguration注解,spring-boot会利用AutoConfigurationImportSelector搜索所有jar包中META-INF文件夹中spring.factories

voalite

volatile修饰的变量具有可见性,对变量的操作在内存中进行,对所有线程共享变量,并且局部阻止了指令重排的发生,限制编译器对修饰变量的相关读写操作和指令重排。

volatile和synchronized的区别

  1. volatile仅能使用在变量上,synchronized可以使用在变量和方法与类级别上;
  2. volatile仅能实现变量的可见性,不能保证原子性,synchronized可以保证变量的可见性和原子性;
  3. volatile不会造成线程阻塞,synchronized可能会造成线程阻塞(因为volatile只是将当前变量的值及时告知所有线程,而synchronized是锁定当前变量不让其它线程访问);
  4. volatile标记的变量不会被编译器优化(因为不能指令重排),synchronized标记的变量可以被编译器优化;
  5. volatile修饰变量适合于一写多读的并发场景,而多写场景一定会产生线程安全问题(因此使用volatile而不是synchronized的唯一安全情况是类中只有一个可变的域)。
  6. 因为所有的操作都需要同步给内存变量,所以volatile一定会使线程的执行速度变慢。

volatile与static

  1. 保证一致性,不保证唯一性,多个实例有多个volatile变量。
  2. 保证唯一性,不保证一致性,多个实例共享一个静态变量。

线程

如何创建线程

  1. 继承Thread类,重写run()方法,调用Thread的start()方法。
  2. 实现Runnable接口(推荐使用),并实现该结构的run()方法,创建Thread对象,用实现Runnable接口的对象作为参数实例化该Thread对象,调用Thread的start()方法。
  3. 实现Callable接口,重写call()方法,Callable接口实际是属于Executor框架中的功能类,Callable结构与Runnable接口的功能类似,但提供了比Runnable更强大的功能,主要体现在如下三点:
  • Callable在任务结束后可以提供一个返回值,Runnable无法提供该功能。
  • Callable中的call()方法可以跑出异常,而Runnable中的run()不能跑出异常。
  • Callable可以拿到一个Future对象,Future对象表示异步计算的结果,它提供能了检查计算是否完成的方法。由于线程输入异步计算模型,因此无法从别的线程中得到函数的返回值,在这种情况下,就可以使用Future来监控目标线程来调用call()方法的情况,当调用Future的get()方法以获取结果时,当前线程会阻塞,直到目标线程的call()方法结束返回结果。

线程的状态-生命周期

  1. 创建: 通过new关键字创建后,进入到新生状态
  2. 就绪: 调用start后进入就绪状态
  3. 运行: CPU调度到本线程后,本线程开始执行。进入到运行状态
  4. 阻塞: 运行中遇到join,yield,sleep造成阻塞,进入阻塞状态。阻塞完成后,又回到就绪状态
  5. 终止: 线程正常执行完,或者遇到异常终止后,进入死亡状态
a4d63fbe314c923f0ee3921213e72381.png

多线程

如何处理多线程数据同步

  1. synchronized关键字,主要用于synchronize方法和synchronize代码块
  2. wait()方法与notify()方法
  3. Lock,JDK5新增加Lock接口以及它的一个实现类ReentrantLock(重入锁),也可以实现多线程的同步;需要自己unlock释放锁
  • a. lock():以阻塞的方式获取锁,也就是说,如果获取到了锁,就会执行,其他线程需要等待,unlock()锁后别的线程才能执行,如果别的线程持有锁,当前线程等待,直到获取锁后返回。
  • b. tryLock()。以非阻塞的方式获取锁。只是尝试性地去获取一下锁,如果获取到锁,立即返回true,否则,返回false。
  • c. tryLock(long timeout,TimeUnit unit)。在给定的时间单元内,获取到了锁返回true,否则false。
  • d. lockInterruptibly(). 如果获取了锁,立即返回;如果没有锁,当前线程处于休眠状态,直到获取锁,或者当前线程被中断(会收到InterruptedException异常)。它与lock()方法最大的区别在于如果()方法获取不到锁,就会一直处于阻塞状态,且会忽略Interrupt()方法。

线程池

线程池是一种多线程处理形式,处理过程中将任务提交到线程池,任务的执行交由线程池来管理

线程池的好处

  1. 降低资源消耗:可以重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控

线程池的工作原理

  1. 线程池判断核心线程池里的线程是否都在执行任务。如果不是,则创建一个新的工作线程来执行任务。如果核心线程池里的线程都在执行任务,则执行第二步。
  2. 线程池判断工作队列是否已经满。如果工作队列没有满,则将新提交的任务存储在这个工作队列里进行等待。如果工作队列满了,则执行第三步
  3. 线程池判断线程池的线程是否都处于工作状态。如果没有,则创建一个新的工作线程来执行任务。如果已经满了,则交给饱和策略(拒绝策略)来处理这个任务

创建线程池的几种方式(线程池种类)

  1. 单线程的线程池 (SingleThreadExecutor): 按顺序执行
  2. 固定大小的线程池(FixedThreadPool):达到最大值时,保持不变,等待老线程结束或异常,新线程代替
  3. 可缓存的线程池(CachedThreadPool):创建一个可缓存的线程池。如果线程池的大小超过了处理任务所需要的线程,那么就会回收部分空闲的线程,当任务数增加时,此线程池又添加新线程来处理任务,可以无限扩大的线程池,比较适合处理执行时间比较小的任务
  4. 定时线程池(ScheduledThreadPool):支持定时以及周期性执行任务的需求
  5. JDK8新增(WorkStealingPool):会根据所需的并行级别参数来动态创建和关闭线程,默认使用CPU数量,通过使用多个队列减少竞争,底层用的ForkJoinPool来实现的。支持把一个任务拆分成多个“小任务”,把多个“小任务”放到多个处理器核心上并行执行;当多个“小任务”执行完成之后,再将这些执行结果合并起来

说说线程池的拒绝策略

AbortPolicy策略:该策略会直接抛出异常,阻止系统正常工作。

CallerRunsPolicy 策略:只要线程池未关闭,该策略直接在调用者线程中,运行当前的被丢弃的任务。

DiscardOleddestPolicy策略: 该策略将丢弃最老的一个请求,并尝试再次提交当前任务。

DiscardPolicy策略:该策略默默的丢弃无法处理的任务,不予任何处理。

自定义策略:实现RejectedExecutionHandler接口

ba33bfde355f0560d496172b31e1cef4.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值