手机看天气

扫码下载app,天气随时看

扫码下载app,天气随时看
收藏网页

您使用的浏览器版本过低!

可能无法正常浏览,您可以选择

木公金母网 > 直接下载更新IE浏览器 >

扫码码下载APP,天气随时看

安卓下载 App Store

发布

本篇文章给大家谈谈线程池拒绝策略,以及线程池拒绝策略什么时候执行对应的知识点,希望对各位有所帮助,不要忘了收藏本站!

内容导航:
  • 线程与线程池
  • 线程池工作原理
  • 线程池的四种创建方式及区别
  • 线程池的一些面试题
  • 线程池七大核心参数
  • 合理使用线程池以及线程变量

Q1:线程与线程池

1、线程的状态。5个。

2、实现线程的方法,及其区别。2种:Runnable、Thread(+2种:Callable、FutureTask)。

3、start()和run()的区别。

4、Thread.sleep()和Thread.yield()区别

yield,音标 /jild/。线程的礼让,该线程退回到就绪状态(然后所有的就绪的线程凭借优先级抢资源)。

sleep,线程的阻塞(当阻塞时间结束,线程转入就绪状态)。

5、wait和sleep的区别

1)wait是object的方法,sleep是thread的静态方法;

2)wait需要在synchronized范围内使用,否则抛错 Exception in thread "main" java.lang.IllegalMonitorStateException ,而sleep则不需要;

3)wait是对象监听器的线程的等待,当该对象wait时,当前线程进入等待,其notify方法是随机唤起一个(等待该对象监听器的)线程;sleep是当前线程的沉睡,该线程的对象锁还是持有的;

4)wait出让系统资源,进入线程池中等待;sleep不会出让锁。二者都会让出CPU。

5、用户线程(user Thread)和守护线程(daemon Thread)的区别。

1)守护线程的区别在于thread.setDaemon(true),设置了就是守护线程,且必须在start()之前设置。

2)守护线程依赖于用户线程,没有用户线程,守护线程不存在。即当用户线程运行完毕,此时不管守护线程是否运行或运行完毕,立即停止。

6、线程调用的两种方式:

1)直接使用start()方法(在主方法中显式迭代调用或者构造方法中,便于外部隐式调用);

2)使用Executor来调用(CachedThreadPool()或者FixedThreadPool())。

两种方法的区别是:Executor执行线程都是隐式的。而且在构造方法中调用start()方法对于多线程是不安全的,而Executor则不会。

7、停止一个运行中线程的方法:

1)interrupt方法;

2)使用退出标志;

3)stop,但不建议(J8废除,原因是可能导致数据不一致)。

关于stop方法,参考 https://blog.csdn.net/a158123/article/details/78776145 。

6、Executor调用线程的两种方式的区别:

newCachedThreadPool()会为每一个任务都分配线程;

newFixedThreadPool(long)会限定可使用的线程数量,在前面的任务执行完之后,会将空线程分配给其他的任务。

7、在并发时,一个任务不能依赖于另一个任务,因为任务的关闭顺序无法保证。解决:1.依赖于非任务对象(volatile变量)来解决。2.锁。

8、锁的方式:2种,synchronize和Lock。区别在于Lock更加细粒度,比如锁的尝试获取,锁的锁定时间。

9、线程池的状态:5个。

1.running

2.shutdown

3.stop

4.tidying(当workQueue为0时,进入该状态)

5.terminated

10、shutdown和stop的区别。

二者都有线程池停止之意,且都不接收新线程了。但shutdown会处理掉已接收和正在执行的线程,而stop会中断所有的已接收和正在执行的线程。

11、threadPoolExecutor的参数含义。

corePoolSize:核心线程数。即最小存活线程数。

maximunPoolSize:最大线程数。

keepAliveTime:当线程数大于核心线程是,线程的存活时间。

unit:keepAliveTime的单位。

workQueue:工作队列。在线程执行前,线程会放在此处。

threadFactory:线程创建工厂(有一个默认工厂)。

handler:拒绝策略。当多余的线程请求时,执行的策略。默认是拒绝策略。

ps:关于workQueue:当需要使用一个线程时,会先看核心线程有无空闲线程,若有,则直接使用,没有,则创建并放在队列中等待被使用;当线程用完时,也会放在队列中,等待一会,实在没人用且已达到核心数时,会消亡该线程。

newSingleThreadExecutor() 和 newFixedThreadPool() 都是用的LinkedBlockingQueue队列,而 newCachedThreadPool() 用的是SynchronousQueue队列。

在 newSingleThreadExecutor() 中,如果前一个线程出异常了,那么我就执行下一个线程,不会出现停止,而其他的线程池会导致停止。

在 newFixedThreadPool() 中keepAliveTime是0,在 newCachedThreadPool() 中keepAliveTime是60s。

12、线程的循环调用(如每隔5秒调用线程):

这是一个初始化后延迟1秒,每隔5秒执行任务(秒单位共享)。

这是一个初始化后延迟1毫秒,每隔5秒执行任务。默认单位为毫秒。

参考: Java多线程线程池(4)--线程池的五种状态 。

Q2:线程池工作原理

管理线程,当线程执行完当前任务,不会死掉而是 会从队列里面取

1.降低系统资源消耗。通过复用已存在的线程,降低线程创建和销毁造成的消耗;

2.提高响应速度。当有任务到达时,无需等待新线程的创建便能立即执行;

3.提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗大量系统资源,还会降低系统的稳定性,使用线程池可以进行对线程进行统一的分配、调优和监控。

本文主要是围绕 ThreadPoolExecutor(线程池框架的核心类)的构造方法参数 展开:

1.corePoolSize

线程池中的核心线程数。当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行。

2.maximumPoolSize

额外最大线程数。上面说到任务数足够多,且使用的是有界队列,如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,首先从队列里面取,如果队列里面的消息执行完毕,等下一定时间,额外线程自动销毁。

3.keepAliveTime

线程空闲时的存活时间。默认情况下,可以理解成额外最大线程数没活干了,额外线程线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,keepAliveTime参数也会起作用,直到线程池中的线程数为0。

4.unit

keepAliveTime参数的时间单位。

5.workQueue

任务缓存队列,用来存放等待执行的任务。如果当前线程数为corePoolSize,继续提交的任务就会被保存到任务缓存队列中,等待被执行。

一般来说,这里的BlockingQueue有以下三种选择:

* SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。因此,如果线程池中始终没有空闲线程(任务提交的平均速度快于被处理的速度),可能出现无限制的线程增长。

* LinkedBlockingQueue:基于链表结构的阻塞队列,如果不设置初始化容量,其容量为Integer.MAX_VALUE,即为无界队列。因此,如果线程池中线程数达到了corePoolSize,且始终没有空闲线程(任务提交的平均速度快于被处理的速度),任务缓存队列可能出现无限制的增长。

* ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务。

6.threadFactory

线程工厂,创建新线程时使用的线程工厂。

7.handler

任务拒绝策略,当阻塞队列满了,且线程池中的线程数达到maximumPoolSize,如果继续提交任务,就会采取任务拒绝策略处理该任务,线程池提供了4种任务拒绝策略:

*AbortPolicy :丢弃任务并抛出RejectedExecutionException异常,默认策略;

*CallerRunsPolicy :由调用execute方法的线程执行该任务;

*DiscardPolicy :丢弃任务,但是不抛出异常;

*DiscardOldestPolicy :丢弃阻塞队列最前面的任务,然后重新尝试执行任务(重复此过程)。

* 当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

总结下上诉参数:假设corePoolSize为10 ,maximumPoolSize为10,线程空闲时的存活时间为60s,队列采用的是有界队列ArrayBlockingQueue 设置阈值200,使 用拒绝策略 , 当前2000个任务提交过来 流程如下图

参数案例描述:

         当前2000笔 任务进来,10个核心线程去处理,剩下的1990任务队列里面放200个。剩下的1790个任务。队列塞满会去创建10个额外线程和核心线程一起去 去执行剩下的1780个任务。 当还有剩下任务处理不了就会触发任务拒绝策略 。  

       当前220笔 任务进来,10个核心线程去处理,剩下的210任务队列里面放200个。剩下的10个任务。队列塞满会去创建10个额外线程 去执行队列放不下的任务。 当额外线程和核心线程处理完队列里面的队列。没有任务可执行时,额外线程会等待我们设置的keepAliveTime,还是没有任务的情况下,就会被回收了 。

以上是绝对理想的状况下。

由参数可知 核心线程 和额外线程值是相同的,额外线程被回收时间是0,采用的是无界队列。默认采用的拒绝策略为 AbortPolicy。分析得 核心线程和额外线程处理不过来得情况,会一直往队列里面放任务。

可能存在的问题:队列过大 导致内存溢出 OOM

当任务量足够大,超过队列。交由额外线程处理。就会创建过多线程。

可能存在问题:特殊场景下,线程过多可能会导致系统奔溃。cpu负载过高。

1.具体解决方案 根据业务系统而定:

         华瑞批量查证举例:定时任务CZJZRW001每隔2min 轮询一次 会从业务表verifycationTask 中 查询出待处理和处理中的状态的任务 根据表中的查证类型 分流到具体的 反欺诈异步查证 ,还款查证,充值查证,贷款查证 。 具体查证根据处理结果更新verifycationTask表查证状态。处理成功 或者失败的定时任务无法再次轮询。这样就不需要考虑以上场景。使用线程池的情况下核心线程,额外线程处理不过来且队列已满使用DiscardPolicy拒绝不抛异常策略 ,即可满足该业务场景。类结构如下图

2.思路

可以实现 RejectedExecutionHandler接口 自定义拒绝策略  将被拒绝的任务信息缓存到磁盘,等待线程池负载较低 从磁盘读取重新提交到任务里面去执行

Q3:线程池的四种创建方式及区别

核心线程数为0,非核心线程数为MAX_VALUE,

队列不存储值,总认为队列是满的,所以每次执行任务时都会创建非核心线程,非核心线程空闲了超过60秒(默认),就会自动回收。

2.newfixedThreadPool 创建定长的线程池

在达到长度之前,每提交一个任务都会创建一个线程,如果达到线程池最大数量,则提交到队列中,在空闲的时候也不会自动回收线程

核心线程数为参数传入,非核心线程数和核心线程数一样,

队列为无界队列,资源有限的时候容易引起OOM.

与newSingledThreadPool不同的是核心线程数不为1.

3.newSingledThreadPool 创建单一线程执行。

只有一个线程按顺序执行任务,如果这个线程出现异常结束,会有另一个线程取代并按顺序执行。

corepoolsize 核心线程数为1 ,非核心线程数为1 ,

队列为无界队列,

单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

4.newScheduedThreadPool 创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。如果延迟3秒执行或每隔3秒执行一次

核心线程数为 参数设定,非核心线程数为MAX_VALUE

定义了一个DelayedWorkQueue,它是一个有序队列,会通过每个任务按照距离下次执行时间间隔的大小来排序;

线程池执行逻辑说明:

判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任务

若核心线程池已满,判断队列是否满,队列是否满和workQueue参数有关,若未满则加入队列中

若队列已满,判断线程池是否已满,线程池是否已满和maximumPoolSize参数有关,若未满创建线程执行任务

若线程池已满,则采用拒绝策略处理无法执执行的任务,拒绝策略和handler参数有关

拒绝策略

拒绝策略 => 默认采用的是AbortPolicy拒绝策略,直接在程序中抛出RejectedExecutionException异常【因为是运行时异常,不强制catch】,这种处理方式不够优雅。处理拒绝策略有以下几种比较推荐:

在程序中捕获RejectedExecutionException异常,在捕获异常中对任务进行处理。针对默认拒绝策略

使用CallerRunsPolicy拒绝策略,该策略会将任务交给调用execute的线程执行【一般为主线程】,此时主线程将在一段时间内不能提交任何任务,从而使工作线程处理正在执行的任务。此时提交的线程将被保存在TCP队列中,TCP队列满将会影响客户端,这是一种平缓的性能降低

自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可

如果任务不是特别重要,使用DiscardPolicy和DiscardOldestPolicy拒绝策略将任务丢弃也是可以

public class ThreadTest {

//ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

//

//scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

//public void run() {

//System.out.println("delay 1 seconds, and excute every 3 seconds");

//

//}

//

//}, 1, 3, TimeUnit.SECONDS);

}

Q4:线程池的一些面试题

1,为什么要用线程池,优势

(1)降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

(2) 提高响应速度,当任务到达时,任务可以不需要的等到线程创建就能立即执行。

(3)  提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

1.1常用方式

