Java面试知识点(全)-spring面试知识点二

Java面试知识点(全)https://nanxiang.blog.csdn.net/article/details/130640392
注:随时更新

Spring 事物

  1. 事务简介:
    事务管理是企业级应用程序开发中必不可少的技术,用来确保数据的完整性和一致性
    事务就是一系列的动作,它们被当作一个单独的工作单元。这些动作要么全部完成,要么全部不起作用

  2. 事务的四个关键属性(ACID)
    ① 原子性(atomicity):事务是一个原子操作,有一系列动作组成。事务的原子性确保动作要么全部完成,要么完全不起作用
    ② 一致性(consistency):一旦所有事务动作完成,事务就被提交。数据和资源就处于一种满足业务规则的一致性状态中
    ③ 隔离性(isolation):可能有许多事务会同时处理相同的数据,因此每个事物都应该与其他事务隔离开来,防止数据损坏
    ④ 持久性(durability):一旦事务完成,无论发生什么系统错误,它的结果都不应该受到影响。通常情况下,事务的结果被写到持久化存储器中

  3. Spring中的事务管理
    作为企业级应用程序框架,Spring在不同的事务管理API之上定义了一个抽象层。而应用程序开发人员不必了解底层的事务管理API,就可以使用Spring的事务管理机制。
    Spring既支持编程式事务管理(也称编码式事务),也支持声明式的事务管理
    编程式事务管理:将事务管理代码嵌入到业务方法中来控制事务的提交和回滚,在编程式事务中,必须在每个业务操作中包含额外的事务管理代码
    声明式事务管理:大多数情况下比编程式事务管理更好用。它将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。事务管理作为一种横切关注点,可以通过AOP方法模块化。Spring通过Spring AOP框架支持声明式事务管理。

  4. Spring的事务管理器
    Spring并不直接管理事务,而是提供了多种事务管理器,它们将事务管理的职责委托给JTA或其他持久化机制所提供的平台相关的事务实现。每个事务管理器都会充当某一特定平台的事务实现的门面,这使得用户在Spring中使用事务时,几乎不用关注实际的事务实现是什么。

Spring提供了许多内置事务管理器实现:

  • DataSourceTransactionManager:位于org.springframework.jdbc.datasource包中,数据源事务管理器,提供对单个javax.sql.DataSource事务管理,用于Spring JDBC抽象框架、iBATIS或MyBatis框架的事务管理;
  • JdoTransactionManager:位于org.springframework.orm.jdo包中,提供对单个javax.jdo.PersistenceManagerFactory事务管理,用于集成JDO框架时的事务管理;
  • JpaTransactionManager:位于org.springframework.orm.jpa包中,提供对单个javax.persistence.EntityManagerFactory事务支持,用于集成JPA实现框架时的事务管理;
  • HibernateTransactionManager:位于org.springframework.orm.hibernate3包中,提供对单个org.hibernate.SessionFactory事务支持,用于集成Hibernate框架时的事务管理;该事务管理器只支持Hibernate3+版本,且Spring3.0+版本只支持Hibernate 3.2+版本;
  • JtaTransactionManager:位于org.springframework.transaction.jta包中,提供对分布式事务管理的支持,并将事务管理委托给Java EE应用服务器事务管理器;
  • OC4JjtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对OC4J10.1.3+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
  • WebSphereUowTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebSphere 6.0+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持;
  • WebLogicJtaTransactionManager:位于org.springframework.transaction.jta包中,Spring提供的对WebLogic 8.1+应用服务器事务管理器的适配器,此适配器用于对应用服务器提供的高级事务的支持。

TransactionDefinition 事务定义信息(隔离、传播、超时、只读)

脏读:一个事务读取了另一个事务改写但还未提交的数据,如果这些数据被回滚,则读到的数据是无效的。
不可重复读:在同一事务中,多次读取同一数据返回的结果有所不同。
幻读:一个事务读取了几行记录后,另一个事务插入一些记录,幻读就发生了。再后来的查询中,第一个事务就会发现有些原来没有的记录。