那java中是怎样实现的线程池呢?是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个接口或类,它们都是JUC包下的。 java.util.concurrent.Executors类是Executor的辅助类,类似于java中操作数组的辅助类java.util.Arrays,以及操作集合的java.util.Collections类

1.2:Executors类中的主要三个方法

线程安全的队列:staticQueue queue = new ConcurrentLinkedQueue<String>();

(1) 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中的等待,它创建的线程池corePoolSize和maximnumPoolSize是相等的,它使用的是LinkedBlockingQueue;

源码如下:

  public static ExecutorService newFixedThreadPool(int nThreads) {

        return new ThreadPoolExecutor(nThreads, nThreads,

                                      0L, TimeUnit.MILLISECONDS,

                                      new LinkedBlockingQueue<Runnable>());

    }

(2)Executors#newSingleThreadExecutor

    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行,它将corePoolSize和maximnumPoolSize都设置为1,它也使用的是LinkedBlockingQueue;

源码:

public static ExecutorService newSingleThreadExecutor() {

        return new FinalizableDelegatedExecutorService

            (new ThreadPoolExecutor(1, 1,

                                    0L, TimeUnit.MILLISECONDS,

                                    new LinkedBlockingQueue<Runnable>()));

    }

(3)Executors#newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。,它将corePoolSize设置为0,将maximnumPoolSize设置为Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是说来了任务就创建线程运行,当前线程空闲超过60秒,就销毁线程;

源码:

public static ExecutorService newCachedThreadPool() {

        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                      60L, TimeUnit.SECONDS,

                                      new SynchronousQueue<Runnable>());

    }

2,线程池的重要参数:

源码:

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler)

参数:

corePoolSize

    线程池中的常驻核心线程数,在创建了线程池后,当有请求任务来之后,就会安排池中的线程去执行请求任务,近似理解为今日当值线程,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

maximumPoolSize

    线程池能够容纳同时执行的最大线程数,此值必须大于等于1。

keepAliveTime

    多余的空闲线程的存活时间,当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。

unit

    keepAliveTime的单位。

workQueue

    任务队列,被提交但尚未被执行的任务。

threadFactory

    表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可。

handler

    拒绝策略,表示当队列满了,再也塞不下新任务了,同时,工作线程大于等于线程池的最大线程数,无法继续为新任务服务,这时候我们就需要拒绝策略机制合理的处理这个问题,默认会抛异常, 那拒绝策略有哪些呢,我们继续往下看。

JDK内置的接口:RejectedExcutionHandle

AbortPolicy(默认)

    直接抛出java.util.concurrent.RejectedExecutionException异常阻止系统正常运行,这种方式显然是不友好的。

CallerRunsPolicy

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

DiscardOldestPolicy

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

DiscardPolicy

    直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种解决方案。

    具体选择哪一种的拒绝策略,也是看自己的系统需求了;

3,底层工作原理

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

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

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

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

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

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

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

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

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

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

创建线程池时,配置多少线程数是合理的:

(1)CPU密集型:CPU核数+1个线程的线程池(CPU密集任务只有在真正的多核CPU上才可能得到加速)

(2)IO密集型:O密集型时,大部分线程都阻塞,故需要多配置线程数,CPU核数/1-阻塞系数 阻塞系数在0.8至0.9之间。例如4核,取个乐观值0.9,可达到40个线程左右

------------------------------------------------------------------------

--------------------------------

阿里巴巴开发手册上:出自于生产时间来说:

1,线程资源必须通过线程池提供,不能够在应用中自行创建线程;

2,线程池不允许使用Executors去创建,而是使用ThreadPoolExecutor的方式,

这样可以明确线程池的规则,规避资源耗尽的风险;

---------------------------

SpringBoot 自定义线程池:

1,application.yml配置:

task:

pool:

corePoolSize:5#设置核心线程数

maxPoolSize:20#设置最大线程数

keepAliveSeconds:300#设置线程活跃时间(秒)

queueCapacity:50#设置队列容量

2,线程池配置属性类:

importorg.springframework.boot.context.properties.ConfigurationProperties;

/**

* 线程池配置属性类

*/

@ConfigurationProperties(prefix ="task.pool")

publicclassTaskThreadPoolConfig{

privateintcorePoolSize;

privateintmaxPoolSize;

privateintkeepAliveSeconds;

privateintqueueCapacity;

    ...getter and setter methods...

}

3,启动类上加上异步支持:

@EnableAsync

@EnableConfigurationProperties({TaskThreadPoolConfig.class} )// 开启配置属性支持

4,自定义线程池:

/**

* 创建线程池配置类

*/

@Configuration

public class TaskExecutePool {

    @Autowired

    private TaskThreadPoolConfig config;

    /**

   * 1.这种形式的线程池配置是需要在使用的方法上面@Async("taskExecutor"),

   * 2.如果在使用的方法上面不加该注解那么spring就会使用默认的线程池

   * 3.所以如果加@Async注解但是不指定使用的线程池,又想自己定义线程池那么就可以重写spring默认的线程池

   * 4.所以第二个方法就是重写默认线程池

   * 注意:完全可以把线程池的参数写到配置文件中

   */

    @Bean

    public Executor taskExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        //核心线程池大小

        executor.setCorePoolSize(config.getCorePoolSize());

        //最大线程数

        executor.setMaxPoolSize(config.getMaxPoolSize());

        //队列容量

        executor.setQueueCapacity(config.getQueueCapacity());

        //活跃时间

        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());

        //线程名字前缀

        executor.setThreadNamePrefix("TaskExecutePool-");

        // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务

        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

       // 等待所有任务结束后再关闭线程池

        executor.setWaitForTasksToCompleteOnShutdown(true);

        executor.initialize();

        return executor;

    }

}

测试:

import io.swagger.annotations.Api;

import io.swagger.annotations.ApiOperation;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

/**

* @author qijx

*/

@Api(description = "测试控制类11111")

@RestController

@RequestMapping("/threadPoolController1")

public class ThreadPoolController1 {

        @Autowired

        private ThreadPoolService1 threadPoolService;

        @ApiOperation(value = "测试方法")

        @ResponseBody

        @RequestMapping(value = "/test",method = RequestMethod.GET)

        public String threadPoolTest() {

            threadPoolService.executeAsync();

            return "hello word!";

        }

}

第二种方法:重写springboot线程池:

**

* 原生(Spring)异步任务线程池装配类,实现AsyncConfigurer重写他的两个方法,这样在使用默认的

*  线程池的时候就会使用自己重写的

*/

@Slf4j

@Configuration

public class NativeAsyncTaskExecutePool implements AsyncConfigurer{

    //注入配置类

    @Autowired

    TaskThreadPoolConfig config;

    @Override

    public Executor getAsyncExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        //核心线程池大小

        executor.setCorePoolSize(config.getCorePoolSize());

        //最大线程数

        executor.setMaxPoolSize(config.getMaxPoolSize());

        //队列容量

        executor.setQueueCapacity(config.getQueueCapacity());

        //活跃时间

        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());

        //线程名字前缀

        executor.setThreadNamePrefix("NativeAsyncTaskExecutePool-");

        // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务

        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 等待所有任务结束后再关闭线程池

        executor.setWaitForTasksToCompleteOnShutdown(true);

        executor.initialize();

        return executor;

    }

    /**

   *  异步任务中异常处理

   * @return

   */

    @Override

    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

        return new AsyncUncaughtExceptionHandler() {

            @Override

            public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) {

                log.error("=========================="+arg0.getMessage()+"=======================", arg0);

                log.error("exception method:"+arg1.getName());

            }

        };

    }

}

测试:

/**

* @author qijx

*/

@Service

public class ThreadPoolService2 {

    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolService2.class);

    /**

   * @Async该注解不需要在指定任何bean

   */

    @Async

    public void executeAsync() {

        logger.info("start executeAsync");

        try {

            System.out.println("当前运行的线程名称:" + Thread.currentThread().getName());

            Thread.sleep(1000);

        } catch (Exception e) {

            e.printStackTrace();

        }

        logger.info("end executeAsync");

    }

}

------------------------------------

Q5:线程池七大核心参数

线程池七大核心参数如下所示:


一、corePoolSize 线程池核心线程大小


线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。


二、maximumPoolSize 线程池最大线程数量


当前线程数达到corePoolSize后,如果继续有任务被提交到线程池,会将任务缓存到工作队列(后面会介绍)中。如果队列也已满,则会去创建一个新线程来出来这个处理。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。


三、keepAliveTime 空闲线程存活时间


一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。


四、unit 空闲线程存活时间单位


空闲线程存活时间单位是keepAliveTime的计量单位。


五、workQueue 工作队列


新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。


六、threadFactory 线程工厂


创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。


七、handler 拒绝策略


当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的。


线程池的优势


1、线程和任务分离,提升线程重用性;


2、控制线程并发数量,降低服务器压力,统一管理所有线程;


3、提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间。

Q6:合理使用线程池以及线程变量

背景

随着计算技术的不断发展,3纳米制程芯片已进入试产阶段,摩尔定律在现有工艺下逐渐面临巨大的物理瓶颈,通过多核处理器技术来提升服务器的性能成为提升算力的主要方向。

在服务器领域,基于java构建的后端服务器占据着领先地位,因此,掌握java并发编程技术,充分利用CPU的并发处理能力是一个开发人员必修的基本功,本文结合线程池源码和实践,简要介绍了线程池和线程变量的使用。

线程池概述

线程池是一种“池化”的线程使用模式,通过创建一定数量的线程,让这些线程处于就绪状态来提高系统响应速度,在线程使用完成后归还到线程池来达到重复利用的目标,从而降低系统资源的消耗。

总体来说,线程池有如下的优势:

线程池的使用

在java中,线程池的实现类是ThreadPoolExecutor,构造函数如下:

可以通过 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,handler)来创建一个线程池。

在构造函数中,corePoolSize为线程池核心线程数。默认情况下,核心线程会一直存活,但是当将allowCoreThreadTimeout设置为true时,核心线程超时也会回收。

在构造函数中,maximumPoolSize为线程池所能容纳的最大线程数。

在构造函数中,keepAliveTime表示线程闲置超时时长。如果线程闲置时间超过该时长,非核心线程就会被回收。如果将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。

在构造函数中,timeUnit表示线程闲置超时时长的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

在构造函数中,blockingQueue表示任务队列,线程池任务队列的常用实现类有:

在构造函数中,threadFactory表示线程工厂。用于指定为线程池创建新线程的方式,threadFactory可以设置线程名称、线程组、优先级等参数。如通过Google工具包可以设置线程池里的线程名:

在构造函数中,rejectedExecutionHandler表示拒绝策略。当达到最大线程数且队列任务已满时需要执行的拒绝策略,常见的拒绝策略如下:

ThreadPoolExecutor线程池有如下几种状态:

线程池提交一个任务时任务调度的主要步骤如下:

核心代码如下:

Tomcat 的整体架构包含连接器和容器两大部分,其中连接器负责与外部通信,容器负责内部逻辑处理。在连接器中:

Tomcat为了实现请求的快速响应,使用线程池来提高请求的处理能力。下面我们以HTTP非阻塞I/O为例对Tomcat线程池进行简要的分析。

在Tomcat中,通过AbstractEndpoint类提供底层的网络I/O的处理,若用户没有配置自定义公共线程池,则AbstractEndpoint通过createExecutor方法来创建Tomcat默认线程池。

核心部分代码如下:

其中,TaskQueue、ThreadPoolExecutor分别为Tomcat自定义任务队列、线程池实现。

Tomcat自定义线程池继承于java.util.concurrent.ThreadPoolExecutor,并新增了一些成员变量来更高效地统计已经提交但尚未完成的任务数量(submittedCount),包括已经在队列中的任务和已经交给工作线程但还未开始执行的任务。

Tomcat在自定义线程池ThreadPoolExecutor中重写了execute()方法,并实现对提交执行的任务进行submittedCount加一。Tomcat在自定义ThreadPoolExecutor中,当线程池抛出RejectedExecutionException异常后,会调用force()方法再次向TaskQueue中进行添加任务的尝试。如果添加失败,则submittedCount减一后,再抛出RejectedExecutionException。

在Tomcat中重新定义了一个阻塞队列TaskQueue,它继承于LinkedBlockingQueue。在Tomcat中,核心线程数默认值为10,最大线程数默认为200,为了避免线程到达核心线程数后后续任务放入队列等待,Tomcat通过自定义任务队列TaskQueue重写offer方法实现了核心线程池数达到配置数后线程的创建。

具体地,从线程池任务调度机制实现可知,当offer方法返回false时,线程池将尝试创建新新线程,从而实现任务的快速响应。TaskQueue核心实现代码如下:

Tomcat中通过自定义任务线程TaskThread实现对每个线程创建时间的记录;使用静态内部类WrappingRunnable对Runnable进行包装,用于对StopPooledThreadException异常类型的处理。

Executors常用方法有以下几个:

Executors类看起来功能比较强大、用起来还比较方便,但存在如下弊端

使用线程时,可以直接调用 ThreadPoolExecutor 的构造函数来创建线程池,并根据业务实际场景来设置corePoolSize、blockingQueue、RejectedExecuteHandler等参数。

使用局部线程池时,若任务执行完后没有执行shutdown()方法或有其他不当引用,极易造成系统资源耗尽。

在工程实践中,通常使用下述公式来计算核心线程数:

nThreads=(w+c)/c*n*u=(w/c+1)*n*u

其中,w为等待时间,c为计算时间,n为CPU核心数(通常可通过 Runtime.getRuntime().availableProcessors()方法获取),u为CPU目标利用率(取值区间为[0, 1]);在最大化CPU利用率的情况下,当处理的任务为计算密集型任务时,即等待时间w为0,此时核心线程数等于CPU核心数。

上述计算公式是理想情况下的建议核心线程数,而不同系统/应用在运行不同的任务时可能会有一定的差异,因此最佳线程数参数还需要根据任务的实际运行情况和压测表现进行微调。

为了更好地发现、分析和解决问题,建议在使用多线程时增加对异常的处理,异常处理通常有下述方案:

为了实现优雅停机的目标,我们应当先调用shutdown方法,调用这个方法也就意味着,这个线程池不会再接收任何新的任务,但是已经提交的任务还会继续执行。之后我们还应当调用awaitTermination方法,这个方法可以设定线程池在关闭之前的最大超时时间,如果在超时时间结束之前线程池能够正常关闭则会返回true,否则,超时会返回false。通常我们需要根据业务场景预估一个合理的超时时间,然后调用该方法。

如果awaitTermination方法返回false,但又希望尽可能在线程池关闭之后再做其他资源回收工作,可以考虑再调用一下shutdownNow方法,此时队列中所有尚未被处理的任务都会被丢弃,同时会设置线程池中每个线程的中断标志位。shutdownNow并不保证一定可以让正在运行的线程停止工作,除非提交给线程的任务能够正确响应中断。

ThreadLocal线程变量概述

ThreadLocal类提供了线程本地变量(thread-local variables),这些变量不同于普通的变量,访问线程本地变量的每个线程(通过其get或set方法)都有其自己的独立初始化的变量副本,因此ThreadLocal没有多线程竞争的问题,不需要单独进行加锁。

ThreadLocal的原理与实践

对于ThreadLocal而言,常用的方法有get/set/initialValue 3个方法。

众所周知,在java中SimpleDateFormat有线程安全问题,为了安全地使用SimpleDateFormat,除了1)创建SimpleDateFormat局部变量;和2)加同步锁 两种方案外,我们还可以使用3)ThreadLocal的方案:

Thread 内部维护了一个 ThreadLocal.ThreadLocalMap 实例(threadLocals),ThreadLocal 的操作都是围绕着 threadLocals 来操作的。

从JDK源码可见,ThreadLocalMap中的Entry是弱引用类型的,这就意味着如果这个ThreadLocal只被这个Entry引用,而没有被其他对象强引用时,就会在下一次GC的时候回收掉。

EagleEye(鹰眼)作为全链路监控系统在集团内部被广泛使用,traceId、rpcId、压测标等信息存储在EagleEye的ThreadLocal变量中,并在HSF/Dubbo服务调用间进行传递。EagleEye通过Filter将数据初始化到ThreadLocal中,部分相关代码如下:

在EagleEyeFilter中,通过EagleEyeRequestTracer.startTrace方法进行初始化,在前置入参转换后,通过startTrace重载方法将鹰眼上下文参数存入ThreadLocal中,相关代码如下:

EagleEyeFilter在finally代码块中,通过EagleEyeRequestTracer.endTrace方法结束调用链,通过clear方法将ThreadLocal中的数据进行清理,相关代码实现如下:

在某权益领取原有链路中,通过app打开一级页面后才能发起权益领取请求,请求经过淘系无线网关(Mtop)后到达服务端,服务端通过mtop sdk获取当前会话信息。

在XX项目中,对权益领取链路进行了升级改造,在一级页面请求时,通过服务端同时发起权益领取请求。具体地,服务端在处理一级页面请求时,同时通过调用hsf/dubbo接口来进行权益领取,因此在发起rpc调用时需要携带用户当前会话信息,在服务提供端将会话信息进行提取并注入到mtop上下文,从而才能通过mtop sdk获取到会话id等信息。某开发同学在实现时,因ThreadLocal使用不当造成下述问题:

【问题1:权益领取失败分析】

在权益领取服务中,该应用构建了一套高效和线程安全的依赖注入框架,基于该框架的业务逻辑模块通常抽象为xxxModule形式,Module间为网状依赖关系,框架会按依赖关系自动调用init方法(其中,被依赖的module 的init方法先执行)。

在应用中,权益领取接口的主入口为CommonXXApplyModule类,CommonXXApplyModule依赖XXSessionModule。当请求来临时,会按依赖关系依次调用init方法,因此XXSessionModule的init方法会优先执行;而开发同学在CommonXXApplyModule类中的init方法中通过调用recoverMtopContext()方法来期望恢复mtop上下文,因recoverMtopContext()方法的调用时机过晚,从而导致XXSessionModule模块获取不到正确的会话id等信息而导致权益领取失败。

【问题2:脏数据分析】

权益领取服务在处理请求时,若当前线程曾经处理过权益领取请求,因ThreadLocal变量值未被清理,此时XXSessionModule通过mtop SDK获取会话信息时得到的是前一次请求的会话信息,从而造成脏数据。

【解决方案】

在依赖注入框架入口处AbstractGate#visit(或在XXSessionModule中)通过recoverMtopContext方法注入mtop上下文信息,并在入口方法的finally代码块清理当前请求的threadlocal变量值。

若使用强引用类型,则threadlocal的引用链为:Thread -> ThreadLocal.ThreadLocalMap -> Entry[] -> Entry -> key(threadLocal对象)和value;在这种场景下,只要这个线程还在运行(如线程池场景),若不调用remove方法,则该对象及关联的所有强引用对象都不会被垃圾回收器回收。

若使用static关键字进行修饰,则一个线程仅对应一个线程变量;否则,threadlocal语义变为perThread-perInstance,容易引发内存泄漏,如下述示例:

在上述main方法第22行debug,可见线程的threadLocals变量中有3个threadlocal实例。在工程实践中,使用threadlocal时通常期望一个线程只有一个threadlocal实例,因此,若不使用static修饰,期望的语义发生了变化,同时易引起内存泄漏。

如果不执行清理操作,则可能会出现:

建议使用try...finally 进行清理。

我们在使用ThreadLocal时,通常期望的语义是perThread,若不使用static进行修饰,则语义变为perThread-perInstance;在线程池场景下,若不用static进行修饰,创建的线程相关实例可能会达到 M * N个(其中M为线程数,N为对应类的实例数),易造成内存泄漏(https://errorprone.info/bugpattern/ThreadLocalUsage)。

在应用中,谨慎使用ThreadLocal.withInitial(Supplier<? extends S> supplier)这个工厂方法创建ThreadLocal对象,一旦不同线程的ThreadLocal使用了同一个Supplier对象,那么隔离也就无从谈起了,如:

总结

在java工程实践中,线程池和线程变量被广泛使用,因线程池和线程变量的不当使用经常造成安全生产事故,因此,正确使用线程池和线程变量是每一位开发人员必须修炼的基本功。本文从线程池和线程变量的使用出发,简要介绍了线程池和线程变量的原理和使用实践,各开发人员可结合最佳实践和实际应用场景,正确地使用线程和线程变量,构建出稳定、高效的java应用服务。

关于线程池拒绝策略和线程池拒绝策略什么时候执行的介绍到此就结束了,不知道你从中找到你需要的信息了吗?如果你还想了解更多这方面的信息,记得收藏关注本站。

查看更多关于线程池拒绝策略的详细内容...

今日天气详情" target="_blank" onClick="allCount('首页_点击_实况天气_实况天气')"> 9 °

本篇文章给大家谈谈线程池拒绝策略,以及线程池拒绝策略什么时候执行对应的知识点,希望对各位有所帮助,不要忘了收藏本站!

内容导航:
  • 线程与线程池
  • 线程池工作原理
  • 线程池的四种创建方式及区别
  • 线程池的一些面试题
  • 线程池七大核心参数
  • 合理使用线程池以及线程变量

Q1:线程与线程池

1、线程的状态。5个。

2、实现线程的方法,及其区别。2种:Runnable、Thread(+2种:Callable、FutureTask)。

3、start()和run()的区别。

4、Thread.sleep()和Thread.yield()区别

yield,音标 /jild/。线程的礼让,该线程退回到就绪状态(然后所有的就绪的线程凭借优先级抢资源)。

sleep,线程的阻塞(当阻塞时间结束,线程转入就绪状态)。

5、wait和sleep的区别

1)wait是object的方法,sleep是thread的静态方法;

2)wait需要在synchronized范围内使用,否则抛错 Exception in thread "main" java.lang.IllegalMonitorStateException ,而sleep则不需要;

3)wait是对象监听器的线程的等待,当该对象wait时,当前线程进入等待,其notify方法是随机唤起一个(等待该对象监听器的)线程;sleep是当前线程的沉睡,该线程的对象锁还是持有的;

4)wait出让系统资源,进入线程池中等待;sleep不会出让锁。二者都会让出CPU。

5、用户线程(user Thread)和守护线程(daemon Thread)的区别。

1)守护线程的区别在于thread.setDaemon(true),设置了就是守护线程,且必须在start()之前设置。

2)守护线程依赖于用户线程,没有用户线程,守护线程不存在。即当用户线程运行完毕,此时不管守护线程是否运行或运行完毕,立即停止。

6、线程调用的两种方式:

1)直接使用start()方法(在主方法中显式迭代调用或者构造方法中,便于外部隐式调用);

2)使用Executor来调用(CachedThreadPool()或者FixedThreadPool())。

两种方法的区别是:Executor执行线程都是隐式的。而且在构造方法中调用start()方法对于多线程是不安全的,而Executor则不会。

7、停止一个运行中线程的方法:

1)interrupt方法;

2)使用退出标志;

3)stop,但不建议(J8废除,原因是可能导致数据不一致)。

关于stop方法,参考 https://blog.csdn.net/a158123/article/details/78776145 。

6、Executor调用线程的两种方式的区别:

newCachedThreadPool()会为每一个任务都分配线程;

newFixedThreadPool(long)会限定可使用的线程数量,在前面的任务执行完之后,会将空线程分配给其他的任务。

7、在并发时,一个任务不能依赖于另一个任务,因为任务的关闭顺序无法保证。解决:1.依赖于非任务对象(volatile变量)来解决。2.锁。

8、锁的方式:2种,synchronize和Lock。区别在于Lock更加细粒度,比如锁的尝试获取,锁的锁定时间。

9、线程池的状态:5个。

1.running

2.shutdown

3.stop

4.tidying(当workQueue为0时,进入该状态)

5.terminated

10、shutdown和stop的区别。

二者都有线程池停止之意,且都不接收新线程了。但shutdown会处理掉已接收和正在执行的线程,而stop会中断所有的已接收和正在执行的线程。

11、threadPoolExecutor的参数含义。

corePoolSize:核心线程数。即最小存活线程数。

maximunPoolSize:最大线程数。

keepAliveTime:当线程数大于核心线程是,线程的存活时间。

unit:keepAliveTime的单位。

workQueue:工作队列。在线程执行前,线程会放在此处。

threadFactory:线程创建工厂(有一个默认工厂)。

handler:拒绝策略。当多余的线程请求时,执行的策略。默认是拒绝策略。

ps:关于workQueue:当需要使用一个线程时,会先看核心线程有无空闲线程,若有,则直接使用,没有,则创建并放在队列中等待被使用;当线程用完时,也会放在队列中,等待一会,实在没人用且已达到核心数时,会消亡该线程。

newSingleThreadExecutor() 和 newFixedThreadPool() 都是用的LinkedBlockingQueue队列,而 newCachedThreadPool() 用的是SynchronousQueue队列。

在 newSingleThreadExecutor() 中,如果前一个线程出异常了,那么我就执行下一个线程,不会出现停止,而其他的线程池会导致停止。