事务隔离级别:(五种)

DEFAULT–使用后端数据库默认的隔离级别(Spring中的选择项)
READ_UNCOMMITED–允许你读取还未提交的改变了的数据。可能导致脏、幻、不可重复读
READ_COMMITTED–允许在并发事务已经提交后读取。可防止脏读,但幻读和不可重复读仍可发生
REPEATABLE_READ–对相同字段的多次读取是一致的,除非数据被事务本身改变。可防止脏、不可重复读,但幻读仍可能发生
SERIALIZABLE–完全服从ACID的隔离级别,确保不发生脏、幻、不可重复读。这在所有的隔离级别中是最慢的,它是典型的通过完全锁定在事务中涉及的数据表来完成的
其中,MySQL默认采用REPEATABLE_READ隔离级别;Oracle默认采用READ_COMMITTED隔离级别

事务传播行为:(七种)

REQUIRED–支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
SUPPORTS–支持当前事务,如果当前没有事务,就以非事务方式执行。
MANDATORY–支持当前事务,如果当前没有事务,就抛出异常。
REQUIRES_NEW–新建事务,如果当前存在事务,把当前事务挂起。
NOT_SUPPORTED–以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
NEVER–以非事务方式执行,如果当前存在事务,则抛出异常。
NESTED–如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与REQUIRED类似的操作。拥有多个可以回滚的保存点,内部回滚不会对外部事务产生影响。只对DataSourceTransactionManager有效

Spring事务的只读

“只读事务”并不是一个强制选项,它只是一个“暗示”,提示数据库驱动程序和数据库系统,这个事务并不包含更改数据的操作,那么JDBC驱动程序和数据库就有可能根据这种情况对该事务进行一些特定的优化,比方说不安排相应的数据库锁,以减轻事务
对数据库的压力,毕竟事务也是要消耗数据库的资源的。“只读事务”仅仅是一个性能优化的推荐配置而已,并非强制你要这样做不可。

Spring事务的事务超时

为了使应用程序更好的运行,事务不能运行太长的时间。因此,声明式事务的第四个特性就是超时。

Spring事务的回滚规则

默认情况下,事务只有在遇到运行期异常时才会回滚,而在遇到检查型异常时不会回滚,但是也可以声明事务在遇到特定的检查型异常时像遇到运行期异常那样回滚。同样,你还可以声明事务遇到特定的异常不回滚,即使这些异常是运行期异常

数据库锁

【为什么要锁】

数据库是一个多用户使用的共享资源,比如一个用户表t_user,两个浏览器前面的人登录了同个一个账号,把电话号码改了。当多个用户并发地存取数据时,在数据库中就会产生多个事务同时存取同一数据的情况。若对并发操作不加控制就可能会读取和存储不正确的数据,破坏数据库的一致性(脏读,不可重复读,幻读等),可能产生死锁。为了解决这个问题,加锁是一个非常重要的技术,对实现数据库并发控制是一个好的方案。简单说,当一个执行sql语句的事务想要操作表记录之前,先向数据库发出请求,对你访问的记录集加锁,在这个事务释放这个锁之前,其他事务不能对这些数据进行更新操作。

【有哪些锁】

锁包括行级锁、表级锁、悲观锁、乐观锁

  • 行级锁:
    一种它锁,防止另外事务修改此行;在使用以下语句时,Oracle会自动应用行级锁:INSERT、UPDATE、DELETE、SELECT … FOR UPDATE [OF columns] [WAIT n | NOWAIT];SELECT… FOR UPDATE语句允许用户一次锁定多条记录进行更新.使用commit或者rollback释放锁。MySql的innodb存储引擎默认是行级锁。特点:开锁大,加锁慢;会出现死锁;锁定粒度最小,发生锁冲突的概率最低,并发度也最高。适合于有大量按索引更新少量不同数据,同时又有并发查询的应用,如一些在线事务处理系统。

  • 表级锁:5种
    行共享 (ROW SHARE) – 禁止排他锁定表,与行排他类似,区别是别的事务还可以在此表上加任何排他锁。(除排他(exclusive)外)