在 newFixedThreadPool() 中keepAliveTime是0,在 newCachedThreadPool() 中keepAliveTime是60s。

12、线程的循环调用(如每隔5秒调用线程):

这是一个初始化后延迟1秒,每隔5秒执行任务(秒单位共享)。

这是一个初始化后延迟1毫秒,每隔5秒执行任务。默认单位为毫秒。

参考: Java多线程线程池(4)--线程池的五种状态 。

Q2:线程池工作原理

管理线程,当线程执行完当前任务,不会死掉而是 会从队列里面取

1.降低系统资源消耗。通过复用已存在的线程,降低线程创建和销毁造成的消耗;

2.提高响应速度。当有任务到达时,无需等待新线程的创建便能立即执行;

3.提高线程的可管理性。线程是稀缺资源,如果无限制的创建,不仅会消耗大量系统资源,还会降低系统的稳定性,使用线程池可以进行对线程进行统一的分配、调优和监控。

本文主要是围绕 ThreadPoolExecutor(线程池框架的核心类)的构造方法参数 展开:

1.corePoolSize

线程池中的核心线程数。当提交一个任务时,线程池创建一个新线程执行任务,直到当前线程数等于corePoolSize;如果当前线程数为corePoolSize,继续提交的任务被保存到阻塞队列中,等待被执行。

2.maximumPoolSize

额外最大线程数。上面说到任务数足够多,且使用的是有界队列,如果当前阻塞队列满了,且继续提交任务,则创建新的线程执行任务,首先从队列里面取,如果队列里面的消息执行完毕,等下一定时间,额外线程自动销毁。

3.keepAliveTime

线程空闲时的存活时间。默认情况下,可以理解成额外最大线程数没活干了,额外线程线程空闲的时间达到keepAliveTime,则会终止,直到线程池中的线程数不超过corePoolSize。但是如果调用了allowCoreThreadTimeOut(boolean)方法,keepAliveTime参数也会起作用,直到线程池中的线程数为0。

4.unit

keepAliveTime参数的时间单位。

5.workQueue

任务缓存队列,用来存放等待执行的任务。如果当前线程数为corePoolSize,继续提交的任务就会被保存到任务缓存队列中,等待被执行。

一般来说,这里的BlockingQueue有以下三种选择:

* SynchronousQueue:一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态。因此,如果线程池中始终没有空闲线程(任务提交的平均速度快于被处理的速度),可能出现无限制的线程增长。

* LinkedBlockingQueue:基于链表结构的阻塞队列,如果不设置初始化容量,其容量为Integer.MAX_VALUE,即为无界队列。因此,如果线程池中线程数达到了corePoolSize,且始终没有空闲线程(任务提交的平均速度快于被处理的速度),任务缓存队列可能出现无限制的增长。

* ArrayBlockingQueue:基于数组结构的有界阻塞队列,按FIFO排序任务。

6.threadFactory

线程工厂,创建新线程时使用的线程工厂。

7.handler

任务拒绝策略,当阻塞队列满了,且线程池中的线程数达到maximumPoolSize,如果继续提交任务,就会采取任务拒绝策略处理该任务,线程池提供了4种任务拒绝策略:

*AbortPolicy :丢弃任务并抛出RejectedExecutionException异常,默认策略;

*CallerRunsPolicy :由调用execute方法的线程执行该任务;

*DiscardPolicy :丢弃任务,但是不抛出异常;

*DiscardOldestPolicy :丢弃阻塞队列最前面的任务,然后重新尝试执行任务(重复此过程)。

* 当然也可以根据应用场景实现 RejectedExecutionHandler 接口,自定义饱和策略,如记录日志或持久化存储不能处理的任务。

总结下上诉参数:假设corePoolSize为10 ,maximumPoolSize为10,线程空闲时的存活时间为60s,队列采用的是有界队列ArrayBlockingQueue 设置阈值200,使 用拒绝策略 , 当前2000个任务提交过来 流程如下图

参数案例描述:

         当前2000笔 任务进来,10个核心线程去处理,剩下的1990任务队列里面放200个。剩下的1790个任务。队列塞满会去创建10个额外线程和核心线程一起去 去执行剩下的1780个任务。 当还有剩下任务处理不了就会触发任务拒绝策略 。  

       当前220笔 任务进来,10个核心线程去处理,剩下的210任务队列里面放200个。剩下的10个任务。队列塞满会去创建10个额外线程 去执行队列放不下的任务。 当额外线程和核心线程处理完队列里面的队列。没有任务可执行时,额外线程会等待我们设置的keepAliveTime,还是没有任务的情况下,就会被回收了 。

以上是绝对理想的状况下。

由参数可知 核心线程 和额外线程值是相同的,额外线程被回收时间是0,采用的是无界队列。默认采用的拒绝策略为 AbortPolicy。分析得 核心线程和额外线程处理不过来得情况,会一直往队列里面放任务。

可能存在的问题:队列过大 导致内存溢出 OOM

当任务量足够大,超过队列。交由额外线程处理。就会创建过多线程。

可能存在问题:特殊场景下,线程过多可能会导致系统奔溃。cpu负载过高。

1.具体解决方案 根据业务系统而定:

         华瑞批量查证举例:定时任务CZJZRW001每隔2min 轮询一次 会从业务表verifycationTask 中 查询出待处理和处理中的状态的任务 根据表中的查证类型 分流到具体的 反欺诈异步查证 ,还款查证,充值查证,贷款查证 。 具体查证根据处理结果更新verifycationTask表查证状态。处理成功 或者失败的定时任务无法再次轮询。这样就不需要考虑以上场景。使用线程池的情况下核心线程,额外线程处理不过来且队列已满使用DiscardPolicy拒绝不抛异常策略 ,即可满足该业务场景。类结构如下图

2.思路

可以实现 RejectedExecutionHandler接口 自定义拒绝策略  将被拒绝的任务信息缓存到磁盘,等待线程池负载较低 从磁盘读取重新提交到任务里面去执行

Q3:线程池的四种创建方式及区别

核心线程数为0,非核心线程数为MAX_VALUE,

队列不存储值,总认为队列是满的,所以每次执行任务时都会创建非核心线程,非核心线程空闲了超过60秒(默认),就会自动回收。

2.newfixedThreadPool 创建定长的线程池

在达到长度之前,每提交一个任务都会创建一个线程,如果达到线程池最大数量,则提交到队列中,在空闲的时候也不会自动回收线程

核心线程数为参数传入,非核心线程数和核心线程数一样,

队列为无界队列,资源有限的时候容易引起OOM.

与newSingledThreadPool不同的是核心线程数不为1.

3.newSingledThreadPool 创建单一线程执行。

只有一个线程按顺序执行任务,如果这个线程出现异常结束,会有另一个线程取代并按顺序执行。

corepoolsize 核心线程数为1 ,非核心线程数为1 ,

队列为无界队列,

单工作线程最大的特点是可保证顺序地执行各个任务,并且在任意给定的时间不会有多个线程是活动的。

4.newScheduedThreadPool 创建一个定长的线程池,而且支持定时的以及周期性的任务执行,支持定时及周期性任务执行。如果延迟3秒执行或每隔3秒执行一次

核心线程数为 参数设定,非核心线程数为MAX_VALUE

定义了一个DelayedWorkQueue,它是一个有序队列,会通过每个任务按照距离下次执行时间间隔的大小来排序;

线程池执行逻辑说明:

判断核心线程数是否已满,核心线程数大小和corePoolSize参数有关,未满则创建线程执行任务

若核心线程池已满,判断队列是否满,队列是否满和workQueue参数有关,若未满则加入队列中

若队列已满,判断线程池是否已满,线程池是否已满和maximumPoolSize参数有关,若未满创建线程执行任务

若线程池已满,则采用拒绝策略处理无法执执行的任务,拒绝策略和handler参数有关

拒绝策略

拒绝策略 => 默认采用的是AbortPolicy拒绝策略,直接在程序中抛出RejectedExecutionException异常【因为是运行时异常,不强制catch】,这种处理方式不够优雅。处理拒绝策略有以下几种比较推荐:

在程序中捕获RejectedExecutionException异常,在捕获异常中对任务进行处理。针对默认拒绝策略

使用CallerRunsPolicy拒绝策略,该策略会将任务交给调用execute的线程执行【一般为主线程】,此时主线程将在一段时间内不能提交任何任务,从而使工作线程处理正在执行的任务。此时提交的线程将被保存在TCP队列中,TCP队列满将会影响客户端,这是一种平缓的性能降低

自定义拒绝策略,只需要实现RejectedExecutionHandler接口即可

如果任务不是特别重要,使用DiscardPolicy和DiscardOldestPolicy拒绝策略将任务丢弃也是可以

public class ThreadTest {

//ScheduledExecutorService scheduledThreadPool = Executors.newScheduledThreadPool(5);

//

//scheduledThreadPool.scheduleAtFixedRate(new Runnable() {

//public void run() {

//System.out.println("delay 1 seconds, and excute every 3 seconds");

//

//}

//

//}, 1, 3, TimeUnit.SECONDS);

}

Q4:线程池的一些面试题

1,为什么要用线程池,优势

(1)降低资源消耗,通过重复利用已创建的线程降低线程创建和销毁造成的消耗。

(2) 提高响应速度,当任务到达时,任务可以不需要的等到线程创建就能立即执行。

(3)  提高线程的可管理性,线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配,调优和监控。

1.1常用方式

那java中是怎样实现的线程池呢?是通过Executor框架实现的,该框架中用到了Executor,Executors,ExecutorService,ThreadPoolExecutor这几个接口或类,它们都是JUC包下的。 java.util.concurrent.Executors类是Executor的辅助类,类似于java中操作数组的辅助类java.util.Arrays,以及操作集合的java.util.Collections类

1.2:Executors类中的主要三个方法

线程安全的队列:staticQueue queue = new ConcurrentLinkedQueue<String>();

(1) 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中的等待,它创建的线程池corePoolSize和maximnumPoolSize是相等的,它使用的是LinkedBlockingQueue;

源码如下:

  public static ExecutorService newFixedThreadPool(int nThreads) {

        return new ThreadPoolExecutor(nThreads, nThreads,

                                      0L, TimeUnit.MILLISECONDS,

                                      new LinkedBlockingQueue<Runnable>());

    }

(2)Executors#newSingleThreadExecutor

    创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序执行,它将corePoolSize和maximnumPoolSize都设置为1,它也使用的是LinkedBlockingQueue;

源码:

public static ExecutorService newSingleThreadExecutor() {

        return new FinalizableDelegatedExecutorService

            (new ThreadPoolExecutor(1, 1,

                                    0L, TimeUnit.MILLISECONDS,

                                    new LinkedBlockingQueue<Runnable>()));

    }

(3)Executors#newCachedThreadPool

创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。,它将corePoolSize设置为0,将maximnumPoolSize设置为Integer.MAX_VALUE,它使用的是SynchronousQueue,也就是说来了任务就创建线程运行,当前线程空闲超过60秒,就销毁线程;

源码:

public static ExecutorService newCachedThreadPool() {

        return new ThreadPoolExecutor(0, Integer.MAX_VALUE,

                                      60L, TimeUnit.SECONDS,

                                      new SynchronousQueue<Runnable>());

    }

2,线程池的重要参数:

源码:

public ThreadPoolExecutor(int corePoolSize,

int maximumPoolSize,

long keepAliveTime,

TimeUnit unit,

BlockingQueue workQueue,

ThreadFactory threadFactory,

RejectedExecutionHandler handler)

参数:

corePoolSize

    线程池中的常驻核心线程数,在创建了线程池后,当有请求任务来之后,就会安排池中的线程去执行请求任务,近似理解为今日当值线程,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中。

maximumPoolSize

    线程池能够容纳同时执行的最大线程数,此值必须大于等于1。

keepAliveTime

    多余的空闲线程的存活时间,当前线程池数量超过corePoolSize时,当空闲时间达到keepAliveTime值时,多余空闲线程会被销毁直到只剩下corePoolSize个线程为止。

unit

    keepAliveTime的单位。

workQueue

    任务队列,被提交但尚未被执行的任务。

threadFactory

    表示生成线程池中工作线程的线程工厂,用于创建线程一般用默认的即可。

handler

    拒绝策略,表示当队列满了,再也塞不下新任务了,同时,工作线程大于等于线程池的最大线程数,无法继续为新任务服务,这时候我们就需要拒绝策略机制合理的处理这个问题,默认会抛异常, 那拒绝策略有哪些呢,我们继续往下看。

JDK内置的接口:RejectedExcutionHandle

AbortPolicy(默认)

    直接抛出java.util.concurrent.RejectedExecutionException异常阻止系统正常运行,这种方式显然是不友好的。