行排他(ROW EXCLUSIVE) – 禁止使用排他锁和共享锁,其他事务依然可以并发地对相同数据表执行查询,插入,更新,删除操作,或对表内数据行加锁的操作,但不能有其他的排他锁(自身是可以的,没发现有什么用)

共享锁(SHARE) - 锁定表,对记录只读不写,多个用户可以同时在同一个表上应用此锁,在表没有被任何DML操作时,多个事务都可加锁,但只有在仅一个事务加锁的情况下只有此事务才能对表更新;当表已经被更新或者指定要更新时(select for update),任何事务都不能加此锁了。

共享行排他(SHARE ROW EXCLUSIVE) – 比共享锁更多的限制,禁止使用共享锁及更高的锁,在表没有被任何DML操作时,只有一个事务可以加锁,可以更新,书上说别的事务可以使用select for update锁定选中的数据行,可是实验后没被验证。
排他(EXCLUSIVE) –限制最强的表锁,仅允许其他用户查询该表的行。禁止修改和锁定表

行级锁和表级锁是根据锁的粒度来区分的,行记录,表都是资源,锁是作用在这些资源上的。如果粒度比较小(比如行级锁),可以增加系统的并发量但需要较大的系统开销,会影响到性能,出现死锁,,因为粒度小则操作的锁的数量会增加;如果作用在表上,粒度大,开销小,维护的锁少,不会出现死锁,但是并发是相当昂贵的,因为锁定了整个表就限制了其它事务对这个表中其他记录的访问。

悲观锁:

Pessimistic Lock正如其名,它指的是对数据被外界(包括本系统当前的其他事务,以及来自外部系统的事务处理)修改持保守悲观态度,事务每次去操作数据的时候都假设有其他事务会修改需要访问的数据,所以在访问之前都要求上锁,行锁,表锁等,读锁,写锁等,都是在做操作之前先上锁,因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能 真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系 统不会修改数据)。
一个典型的倚赖数据库的悲观锁调用: select * from account where name=”Erica” for update 这条sql 语句锁定了account 表中所有符合检索条件(name=”Erica”)的记录。 本次事务提交之前(事务提交时会释放事务过程中的锁),外界无法修改这些记录。

乐观锁:

Optimistic Lock,和悲欢锁相反,事务每次去操作数据之前,都假设其他事务不会修改这些需要访问的数据 ,所以 在访问之前不要求上锁,只是在进行更新修改操作的时候判断一下在访问的期间有没有其他人修改数据 了。它适用于多读的应用类型,冲突真的发生比较少的时候就比较好,这样省去了开销的开销,可以提高吞吐量;但如果是真的经常要发生冲突的,那每次还要去判断进行retry,反倒降低的性能,这个时候悲欢锁比较好。数据库如果提供类似于write_condition机制的其实都是提供的乐观锁。
它的实现大多是基于数据版本version记录机制。举个例子:
1.利润表t_profit中有一个 version字段,当前值为1;而总资产余额字段(balance)为$10000
2.操作员A读出version=1,从总资产减除2000,10000-2000=8000.
3.A还没操作结束,此时操作员B也读出version=1,总资产减除5000,10000-5000=5000.
4.A操作完成,把version加1,修改为2,把总资产减2000后提交更新数据库,更新成功
5.B操作了,也加version加1,修改为2,把总资产减5000后提交更新数据库,此时发现version已经为2了,如B修改后加1的version一样,不满足乐观锁策略:“提交的版本必有大于记录当前的版本才能执行”。因此B的操作请求被驳回,这样就避免了B就version=1的旧数据修改的结果覆盖了A操作的结果的可能。如没有乐观锁,那A减去2000后剩余8000,但B操作的时候是用10000-5000剩余5000的,如果B的提交成功,总资产余额就是5000,但实际情况应该是8000-5000=3000的。出现总资产表记录和实际支出不一致。

ThreadLocal

我们知道Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突。
我们使用模板类访问底层数据,根据持久化技术的不同,模板类需要绑定数据连接或会话的资源。但这些资源本身是非线程安全的,也就是说它们不能在同一时刻被多个线程共享。

虽然模板类通过资源池获取数据连接或会话,但资源池本身解决的是数据连接或会话的缓存问题,并非数据连接或会话的线程安全问题。

按照传统经验,如果某个对象是非线程安全的,在多线程环境下,对对象的访问必须采用synchronized进行线程同步。但Spring的DAO模板类并未采用线程同步机制,因为线程同步限制了并发访问,会带来很大的性能损失。

此外,通过代码同步解决性能安全问题挑战性很大,可能会增强好几倍的实现难度。那模板类究竟仰丈何种魔法神功,可以在无需同步的情况下就化解线程安全的难题呢?答案就是ThreadLocal!

ThreadLocal在Spring中发挥着重要的作用,在管理request作用域的Bean、事务管理、任务调度、AOP等模块都出现了它们的身影,起着举足轻重的作用。要想了解Spring事务管理的底层技术,ThreadLocal是必须攻克的山头堡垒。

ThreadLocal是什么

早在JDK1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。使用这个工具类可以很简洁地编写出优美的多线程程序。
ThreadLocal很容易让人望文生义,想当然地认为是一个“本地线程”。其实,ThreadLocal并不是一个Thread,而是Thread的局部变量,也许把它命名为ThreadLocalVariable更容易让人理解一些。
当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

从线程的角度看,目标变量就象是线程的本地变量,这也是类名中“Local”所要表达的意思。
线程局部变量并不是Java的新发明,很多语言(如IBM IBM XLFORTRAN)在语法层面就提供线程局部变量。在Java中没有提供在语言级支持,而是变相地通过ThreadLocal的类提供支持。
所以,在Java中编写线程局部变量的代码相对来说要笨拙一些,因此造成线程局部变量没有在Java开发者中得到很好的普及。

ThreadLocal的接口方法

ThreadLocal类接口很简单,只有4个方法,我们先来了解一下:
void set(Object value)
设置当前线程的线程局部变量的值。
public Object get()
该方法返回当前线程所对应的线程局部变量。
public void remove()
将当前线程局部变量的值删除,目的是为了减少内存的占用,该方法是JDK5.0新增的方法。需要指出的是,当线程结束后,对应该线程的局部变量将自动被垃圾回收,所以显式调用该方法清除线程的局部变量并不是必须的操作,但它可以加快内存回收的速度。
protected Object initialValue()
返回该线程局部变量的初始值,该方法是一个protected的方法,显然是为了让子类覆盖而设计的。这个方法是一个延迟调用方法,在线程第1次调用get()或set(Object)时才执行,并且仅执行1次。ThreadLocal中的缺省实现直接返回一个null。
值得一提的是,在JDK5.0中,ThreadLocal已经支持泛型,该类的类名已经变为ThreadLocal。API方法也相应进行了调整,新版本的API方法分别是voidset(T value)、T get()以及T initialValue()。
ThreadLocal是如何做到为每一个线程维护变量的副本的呢?其实实现的思路很简单:在ThreadLocal类中有一个Map,用于存储每一个线程的变量副本,Map中元素的键为线程对象,而值对应线程的变量副本。我们自己就可以提供一个简单的实现版本:

	//SimpleThreadLocal
	class SimpleThreadLocal {
	    private MapvalueMap = Collections.synchronizedMap(new HashMap());
	    public voidset(Object newValue) {
	       valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本
	    }
	    publicObject get() {
	       Thread currentThread = Thread.currentThread();
	       Object o = valueMap.get(currentThread);// ②返回本线程对应的变量
	       if (o == null &&!valueMap.containsKey(currentThread)) {// ③如果在Map中不存在,放到Map
	           // 中保存起来。
	           o = initialValue();
	           valueMap.put(currentThread, o);
	       }
	       return o;
	    }
	    public voidremove() {
	       valueMap.remove(Thread.currentThread());
	    }
	    publicObject initialValue() {
	       return null;
	    }
	}