CallerRunsPolicy

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

DiscardOldestPolicy

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

DiscardPolicy

    直接丢弃任务,不予任何处理也不抛出异常。如果允许任务丢失,这是最好的一种解决方案。

    具体选择哪一种的拒绝策略,也是看自己的系统需求了;

3,底层工作原理

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

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

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

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

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

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

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

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

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

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

创建线程池时,配置多少线程数是合理的:

(1)CPU密集型:CPU核数+1个线程的线程池(CPU密集任务只有在真正的多核CPU上才可能得到加速)

(2)IO密集型:O密集型时,大部分线程都阻塞,故需要多配置线程数,CPU核数/1-阻塞系数 阻塞系数在0.8至0.9之间。例如4核,取个乐观值0.9,可达到40个线程左右

------------------------------------------------------------------------

--------------------------------

阿里巴巴开发手册上:出自于生产时间来说:

1,线程资源必须通过线程池提供,不能够在应用中自行创建线程;

2,线程池不允许使用Executors去创建,而是使用ThreadPoolExecutor的方式,

这样可以明确线程池的规则,规避资源耗尽的风险;

---------------------------

SpringBoot 自定义线程池:

1,application.yml配置:

task:

pool:

corePoolSize:5#设置核心线程数

maxPoolSize:20#设置最大线程数

keepAliveSeconds:300#设置线程活跃时间(秒)

queueCapacity:50#设置队列容量

2,线程池配置属性类:

importorg.springframework.boot.context.properties.ConfigurationProperties;

/**

* 线程池配置属性类

*/

@ConfigurationProperties(prefix ="task.pool")

publicclassTaskThreadPoolConfig{

privateintcorePoolSize;

privateintmaxPoolSize;

privateintkeepAliveSeconds;

privateintqueueCapacity;

    ...getter and setter methods...

}

3,启动类上加上异步支持:

@EnableAsync

@EnableConfigurationProperties({TaskThreadPoolConfig.class} )// 开启配置属性支持

4,自定义线程池:

/**

* 创建线程池配置类

*/

@Configuration

public class TaskExecutePool {

    @Autowired

    private TaskThreadPoolConfig config;

    /**

   * 1.这种形式的线程池配置是需要在使用的方法上面@Async("taskExecutor"),

   * 2.如果在使用的方法上面不加该注解那么spring就会使用默认的线程池

   * 3.所以如果加@Async注解但是不指定使用的线程池,又想自己定义线程池那么就可以重写spring默认的线程池

   * 4.所以第二个方法就是重写默认线程池

   * 注意:完全可以把线程池的参数写到配置文件中

   */

    @Bean

    public Executor taskExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        //核心线程池大小

        executor.setCorePoolSize(config.getCorePoolSize());

        //最大线程数

        executor.setMaxPoolSize(config.getMaxPoolSize());

        //队列容量

        executor.setQueueCapacity(config.getQueueCapacity());

        //活跃时间

        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());

        //线程名字前缀

        executor.setThreadNamePrefix("TaskExecutePool-");

        // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务

        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

       // 等待所有任务结束后再关闭线程池

        executor.setWaitForTasksToCompleteOnShutdown(true);

        executor.initialize();

        return executor;

    }

}

测试:

import io.swagger.annotations.Api;

import io.swagger.annotations.ApiOperation;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RequestMethod;

import org.springframework.web.bind.annotation.ResponseBody;

import org.springframework.web.bind.annotation.RestController;

/**

* @author qijx

*/

@Api(description = "测试控制类11111")

@RestController

@RequestMapping("/threadPoolController1")

public class ThreadPoolController1 {

        @Autowired

        private ThreadPoolService1 threadPoolService;

        @ApiOperation(value = "测试方法")

        @ResponseBody

        @RequestMapping(value = "/test",method = RequestMethod.GET)

        public String threadPoolTest() {

            threadPoolService.executeAsync();

            return "hello word!";

        }

}

第二种方法:重写springboot线程池:

**

* 原生(Spring)异步任务线程池装配类,实现AsyncConfigurer重写他的两个方法,这样在使用默认的

*  线程池的时候就会使用自己重写的

*/

@Slf4j

@Configuration

public class NativeAsyncTaskExecutePool implements AsyncConfigurer{

    //注入配置类

    @Autowired

    TaskThreadPoolConfig config;

    @Override

    public Executor getAsyncExecutor() {

        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        //核心线程池大小

        executor.setCorePoolSize(config.getCorePoolSize());

        //最大线程数

        executor.setMaxPoolSize(config.getMaxPoolSize());

        //队列容量

        executor.setQueueCapacity(config.getQueueCapacity());

        //活跃时间

        executor.setKeepAliveSeconds(config.getKeepAliveSeconds());

        //线程名字前缀

        executor.setThreadNamePrefix("NativeAsyncTaskExecutePool-");

        // setRejectedExecutionHandler:当pool已经达到max size的时候,如何处理新任务

        // CallerRunsPolicy:不在新线程中执行任务,而是由调用者所在的线程来执行

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        // 等待所有任务结束后再关闭线程池

        executor.setWaitForTasksToCompleteOnShutdown(true);

        executor.initialize();

        return executor;

    }

    /**

   *  异步任务中异常处理

   * @return

   */

    @Override

    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {

        return new AsyncUncaughtExceptionHandler() {

            @Override

            public void handleUncaughtException(Throwable arg0, Method arg1, Object... arg2) {

                log.error("=========================="+arg0.getMessage()+"=======================", arg0);

                log.error("exception method:"+arg1.getName());

            }

        };

    }

}

测试:

/**

* @author qijx

*/

@Service

public class ThreadPoolService2 {

    private static final Logger logger = LoggerFactory.getLogger(ThreadPoolService2.class);

    /**

   * @Async该注解不需要在指定任何bean

   */

    @Async

    public void executeAsync() {

        logger.info("start executeAsync");

        try {

            System.out.println("当前运行的线程名称:" + Thread.currentThread().getName());

            Thread.sleep(1000);

        } catch (Exception e) {

            e.printStackTrace();

        }

        logger.info("end executeAsync");

    }

}

------------------------------------

Q5:线程池七大核心参数

线程池七大核心参数如下所示:


一、corePoolSize 线程池核心线程大小


线程池中会维护一个最小的线程数量,即使这些线程处理空闲状态,他们也不会被销毁,除非设置了allowCoreThreadTimeOut。这里的最小线程数量即是corePoolSize。任务提交到线程池后,首先会检查当前线程数是否达到了corePoolSize,如果没有达到的话,则会创建一个新线程来处理这个任务。


二、maximumPoolSize 线程池最大线程数量


当前线程数达到corePoolSize后,如果继续有任务被提交到线程池,会将任务缓存到工作队列(后面会介绍)中。如果队列也已满,则会去创建一个新线程来出来这个处理。线程池不会无限制的去创建新线程,它会有一个最大线程数量的限制,这个数量即由maximunPoolSize指定。


三、keepAliveTime 空闲线程存活时间


一个线程如果处于空闲状态,并且当前的线程数量大于corePoolSize,那么在指定时间后,这个空闲线程会被销毁,这里的指定时间由keepAliveTime来设定。


四、unit 空闲线程存活时间单位


空闲线程存活时间单位是keepAliveTime的计量单位。


五、workQueue 工作队列


新任务被提交后,会先进入到此工作队列中,任务调度时再从队列中取出任务。


六、threadFactory 线程工厂


创建一个新线程时使用的工厂,可以用来设定线程名、是否为daemon线程等等。


七、handler 拒绝策略


当工作队列中的任务已到达最大限制,并且线程池中的线程数量也达到最大限制,这时如果有新任务提交进来,该如何处理呢。这里的拒绝策略,就是解决这个问题的。


线程池的优势


1、线程和任务分离,提升线程重用性;


2、控制线程并发数量,降低服务器压力,统一管理所有线程;


3、提升系统响应速度,假如创建线程用的时间为T1,执行任务用的时间为T2,销毁线程用的时间为T3,那么使用线程池就免去了T1和T3的时间。

Q6:合理使用线程池以及线程变量

背景

随着计算技术的不断发展,3纳米制程芯片已进入试产阶段,摩尔定律在现有工艺下逐渐面临巨大的物理瓶颈,通过多核处理器技术来提升服务器的性能成为提升算力的主要方向。

在服务器领域,基于java构建的后端服务器占据着领先地位,因此,掌握java并发编程技术,充分利用CPU的并发处理能力是一个开发人员必修的基本功,本文结合线程池源码和实践,简要介绍了线程池和线程变量的使用。

线程池概述

线程池是一种“池化”的线程使用模式,通过创建一定数量的线程,让这些线程处于就绪状态来提高系统响应速度,在线程使用完成后归还到线程池来达到重复利用的目标,从而降低系统资源的消耗。

总体来说,线程池有如下的优势:

线程池的使用

在java中,线程池的实现类是ThreadPoolExecutor,构造函数如下:

可以通过 new ThreadPoolExecutor(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue, threadFactory,handler)来创建一个线程池。

在构造函数中,corePoolSize为线程池核心线程数。默认情况下,核心线程会一直存活,但是当将allowCoreThreadTimeout设置为true时,核心线程超时也会回收。

在构造函数中,maximumPoolSize为线程池所能容纳的最大线程数。

在构造函数中,keepAliveTime表示线程闲置超时时长。如果线程闲置时间超过该时长,非核心线程就会被回收。如果将allowCoreThreadTimeout设置为true时,核心线程也会超时回收。

在构造函数中,timeUnit表示线程闲置超时时长的时间单位。常用的有:TimeUnit.MILLISECONDS(毫秒)、TimeUnit.SECONDS(秒)、TimeUnit.MINUTES(分)。

在构造函数中,blockingQueue表示任务队列,线程池任务队列的常用实现类有:

在构造函数中,threadFactory表示线程工厂。用于指定为线程池创建新线程的方式,threadFactory可以设置线程名称、线程组、优先级等参数。如通过Google工具包可以设置线程池里的线程名:

在构造函数中,rejectedExecutionHandler表示拒绝策略。当达到最大线程数且队列任务已满时需要执行的拒绝策略,常见的拒绝策略如下:

ThreadPoolExecutor线程池有如下几种状态:

线程池提交一个任务时任务调度的主要步骤如下:

核心代码如下:

Tomcat 的整体架构包含连接器和容器两大部分,其中连接器负责与外部通信,容器负责内部逻辑处理。在连接器中:

Tomcat为了实现请求的快速响应,使用线程池来提高请求的处理能力。下面我们以HTTP非阻塞I/O为例对Tomcat线程池进行简要的分析。

在Tomcat中,通过AbstractEndpoint类提供底层的网络I/O的处理,若用户没有配置自定义公共线程池,则AbstractEndpoint通过createExecutor方法来创建Tomcat默认线程池。

核心部分代码如下:

其中,TaskQueue、ThreadPoolExecutor分别为Tomcat自定义任务队列、线程池实现。

Tomcat自定义线程池继承于java.util.concurrent.ThreadPoolExecutor,并新增了一些成员变量来更高效地统计已经提交但尚未完成的任务数量(submittedCount),包括已经在队列中的任务和已经交给工作线程但还未开始执行的任务。

Tomcat在自定义线程池ThreadPoolExecutor中重写了execute()方法,并实现对提交执行的任务进行submittedCount加一。Tomcat在自定义ThreadPoolExecutor中,当线程池抛出RejectedExecutionException异常后,会调用force()方法再次向TaskQueue中进行添加任务的尝试。如果添加失败,则submittedCount减一后,再抛出RejectedExecutionException。

在Tomcat中重新定义了一个阻塞队列TaskQueue,它继承于LinkedBlockingQueue。在Tomcat中,核心线程数默认值为10,最大线程数默认为200,为了避免线程到达核心线程数后后续任务放入队列等待,Tomcat通过自定义任务队列TaskQueue重写offer方法实现了核心线程池数达到配置数后线程的创建。

具体地,从线程池任务调度机制实现可知,当offer方法返回false时,线程池将尝试创建新新线程,从而实现任务的快速响应。TaskQueue核心实现代码如下:

Tomcat中通过自定义任务线程TaskThread实现对每个线程创建时间的记录;使用静态内部类WrappingRunnable对Runnable进行包装,用于对StopPooledThreadException异常类型的处理。

Executors常用方法有以下几个:

Executors类看起来功能比较强大、用起来还比较方便,但存在如下弊端

使用线程时,可以直接调用 ThreadPoolExecutor 的构造函数来创建线程池,并根据业务实际场景来设置corePoolSize、blockingQueue、RejectedExecuteHandler等参数。