虽然这个ThreadLocal实现版本显得比较幼稚,但它和JDK所提供的ThreadLocal类在实现思路上是相近的。

一个TheadLocal实例
下面,我们通过一个具体的实例了解一下ThreadLocal的具体使用方法


	public class SequenceNumber {
	    //①通过匿名内部类覆盖ThreadLocal的initialValue()方法,指定初始值
	    privatestatic ThreadLocal<Integer> seqNum =new ThreadLocal<Integer>() {
	       public Integer initialValue() {
	           return 0;
	       }
	    };
	    //②获取下一个序列值
	    public intgetNextNum() {
	       seqNum.set(seqNum.get() + 1);
	       return seqNum.get();
	    }
	    publicstatic void main(String[] args)
	    {
	       SequenceNumber sn = new SequenceNumber();
	       // ③ 3个线程共享sn,各自产生序列号
	       TestClient t1 = new TestClient(sn);
	       TestClient t2 = new TestClient(sn);
	       TestClient t3 = new TestClient(sn);
	       t1.start();
	       t2.start();
	       t3.start();
	    }
	    privatestatic class TestClient extends Thread
	    {
	       private SequenceNumber sn;
       	   public TestClient(SequenceNumber sn) {
	           this.sn = sn;
	       }
	       public void run()
	       {
	           for (int i = 0; i < 3; i++) {
	               // ④每个线程打出3个序列值
	               System.out.println("thread[" + Thread.currentThread().getName()+"]sn[" + sn.getNextNum() + "]");
	           }
	       }
	    }
	}

通常我们通过匿名内部类的方式定义ThreadLocal的子类,提供初始的变量值,如例子中①处所示。TestClient线程产生一组序列号,在③处,我们生成3个TestClient,它们共享同一个SequenceNumber实例。运行以上代码,在控制台上输出以下的结果:
thread[Thread-2] sn[1]
thread[Thread-0] sn[1]
thread[Thread-1] sn[1]
thread[Thread-2] sn[2]
thread[Thread-0] sn[2]
thread[Thread-1] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[3]
thread[Thread-1] sn[3]

考察输出的结果信息,我们发现每个线程所产生的序号虽然都共享同一个SequenceNumber实例,但它们并没有发生相互干扰的情况,而是各自产生独立的序列号,这是因为我们通过ThreadLocal为每一个线程提供了单独的副本。

Thread同步机制的比较

ThreadLocal和线程同步机制相比有什么优势呢?ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。

在同步机制中,通过对象的锁机制保证同一时间只有一个线程访问变量。这时该变量是多个线程共享的,使用同步机制要求程序慎密地分析什么时候对变量进行读写,什么时候需要锁定某个对象,什么时候释放对象锁等繁杂的问题,程序设计和编写难度相对较大。

而ThreadLocal则从另一个角度来解决多线程的并发访问。ThreadLocal会为每一个线程提供一个独立的变量副本,从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本,从而也就没有必要对该变量进行同步了。
ThreadLocal提供了线程安全的共享对象,在编写多线程代码时,可以把不安全的变量封装进ThreadLocal。

由于ThreadLocal中可以持有任何类型的对象,低版本JDK所提供的get()返回的是Object对象,需要强制类型转换。但JDK5.0通过泛型很好的解决了这个问题,在一定程度地简化ThreadLocal的就使用了JDK5.0新的ThreadLocal版本。
概括起来说,对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。前者仅提供一份变量,让不同的线程排队访问,而后者为每一个线程都提供了一份变量,因此可以同时访问而互不影响。

Spring使用ThreadLocal解决线程安全问题

我们知道在一般情况下,只有无状态的Bean才可以在多线程环境下共享,在Spring中,绝大部分Bean都可以声明为singleton作用域。就是因为Spring对一些Bean(如RequestContextHolder、TransactionSynchronizationManager、LocaleContextHolder等)中非线程安全状态采用ThreadLocal进行处理,让它们也成为线程安全的状态,因为有状态的Bean就可以在多线程中共享了。

一般的Web应用划分为展现层、服务层和持久层三个层次,在不同的层中编写对应的逻辑,下层通过接口向上层开放功能调用。在一般情况下,从接收请求到返回响应所经过的所有程序调用都同属于一个线程,

Spring TaskExecutor线程池

多线程并发处理起来通常比较麻烦,如果你使用spring容器来管理业务bean,事情就好办了多了。spring封装了Java的多线程的实现,你只需要关注于并发事物的流程以及一些并发负载量等特性,具体来说如何使用spring来处理并发事务:

  • 1.了解 TaskExecutor接口
    Spring的TaskExecutor接口等同于java.util.concurrent.Executor接口。 实际上,它存在的主要原因是为了在使用线程池的时候,将对Java5的依赖抽象出来。 这个接口只有一个方法execute(Runnable task),它根据线程池的语义和配置,来接受一个执行任务。最初创建TaskExecutor是为了在需要时给其他Spring组件提供一个线程池的抽象。 例如ApplicationEventMulticaster组件、JMS的 AbstractMessageListenerContainer和对Quartz的整合都使用了TaskExecutor抽象来提供线程池。 当然,如果你的bean需要线程池行为,你也可以使用这个抽象层。

    1. TaskExecutor接口的实现类
      (1)SimpleAsyncTaskExecutor 类
      这个实现不重用任何线程,或者说它每次调用都启动一个新线程。但是,它还是支持对并发总数设限,当超过线程并发总数限制时,阻塞新的调用,直到有位置被释放。如果你需要真正的池,请继续往下看。
      (2)SyncTaskExecutor类
      这个实现不会异步执行。相反,每次调用都在发起调用的线程中执行。它的主要用处是在不需要多线程的时候,比如简单的test case。
      (3)ConcurrentTaskExecutor 类
      这个实现是对Java 5 java.util.concurrent.Executor类的包装。有另一个备选, ThreadPoolTaskExecutor类,它暴露了Executor的配置参数作为bean属性。很少需要使用ConcurrentTaskExecutor, 但是如果ThreadPoolTaskExecutor不敷所需,ConcurrentTaskExecutor是另外一个备选。
      (4)SimpleThreadPoolTaskExecutor 类
      这个实现实际上是Quartz的SimpleThreadPool类的子类,它会监听Spring的生命周期回调。当你有线程池,需要在Quartz和非Quartz组件中共用时,这是它的典型用处。
      (5)ThreadPoolTaskExecutor 类
      它不支持任何对java.util.concurrent包的替换或者下行移植。Doug Lea和Dawid Kurzyniec对java.util.concurrent的实现都采用了不同的包结构,导致它们无法正确运行。 这个实现只能在Java 5环境中使用,但是却是这个环境中最常用的。它暴露的bean properties可以用来配置一个java.util.concurrent.ThreadPoolExecutor,把它包装到一个TaskExecutor中。如果你需要更加先进的类,比如ScheduledThreadPoolExecutor,我们建议你使用ConcurrentTaskExecutor来替代。
      (6)TimerTaskExecutor类
      这个实现使用一个TimerTask作为其背后的实现。它和SyncTaskExecutor的不同在于,方法调用是在一个独立的线程中进行的,虽然在那个线程中是同步的。
      (7)WorkManagerTaskExecutor类
      这个实现使用了CommonJ WorkManager作为其底层实现,是在Spring context中配置CommonJ WorkManager应用的最重要的类。和SimpleThreadPoolTaskExecutor类似,这个类实现了WorkManager接口,因此可以直接作为WorkManager使用。