使用局部线程池时,若任务执行完后没有执行shutdown()方法或有其他不当引用,极易造成系统资源耗尽。

在工程实践中,通常使用下述公式来计算核心线程数:

nThreads=(w+c)/c*n*u=(w/c+1)*n*u

其中,w为等待时间,c为计算时间,n为CPU核心数(通常可通过 Runtime.getRuntime().availableProcessors()方法获取),u为CPU目标利用率(取值区间为[0, 1]);在最大化CPU利用率的情况下,当处理的任务为计算密集型任务时,即等待时间w为0,此时核心线程数等于CPU核心数。

上述计算公式是理想情况下的建议核心线程数,而不同系统/应用在运行不同的任务时可能会有一定的差异,因此最佳线程数参数还需要根据任务的实际运行情况和压测表现进行微调。

为了更好地发现、分析和解决问题,建议在使用多线程时增加对异常的处理,异常处理通常有下述方案:

为了实现优雅停机的目标,我们应当先调用shutdown方法,调用这个方法也就意味着,这个线程池不会再接收任何新的任务,但是已经提交的任务还会继续执行。之后我们还应当调用awaitTermination方法,这个方法可以设定线程池在关闭之前的最大超时时间,如果在超时时间结束之前线程池能够正常关闭则会返回true,否则,超时会返回false。通常我们需要根据业务场景预估一个合理的超时时间,然后调用该方法。

如果awaitTermination方法返回false,但又希望尽可能在线程池关闭之后再做其他资源回收工作,可以考虑再调用一下shutdownNow方法,此时队列中所有尚未被处理的任务都会被丢弃,同时会设置线程池中每个线程的中断标志位。shutdownNow并不保证一定可以让正在运行的线程停止工作,除非提交给线程的任务能够正确响应中断。

ThreadLocal线程变量概述

ThreadLocal类提供了线程本地变量(thread-local variables),这些变量不同于普通的变量,访问线程本地变量的每个线程(通过其get或set方法)都有其自己的独立初始化的变量副本,因此ThreadLocal没有多线程竞争的问题,不需要单独进行加锁。

ThreadLocal的原理与实践

对于ThreadLocal而言,常用的方法有get/set/initialValue 3个方法。

众所周知,在java中SimpleDateFormat有线程安全问题,为了安全地使用SimpleDateFormat,除了1)创建SimpleDateFormat局部变量;和2)加同步锁 两种方案外,我们还可以使用3)ThreadLocal的方案:

Thread 内部维护了一个 ThreadLocal.ThreadLocalMap 实例(threadLocals),ThreadLocal 的操作都是围绕着 threadLocals 来操作的。

从JDK源码可见,ThreadLocalMap中的Entry是弱引用类型的,这就意味着如果这个ThreadLocal只被这个Entry引用,而没有被其他对象强引用时,就会在下一次GC的时候回收掉。

EagleEye(鹰眼)作为全链路监控系统在集团内部被广泛使用,traceId、rpcId、压测标等信息存储在EagleEye的ThreadLocal变量中,并在HSF/Dubbo服务调用间进行传递。EagleEye通过Filter将数据初始化到ThreadLocal中,部分相关代码如下:

在EagleEyeFilter中,通过EagleEyeRequestTracer.startTrace方法进行初始化,在前置入参转换后,通过startTrace重载方法将鹰眼上下文参数存入ThreadLocal中,相关代码如下:

EagleEyeFilter在finally代码块中,通过EagleEyeRequestTracer.endTrace方法结束调用链,通过clear方法将ThreadLocal中的数据进行清理,相关代码实现如下:

在某权益领取原有链路中,通过app打开一级页面后才能发起权益领取请求,请求经过淘系无线网关(Mtop)后到达服务端,服务端通过mtop sdk获取当前会话信息。

在XX项目中,对权益领取链路进行了升级改造,在一级页面请求时,通过服务端同时发起权益领取请求。具体地,服务端在处理一级页面请求时,同时通过调用hsf/dubbo接口来进行权益领取,因此在发起rpc调用时需要携带用户当前会话信息,在服务提供端将会话信息进行提取并注入到mtop上下文,从而才能通过mtop sdk获取到会话id等信息。某开发同学在实现时,因ThreadLocal使用不当造成下述问题:

【问题1:权益领取失败分析】

在权益领取服务中,该应用构建了一套高效和线程安全的依赖注入框架,基于该框架的业务逻辑模块通常抽象为xxxModule形式,Module间为网状依赖关系,框架会按依赖关系自动调用init方法(其中,被依赖的module 的init方法先执行)。

在应用中,权益领取接口的主入口为CommonXXApplyModule类,CommonXXApplyModule依赖XXSessionModule。当请求来临时,会按依赖关系依次调用init方法,因此XXSessionModule的init方法会优先执行;而开发同学在CommonXXApplyModule类中的init方法中通过调用recoverMtopContext()方法来期望恢复mtop上下文,因recoverMtopContext()方法的调用时机过晚,从而导致XXSessionModule模块获取不到正确的会话id等信息而导致权益领取失败。

【问题2:脏数据分析】

权益领取服务在处理请求时,若当前线程曾经处理过权益领取请求,因ThreadLocal变量值未被清理,此时XXSessionModule通过mtop SDK获取会话信息时得到的是前一次请求的会话信息,从而造成脏数据。

【解决方案】

在依赖注入框架入口处AbstractGate#visit(或在XXSessionModule中)通过recoverMtopContext方法注入mtop上下文信息,并在入口方法的finally代码块清理当前请求的threadlocal变量值。

若使用强引用类型,则threadlocal的引用链为:Thread -> ThreadLocal.ThreadLocalMap -> Entry[] -> Entry -> key(threadLocal对象)和value;在这种场景下,只要这个线程还在运行(如线程池场景),若不调用remove方法,则该对象及关联的所有强引用对象都不会被垃圾回收器回收。

若使用static关键字进行修饰,则一个线程仅对应一个线程变量;否则,threadlocal语义变为perThread-perInstance,容易引发内存泄漏,如下述示例:

在上述main方法第22行debug,可见线程的threadLocals变量中有3个threadlocal实例。在工程实践中,使用threadlocal时通常期望一个线程只有一个threadlocal实例,因此,若不使用static修饰,期望的语义发生了变化,同时易引起内存泄漏。

如果不执行清理操作,则可能会出现:

建议使用try...finally 进行清理。