案例-注册TaskExecutor

  @Configuration
  public class WebMvcConfigurerAdpter extends AbstractWebMvcConfigurerAdpter {
 
     @Override
     public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
         super.configureMessageConverters(converters);
         WafJsonMapper.getMapper().enable(DeserializationFeature.FAIL_ON_NUMBERS_FOR_ENUMS);
    }
     @Bean
     public TaskExecutor taskExecutor() {
         ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(5);
         executor.setMaxPoolSize(10);
         return executor;
     }
 }
  @Service
 public class TaskService {
 
     @Autowired
     private TaskExecutor executor;
 
    public void execute() {
         executor.execute(new Runnable() {
             @Override
             public void run() {
                for (int i = 0; i < 10; i++) {
                     try {
                        Thread.sleep(1000);
                        System.out.println("task running ...");
                     } catch (Exception e) {
 
 @RestController
 @RequestMapping(value = "/v0.1")
 public class TaskController {
 
    @Autowired
     private TaskService taskService;

    @RequestMapping()
    public Object execute() {
         taskService.execute();
         Map res = new HashMap();
         res.put("result", "success");
         return res;
     }
 }

程序不会等到10个线程都跑完才返回结果,不是阻塞程序,返回结果后,线程仍然在执行。

案例:

 ThreadPoolTaskExecutor poolTaskExecutor = new ThreadPoolTaskExecutor();  
//线程池所使用的缓冲队列  
 poolTaskExecutor.setQueueCapacity(200);  
 //线程池维护线程的最少数量  
 poolTaskExecutor.setCorePoolSize(5);  
//线程池维护线程的最大数量  
poolTaskExecutor.setMaxPoolSize(1000);  
 //线程池维护线程所允许的空闲时间  
 poolTaskExecutor.setKeepAliveSeconds(30000);  
 poolTaskExecutor.initialize();  

配置解释

当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5、 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。

xml配置使用

 <!-- 配置线程池 -->  
 <bean id ="taskExecutor"  class ="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor" >  
     <!-- 线程池维护线程的最少数量 -->  
 <span style="white-space:pre">  </span><property name ="corePoolSize" value ="5" />  
     <!-- 线程池维护线程所允许的空闲时间 -->  
 <span style="white-space:pre">  </span><property name ="keepAliveSeconds" value ="30000" />  
     <!-- 线程池维护线程的最大数量 -->  
 <span style="white-space:pre">  </span><property name ="maxPoolSize" value ="1000" />  
     <!-- 线程池所使用的缓冲队列 -->  
 <span style="white-space:pre">  </span><property name ="queueCapacity" value ="200" />  
 </bean>  

 ApplicationContext ctx =  new ClassPathXmlApplicationContext("applicationContext.xml");
 ThreadPoolTaskExecutor poolTaskExecutor = (ThreadPoolTaskExecutor)ctx.getBean("taskExecutor");
 
 Thread udpThread = new Thread(udp);
 poolTaskExecutor.execute(udpThread);
 获取当前线程池活动的线程数:
 int count = poolTaskExecutor.getActiveCount();
 logger.debug("[x] - now threadpool active threads totalNum : " +count);

外传

😜 原创不易,如若本文能够帮助到您的同学
🎉 支持我:关注我+点赞👍+收藏⭐️
📝 留言:探讨问题,看到立马回复
💬 格言:己所不欲勿施于人 扬帆起航、游历人生、永不言弃!🔥

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

南巷Dong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值