我们在使用ThreadLocal时,通常期望的语义是perThread,若不使用static进行修饰,则语义变为perThread-perInstance;在线程池场景下,若不用static进行修饰,创建的线程相关实例可能会达到 M * N个(其中M为线程数,N为对应类的实例数),易造成内存泄漏(https://errorprone.info/bugpattern/ThreadLocalUsage)。

在应用中,谨慎使用ThreadLocal.withInitial(Supplier<? extends S> supplier)这个工厂方法创建ThreadLocal对象,一旦不同线程的ThreadLocal使用了同一个Supplier对象,那么隔离也就无从谈起了,如:

总结

在java工程实践中,线程池和线程变量被广泛使用,因线程池和线程变量的不当使用经常造成安全生产事故,因此,正确使用线程池和线程变量是每一位开发人员必须修炼的基本功。本文从线程池和线程变量的使用出发,简要介绍了线程池和线程变量的原理和使用实践,各开发人员可结合最佳实践和实际应用场景,正确地使用线程和线程变量,构建出稳定、高效的java应用服务。

关于线程池拒绝策略和线程池拒绝策略什么时候执行的介绍到此就结束了,不知道你从中找到你需要的信息了吗?如果你还想了解更多这方面的信息,记得收藏关注本站。

查看更多关于线程池拒绝策略的详细内容...

今日天气详情" target="_blank">阴
空气质量73优
未来2小时内无雨~

“小非农”、FOMC利率决议:做空美元危险?两大信号暗示今夜有剧烈波动

    资料图 中新社发 张云 摄中新网广州8月8日电 (记者 程景伟)记者8日从知名房产租售服务平台获悉,小非农该平台最新发布的全国主要城市房源数据报告指出,小非农在飙涨的房价作用下,北京、上海、深圳部分购房者正面临着“被豪宅”的尴尬,目前京沪深三地500万元以上的房源均已经超过三。

    中日海警船在钓鱼岛海域对峙原标题:利率决议日本分析中方意图和目标 德媒称或针对日新任防卫相虽然不断向中国提出抗议,利率决议但对于中国为什么会采取这样的行动,日本官方没有给出说法。日本NHK电视台7日报道称,日本政府在强化警戒的同时,正在加紧分析中方的意图和目标。不。中国空军日前出动多型战机对南沙进行战斗巡航原标题:空美元危险两日本炒作中国军事化东海 中国专家:空美元危险两倒打一耙在刚刚过去的这个周末,“抗议”几乎成了日本官方对华发出的最主要声音。根据日媒的公开报道,在5-7日这三天里,日本通过不同途径至少向中方提出了6次抗议——它不。

    “小非农”、FOMC利率决议:做空美元危险?两大信号暗示今夜有剧烈波动

    奥巴马和女儿中新网8月8日电 据外媒报道,大信号暗示今白宫方面近日表示,大信号暗示今美国总统奥巴马与妻子米歇尔及两个女儿当地时间本月7日抵达马萨诸塞州马莎葡萄园岛,展开为期两周的暑假。奥巴马夫妇的爱女玛丽亚和莎夏如今亭亭玉立,俨然成为“时尚偶像”。据报道,玛莎葡萄园岛(M。8月7日下午,在韩国青瓦台,韩国总统首席新闻秘书金声宇发表政府立场。(韩联社)原标题:夜有剧烈波动韩国总统府声称:夜有剧烈波动部署萨德反导系统不可避免 中国应抗议朝鲜据报道,韩国总统府首席新闻秘书金声宇8月7日声称,部署“萨德”反导系统是韩国政府为保护国民生命与安全而做出的“不可避。附奖票的海报通过邮政投递扩散前几天,小非农家住大庆让胡路区创业城的张大爷遇到了一件“喜事”,小非农他从报箱取晚报时,还发现了一个快递。打开一看可不得了,里面有张刮奖卡,是为庆祝海尔电器成立30周年举行的抽奖活动。刮开涂层后,他发现自己中了100万元。这巨款,差点把张大。利率决议短短两周,107医院接诊了5例电扇伤人的事件。8月4日,李女士在单位上班,外出回来时,不小心被厂房里大风扇的电线绊了一下,李女士条件反射地去扶电扇,由于风扇的外罩空隙很大,右手瞬间被扇叶绞在里面,整个手血淋淋的,同事赶紧把她送到医院。经手足外科医生检查,李女士右手食。土耳其F-16多用途战斗机(美国《国家利益》网站)原标题:空美元危险两外媒称埃尔多安专机差点被击落:空美元危险两叛军战机没油了参考消息网8月8日报道 据俄罗斯卫星网8月8日报道称,据土耳其媒体消息,土耳其总统埃尔多安的飞机在政变期间差点被战机击落。报道称,在埃尔多安从度假胜地达拉曼起飞。

    如果你在午饭时间去北京像素,大信号暗示今就会发现到处是外卖小哥忙碌身影,大信号暗示今跟着走你就能发现102家开在走廊里的小餐馆。看了后厨污水横流,苍蝇乱飞,你可能再不想订外卖了。逼仄的格子间,洗碗池内洗拖把,菜墩放在地上切肉,肉串在垃圾桶上串成,就是在这样的地方,土豆粉、炖菜。女子10米气步枪决赛颁奖仪式上的五星红旗,夜有剧烈波动四颗小五角星是平行的。另外,夜有剧烈波动记者发现,孙杨夺得男子400米自由泳亚军的颁奖仪式国旗也是错的,如上图。里约奥组委向人民日报记者独家回应中国“错误国旗”问题:所有在里约奥运会上使用的国旗都得到了各国奥组委的许可。发现问。据英国广播公司(BBC)8月7日报道,小非农两名中国工人周五(5日)下午在尼日利亚北部地区遭枪手绑架。警方已经开展搜索行动。报道称,小非农这两名中国工人是一家矿业公司的员工,其中一名工人50岁,另一名工人45岁。5日下午他们乘汽车从纳萨拉瓦州(Nassarawa State)一个村庄前往。

    南国都市报8月6日讯(记者 王忠新)家住万宁的李先生反映,利率决议5日晚11时许,利率决议在万宁人民中路与光明北路路口,发生一起交通事故。一名年轻男子驾驶摩托车高速通过路口时摔倒在地,伤势较重。让居民想不到的是,现场还散落了数把刀具。据了解,事发后,辖区万城派出所、交警等。尼日利亚现有5万华侨华人,空美元危险两3万多人集中于最大的城市拉各斯原标题:空美元危险两外媒:尼日利亚两中国工人遭绑架据英国广播公司8月7日报道,两名中国工人5日在尼日利亚遭绑架。警方已经开展营救行动。报道称,两名中国工人其中一人50岁,另一人45岁,他们5日下午乘汽车从纳萨拉瓦州一。坦克两项赛,大信号暗示今我军第一辆出场的是107号坦克原标题:大信号暗示今一周军情:96B坦克上跑圈赛 你告诉我怎么输?本周,俄罗斯国际军事竞赛重要赛事“坦克两项”上,中国代表队的96B式主战坦克首次亮相国际赛场。第一天比赛,我军选手在射击项目中发挥失常,3发主炮炮弹未能命中靶标,但仍。中国日报网8月7日电 (信莲)美国民主党总统候选人希拉里·克林顿依靠演讲年入数百万美元,夜有剧烈波动但她在最近的一次演讲中却口误连连,夜有剧烈波动甚至把竞争对手、共和党总统候选人唐纳德·特朗普误称作“我的丈夫”,引得在场观众掩嘴偷笑。据英国《每日邮报》网站8月6日报道,希拉里5日。

    新华社乌鲁木齐8月7日电 (记者 赵春晖)一年之内经历了十多次大大小小的地震,牧民阿力亚·阿不都外力新家的墙皮上却连一个裂纹都没有。他说,现在,一般的地震,都不用从屋里往外跑了。去年7月搬进安居富民房的阿力亚·阿不都外力是新疆乌恰县乌鲁克恰提乡牧民,此前,。马术比赛。 中新社记者 熊然 摄 中新网8月7日电 据外媒报道,当地时间6日,里约奥运会马术比赛场地媒体中心的帐篷遭一枚子弹击中,事件未造成人员伤亡。据报道,子弹射中了帐篷的帆布屋顶,可看到清晰的弹孔。子弹落在地板上,但未造成人员受伤。警方称,此次事。

    “小非农”、FOMC利率决议:做空美元危险?两大信号暗示今夜有剧烈波动

    苏州一小区里群租的张小姐,在公共浴室洗澡,正洗得欢呢~突然发现浴室窗台上放着一包香烟和一个打火机!打火机上似乎还 有个小灯在一闪一闪, 张小姐拿起来仔细看了看…… 一看不得了! 打火机里还有内存卡!随后,隔壁房间的一名30多岁的男。旅客滞留机场。吕俊明摄中新网成都8月7日电 (王鹏吕俊明)7日早间,记者从成都双流国际机场获悉,该机场在凌晨突发雷阵雨天气,造成机场停航关闭1个小时,3个进港航班备降外场,12个航班被取消,16个出港航班延误,导致3000余名旅客出行受影响,部分航班推迟到今日继续执。到今年9月底,焦煤、同煤、阳煤、潞安、晋煤、晋能、山煤等七大山西省属煤企的银行贷款将全部重组为转型升级中长期专项贷款,涉及资金4000多亿元。 周平浪 澎湃资料图人民网太原8月6日电(记者 周亚军)为支持山西省属七大煤炭集团化解过剩产能、加快转型升级,山西银行。越南政府办公室主任梅进勇答记者问(网页截图)原标题:越南将彻查中国进口技术设备 称其“威胁信息安全”国际在线专稿:据《越南青年报》(TuoiTreNews)8月4日报道,越南政府宣布将对中国进口设备和技术进行彻查,以确保信息安全。此前,越南机场遭到网络袭击,有媒体称。

    中国日报网8月7日电 (信莲)据英国《镜报》8月5日报道,仅仅为了一头羊,阿富汗一名6岁女童就被父亲卖给当地55岁高龄的乡村毛拉做新娘,所幸警方及时干预,阻止了一场悲剧。近日,阿富汗警方接到报警称,赫拉特省(Herat province)一村庄的毛拉赛义德· 阿卜杜勒· 卡。中国空军苏-30战机双机起飞。 新华社发中国空军轰-6K战机战斗巡航。新华社发(高飞 摄)中国空军歼击机海上战巡。新华社发(高益平 摄)参加巡航的中国空军轰-6K战机。新华社发(赵世杰 摄)西北某陌生地域,空军导弹某师采取“进驻即打”的方式,在极端恶劣条。央广网北京8月7日消息 据中国之声《央广新闻》报道,泰国是中国游客境外游的热门目的地之一。近日,很多中国网友注意到了一条新闻,说泰国清迈的一家动物园打出了“我爱中国游客”的横幅。从今年上半年炒作中国游客形象差到现在表示爱中国游客,这种反差背后的原因是。据英国每日邮报报道,澳大利亚一名男子在澳洲北部皇后岛附近享受划艇假期时,突然遭到鳄鱼攻击,被困荒岛四天后才最终获救。(转载请注明出处,更多精彩内容请关注腾讯国际微信公众号“糖醋国际”) 这个惊心动魄的故事发生在上周,事发时该男子正悠闲地在海上划行,大。

    据《每日邮报》报道,因贝尔小镇位于英国英格兰南部城市索尔兹伯里,二战时出于安全原因这里的村民被疏散。自此以后,因贝尔小镇便空无一人,70年来犹如鬼城。(转载请注明出处,更多精内容来自腾讯国际新闻微信公众号“糖醋国际”)  1943年12月,因贝尔小镇的15。中纪委网站发布消息称,近日,河南省纪委监察厅、省工商联联合下发《河南省构建新型政商关系暂行办法》,制定了政商之间“亲”“清”交往的具体规范,着力解决为官不为、懒政怠政等问题。 在政商交往中,到底哪些行为能做,哪些行为不能做?《暂行办法》。

    “小非农”、FOMC利率决议:做空美元危险?两大信号暗示今夜有剧烈波动

    20年前,叶利钦就职三天后,车臣武装再次攻入格罗兹尼。在巴萨耶夫的率领下,1500名车臣武装分子控制了城内的化工厂和供热站,与城内的1.2万名俄军展开激烈巷战,并包围了俄军在阿尔贡和古杰尔梅斯的兵营。俄军立即对这一威胁做出反应,实施大规模不加区。荆楚网消息(记者张扬 通讯员郐国辉 邬鹏)8月6日上午,湖北省高速民警巡逻至随岳高速公路京山段时,发现一辆轿车停在应急车道内,车辆双闪灯开启,但车后方未设置警示标志牌。民警下车走到驾驶室旁边,发现驾驶员躺在座上睡着了,民警敲击窗户喊醒了驾驶员。驾驶员下车。

    中新网8月7日电 据国土部网站消息,2016年08月06日20时至07日20时,云南西部局部,贵州东部局部,湖南西部局部、中西部发生地质灾害的可能性较大(黄色预警)。“航空飞镖2016”航空比赛开幕,中国空军歼轰-7战机出场,参加火箭弹对地攻击项目。航空飞镖赛场都是以退役战机作为靶标,米格-29战斗机和L-39教练机都成为靶子,将接受火箭弹和炸弹的“洗礼”。雅克-130教练\/攻击机发射火箭弹。 中国空军歼轰-7战斗轰炸机起飞 中国空军。中新网8月7日电 据中国海洋局网站消息,中国海警2166、33115舰船编队在钓鱼岛领海内巡航。中新网南京8月6日电 (记者 崔佳明)今天早晨,镇江丹阳丹北镇埤城常麓工业园电镀整治园区9号楼发生火灾,事故已致1死23伤。6日夜11时13分,镇江官方通报,涉事的电镀园已被停产整顿。官方通报说,截至目前,危重伤员中一名政府专职消防队员(眭涛,21岁)经抢救无效光荣牺牲。里约奥运会前夕,Leblon沙滩到处充满巴西元素。图为用沙子堆成的城堡,插上了巴西的国旗。中新网记者 杜洋 摄中新网8月7日电 据外媒报道,里约奥运会开始后,当地已经开始了节日气氛,到处是游客和运动员,城市居民也看到了赚钱的机会。很多产品在奥运会期间涨价,部分海。河南省郑州市,第37届国际比基尼小姐大赛华中区总决赛选手摆“奥运五环”助威里约奥运会,为中国健儿加油。卢键\/东方IC 比基尼小姐们拿着奥运五环拍照。 比基尼小姐们摆“奥运五环”造型照。

    老人向齐鲁晚报记者展示保健品公司赠送的“按摩神器”,市面只卖十几块钱。8月5日本报刊登了《走出保健品公司,老人晕倒离世》。其实,据齐鲁晚报记者调查,如今济南许多地方都开着以“生活馆”“健康室”“绿色食品”等为名的保健品经营场所,用免费送礼为噱头专。新文化讯(记者 唐奇 实习生 吴佳莉)“他连拧矿泉水的力气都没有了。”网友微博消息,在长春市解放大路与民康路的建行自助取款厅内,住着一名少年,经网友简单了解,男孩称被骗到长春,父母离异,好心网友买水买吃的,但男孩好像饿得连拧矿泉水瓶的力量都没。

    中新网北京8月7日电(记者 金嘉龙)留美博士张博将驾驶一架轻型飞机,从北京首都国际机场出发,开始中国首次环球飞行。迄今为止,人类共完成350多次环球飞行,但还没有从中国起飞完成环球飞行的纪录。“我这次挑战中国首次环球飞行,除实现个人的梦想外,也想为中。印度尼西亚爪哇62岁的瓦卢约(Waluyo),去年5月出门去外地打工,结果不幸发生车祸,家人闻讯后前往医院照顾,但他在昏迷一周后仍然回天乏术,伤心的妻子当时还亲自帮他下葬。  但没想到的是,今年2日瓦卢约竟“活了过来”?并且突然出现在家门前,把所有人都吓坏。

    8分钟的“黑暗聊天”配对成功的男女嘉宾人民网海南视窗海口8月7日电 “我们拒绝外貌或者物质的判断,只聆听内心的渴望。”一场的特殊相亲交友会——“黑暗约会”在儋州广物光村雪茄风情小镇上演,所有参与者分别蒙上眼罩与相亲对象交流,以求“完全释放。据英国《每日邮报》8月5日报道,来自美国加州的一名女子Carly Mersola每半年就要花费1万美元(超过66000人民币)进行整形手术,为的是让自己看起来像芭比娃娃。 25岁的Carly Mersola每次手术的项目包括皮肤美黑、唇部注射和头发漂白等项目。 图为她16岁时候的。

    李成斌(音)和儿子李鑫(音)耕种着这片面积82英亩(约合495亩)的土地。这块地是他们租来的,位于跨过中国边境的俄罗斯远东地区的克拉斯诺亚尔斯克村原标题:美媒:俄罗斯人对中国农民在俄远东耕种看法相左参考消息网8月7日报道 美媒称,来自中国的62岁农民李成斌(音)驾驶着。在日本广岛和平纪念公园附近,人们举行集会游行。新华社发原标题:安倍出席广岛核爆71周年纪念仪式遭抗议新华社电 日本广岛市6日在和平纪念公园举行原子弹爆炸71周年纪念活动。当天有约1000名日本各地民众在附近集会游行,反对首相安倍晋三出席纪念仪式,抗议安。与俄军士兵一起巡逻的机器人原标题:俄新型助战机器人将登场新华社莫斯科8月4日电(记者栾海)单个士兵的作战能力有限。为了给战场上的士兵添个好帮手,俄罗斯研制出“涅列赫塔2号”武装机器人,准备于今年年底进行测试。“涅列赫塔2号”机器人由俄捷格加廖夫兵。萨德入韩严重威胁中国安全原标题:中国\"限韩令\"反制萨德? 媒体:不能说两者无关最近,“限韩令”的消息炒得沸沸扬扬。一时间有人惊呼:以后见不到韩国欧巴了?消息有点儿蹊跷。一方面,境外媒体、尤其是韩国媒体高度关注,因为他们听说,部分涉韩影视娱乐节目近。

    当地时间巴西里约,场地自行车比赛选手在训练中适应场地。 中新网记者 杜洋 摄中新网8月7日电 据外媒报道,里约奥运会男子自行车公路赛终点处附近6日发生爆炸,暂无人伤亡,也没有恐慌迹象,比赛在继续进行。报道称,工兵队伍曾在媒体看台附近作业,但爆。编者按:2015年12月中央经济工作会议以来,以习近平同志为总书记的党中央高瞻远瞩,统揽全局,针对当前经济新常态提出供给侧结构性改革的新战略,并从我国经济发展的阶段性特征出发,形成了“三去一降一补”这一具有重大指导性、前瞻性、针对性的经济工作部署。针对上半。

    日本东京,日本新防卫大臣稻田朋美上任后检阅自卫队原标题:日本鹰派防卫大臣让中韩警惕 外媒:安倍决意对华强硬参考消息网8月7日报道 外媒称,日本首相安倍晋三8月3日基于7月国会参议院选举的结果,改组了自民党领导层和内阁。新人事中,被日本内外视为保守鹰派。巴基斯坦军队的米17直升机原标题:俄外交部证实其公民被阿富汗塔利班扣为人质新华社莫斯科8月6日电(记者胡晓光)俄罗斯总统阿富汗问题特使、外交部第二亚洲局局长扎米尔·卡布洛夫6日证实,一名俄罗斯人已被阿富汗塔利班扣为人质。据巴基斯坦媒体报道,一名俄罗。

    【学习进行时】习近平一直高度重视经济工作,在不同场合多次就做好经济工作作出重要指示。新华网《学习进行时》原创品牌栏目“讲习所”今天推出《习近平告诉你经济应该这么抓》,梳理今年以来习近平有关经济工作的一些重要讲话,与你一起学习。我国经济发展进入新常态后。梳妆台抽屉内1080元现金被盗事发现场原标题:株洲一女子伪装成智障躲过路人 入室盗窃千余元攸县公安局桃水派出所成功破获一起入室盗窃案,抓获犯罪嫌疑人1人,追回被盗现金1080元。现金不翼而飞了7月29日9时许,桃水镇某超市老板戴某报案称,其家中三楼卧室的梳。

    天宫二号与神舟十一号载人飞行任务进入实施阶段原标题:长征二号FT2和长征二号F遥十一火箭安全运抵发射场 新一轮载人航天任务进入发射准备阶段 新华社北京8月6日电(李国利、杨欣)中国载人航天工程办公室透露,发射天宫二号空间实验室的长征二号FT2火箭及发射神舟十一号。2012年9月,日本公务船骚扰我台湾地区赴钓鱼岛海域维权渔船原标题:外交部发言人华春莹就外媒相关报道答记者问;外交部发言人华春莹就新加坡总理李显龙有关言论答记者问据外交部网站昨日消息,针对外媒称钓鱼岛海域发现大量中方船只,外交部进行回应。有记者问:。直10武装直升机直十武装直升机正式列装西部战区陆军第13集团军某陆航旅,这标志着我军所有陆航作战部队全部列装这款国产先进攻击型直升机。新型陆军机动作战立体攻防的战略目标也在逐步成为现实。周国祥美国旧金山唐人街华裔黑帮老大周国祥4日被联邦法院判处两个终身监禁。他的罪名共有162项之多,包括谋杀、敲诈勒索、洗钱和销赃。北加州联邦地区法院法官查尔斯·布雷耶说,现年56岁的周国祥在庭审期间辩称自己早已金盆洗手,但这与证据矛盾,“被告本性难移”。周。

    东盟10+3(东盟10国+中日韩3国)作者:乔良原标题:美国要的从来就不是领土!西方设计的“丛林法则”已经主导了世界几百年的发展,我们常常说殖民主义并没有远离这个世界,只是在演变。编者认为“殖民主义”可以分为三个阶段:殖民1.0时代:倡导者是西班牙、英国、法国等。夜半,一种哭声,时不时在杭州桐庐一条小溪边传来。村民早已入睡,狗和鸡也是安静的。为什么会有哭声?而且听起来还像是小孩子的?不走夜路,不知夜深。有人听到了这种哭声:有些担心,难道是有小孩走失了?几个人围过去,但什么都没有……好奇的人散去,那种声音飘飘忽。

    正在过河的俄罗斯队T72B3M原标题:96B坦克两项射击项目除一近失外全中 跑圈破记录8月6日的坦克两项大赛中,中国队103号车组驾驶96b式坦克与俄罗斯队T72B3M和津巴布韦队T72B3坦克同台竞技,在射击环节,中国队抱憾第一发炮弹打中了靶标下方的土堆,随后两发命中,直升机靶。人民网济南8月6日电 (宋翠)日前,山东省胶州市高三学生常升高考志愿被篡改成为各大媒体及社会关注的焦点。无独有偶,记者从山东菏泽市单县县委宣传部获悉,近日有群众反映单县两位高考考生志愿被同学修改,目前涉事同学已被刑事拘留。据当地媒体报道,山东单县一种高三学。

    当地时间意大利文蒂米利亚,当地大约300名难民不顾警方阻拦,跳入海中游向法国。东方IC 意大利和法国的警方试图用催泪弹阻止难民,但无济于事。图为现场。东方IC 意大利和法国的警方试图用催泪弹阻止难民,但无济于事。图为现场。东方IC 图为一名难民冲破。来自广西江底乡的吴文星是一个寻蜂人,茫茫大山中,处处都有他寻觅野蜂的踪迹。每年七月到十一月,是他最忙碌的时间,在这几个月里,他可以找到上百斤的蜂蛹。寻蜂,也成了他重要的经济来源。(图\/文 桂林晚报 游拥军) 江底乡位于广西桂林市的东北部,与广西。

  • 揭婚恋网行骗套路:根据聊天程度诱导下载诈骗APP 揭婚恋网行骗套路:根据聊天程度诱导下载诈骗APP
  • 恒大发布足协杯海报: 恒大发布足协杯海报:"劳而有得"
  • 他从小惧怕女人,却火到让全韩女生尖叫,崔康熙曾为他泪流满面 他从小惧怕女人,却火到让全韩女生尖叫,崔康熙曾为他泪流满面
  • 处女座闺蜜用网红单品搭出时髦家 全屋雪白美极了 处女座闺蜜用网红单品搭出时髦家 全屋雪白美极了
  • 三大运营商联合整治骚扰电话 要根治还需法律“亮剑” 三大运营商联合整治骚扰电话 要根治还需法律“亮剑”
  • 国家卫健委:应尽量避免学龄前儿童使用手机和电脑 国家卫健委:应尽量避免学龄前儿童使用手机和电脑
  • 北大吴谢宇的男模人生:6块腹肌 每晚都有客人点 北大吴谢宇的男模人生:6块腹肌 每晚都有客人点
  • 世园会用大数据“导游”,让游客进得来出得去 世园会用大数据“导游”,让游客进得来出得去
  • 报告:中国境外房产投资创4年新低 美国市场现净流出 报告:中国境外房产投资创4年新低 美国市场现净流出
  • 5月10日将上市 吉利星越5月1日开启预售 5月10日将上市 吉利星越5月1日开启预售

城市天气预报

  • 热门
  • ABCD
  • EFGH
  • JKLM
  • NPQR
  • STWX
  • YZ

国际城市天气预报

  • 亚洲
  • 欧洲
  • 美洲
  • 大洋洲
  • 非洲

合作伙伴

气温排行榜

  • 高温
  • 低温
排名 城市 今天气温
1 油耗2.9L 吉利缤系挑战最具技术含量的公路如果处女座喜欢你,请一定要珍惜 23~26 °
2 为何你宁愿吃生活的苦,也不愿吃学习的苦?一流销售人才都是提问高手 23~25 °
3 一加7 Pro真机照被刘作虎曝光:或采用升降前置《海岛大亨6》评测:总统宝座还真难坐! 23~25 °
4 汇源"卖身"天地壹号 朱新礼能否借机"扳回一城"?WeWork秘密提交IPO申请 曾表示去年净亏19亿美元 23~25 °
5 习近平同老挝人革党中央总书记举行会谈空巢夫妻在300㎡家中造了5个院子 请3个佣人打理 23~25 °
6 罗永浩认输:"我更喜欢被当成失败的小丑"感染用户近4000万 病毒“家族”浮现水面 23~25 °
7 现实版逆袭:废柴青年变身救人英雄如果处女座喜欢你,请一定要珍惜 20~25 °
8 鼓励远距离拼车 滴滴试运营特惠拼车功能老人初次乘飞机 嫌下客排队长打开应急舱门被拘留 20~25 °
9 网易公布2018年第三季度财报科创板第二批问询回复出炉 5公司被问了248个问题 17~25 °
10 盒马开业3年首关店:曾舍命狂奔 要开始考虑盈利了不要因为无知而违法!学生兼职多爱刷单、替考 12~25 °
查看更多>
1 "都"挺好 广汽本田奥德赛锐·混动22.98万起粤网文【2017】6527-1578号 -24~-12 °
2 怀三胎也要工作!冉莹颖挺孕肚主持脸肿到认不出专访华创公元:中国运载火箭大规模商业化还需3-5年 -24~-9 °
3 刘强东退出章泽天首投项目 曾帮她看财报唐德影视巨亏9亿:范冰冰撤退 《巴清传》播出无期 -23~-12 °
4 《海岛大亨6》评测:总统宝座还真难坐!一汽夏利牵手南京博郡 合资打造新能源汽车 -23~-8 °
5 又要地震?曝内马尔已告知马塞洛:下赛季我想去皇马!医院飞身擒小偷!见义勇为的身影中又见退役军人 -20~-8 °
6 张路解欧冠投注玄机 用魔球理论带彩民赚钱沪指窄幅震荡涨0.52% 创指4月下跌4.12% -20~-8 °
7 回头率为啥高?10位汽车顶尖设计师讲最炫车双一流高校漫步:上海交通大学 -20~-5 °
8 如果处女座喜欢你,请一定要珍惜广告周CMO谈与网易战略合作 -19~-10 °
9 俄军“特工沙皇”很神秘 俄6代战机或用氢燃料广告周CMO谈与网易战略合作 -18~-12 °
10 表忠心!拉斯:想念福建的兄弟们 等不及再见他们国内很少见的家居设计 137平里蕴含了对未来的思考 -18~-10 °
查看更多>

空气质量排行榜

  • 最优
  • 最差
排名 城市 今天空气
1 谷歌Pixel 3a现身Geekbench:处理器成谜房企去库存进行时 二线城市项目计提跌价准备 7优
2 等见到医生已癌症晚期韩媒指张紫妍案证词疑造假 尹智吾自演被报复桥段 7优
3 为什么有人干吃不胖,有人呼吸都在膨胀?全民调查:你想生几个娃?养个孩子你花了多少钱? 7优
4 凯旋!刘诗雯亲吻小球迷 刘国梁乐开花终身受益曾国藩24个锦囊 7优
5 参观姑妈129平养老宅 配色沉稳看着就很舒心拼多多回应"刷单":没有动力支持 有黑手助推转发 7优
6 合成旅坦克实弹射击 第一视角展示坦克威力尴尬!特鲁多会见安倍 两次把"日本"说成了"中国" 7优
7 以"大"欺"小" 瑞风S7 1.5T DCT智能型大兴机场跑道、滑行道等飞行区工程通过竣工验收 7优
8 藏家王珺 :玩儿也是件严肃的事儿前4月A股市值大涨11万亿!质押股份减少168亿股 7优
9 刘强东退出章泽天首投项目 曾帮她看财报这回真的哭了!苹果Siri竟然内置《复联4》彩蛋 7优
10 每周两次间歇训练 早逝或患心脏病风险降3%表忠心!拉斯:想念福建的兄弟们 等不及再见他们 7优
查看更多>
1 习近平同老挝人革党中央总书记举行会谈[找对象] 气质满分妹子 爱宠物爱生活 381严重
2 齐达内次子"习惯性"重大失误!皇马B队升级受阻了情商最低行为是不停讲道理 324严重
3 俄醉酒乘客谎称飞机有炸弹 航班被迫延误5小时恋爱中男生付出自己的全部,女友当做理所当然?你怎么看 273重度
4 这些大城市迎来"零门槛落户" 影响楼市限购吗?中国制造 | 欧洲这艘最先进大船竟是中国造 270重度
5 新奔驰刚开1小时差点要了命 4S店:退车不太可能拍下你入坑的手账,送貌美实用文具~ 269重度
6 汇源"卖身"天地壹号 朱新礼能否借机"扳回一城"?苹果发布iOS 12.3测试版:提升速度、稳定性 257重度
7 李诞自曝幽默养成秘诀《海岛大亨6》评测:总统宝座还真难坐! 256重度
8 这次蒋雯丽“忘记自己是个男人”俄醉酒乘客谎称飞机有炸弹 航班被迫延误5小时 229重度
9 冉莹颖挺着孕肚主持节目 浓妆难掩疲态Q1软件业务收入增逾14% 机构扎堆调研6只概念股 229重度
10 让工业遗产成为文化新地标全世界正遭遇跟国内相似"调控"房住不炒 226重度
查看更多>
>

友情链接: