Spring-AOP源码解析(案例+解析)

1.AOP解释

摘自百度百科

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

2.如何使用SpringAOP

准备工作

1、导入aop模块;Spring AOP:(spring-aspects)
2、定义一个业务逻辑类(MathCalculator);在业务逻辑运行的时候将日志进行打印(方法之前、方法运行结束、方法出现异常,xxx)
3、定义一个日志切面类(LogAspects):切面类里面的方法需要动态感知MathCalculator.div运行到哪里然后执行;
通知方法:
前置通知(@Before):logStart:在目标方法(div)运行之前运行
后置通知(@After):logEnd:在目标方法(div)运行结束之后运行(无论方法正常结束还是异常结束)
返回通知(@AfterReturning):logReturn:在目标方法(div)正常返回之后运行
异常通知(@AfterThrowing):logException:在目标方法(div)出现异常以后运行
环绕通知(@Around):动态代理,手动推进目标方法运行(joinPoint.procced())
4、给切面类的目标方法标注何时何地运行(通知注解);
5、将切面类和业务逻辑类(目标方法所在类)都加入到容器中;
6、必须告诉Spring哪个类是切面类(给切面类上加一个注解:@Aspect)
7、给配置类中加 @EnableAspectJAutoProxy 【开启基于注解的aop模式】

切面类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
@Aspect
public class LogAspects {

//抽取公共的切入点表达式
//1、本类引用
//2、其他的切面引用
@Pointcut("execution(public int com.bernard.aop.MathCalculator.*(..))")
public void pointCut(){};

//@Before在目标方法之前切入;切入点表达式(指定在哪个方法切入)
@Before("pointCut()")
public void logStart(JoinPoint joinPoint){
Object[] args = joinPoint.getArgs();
System.out.println(""+joinPoint.getSignature().getName()+"运行。。。@Before:参数列表是:{"+ Arrays.asList(args)+"}");
}

@After("com.bernard.aop.LogAspects.pointCut()")
public void logEnd(JoinPoint joinPoint){
System.out.println(""+joinPoint.getSignature().getName()+"结束。。。@After");
}

//JoinPoint一定要出现在参数表的第一位
@AfterReturning(value="pointCut()",returning="result")
public void logReturn(JoinPoint joinPoint,Object result){
System.out.println(""+joinPoint.getSignature().getName()+"正常返回。。。@AfterReturning:运行结果:{"+result+"}");
}

@AfterThrowing(value="pointCut()",throwing="exception")
public void logException(JoinPoint joinPoint,Exception exception){
System.out.println(""+joinPoint.getSignature().getName()+"异常。。。异常信息:{"+exception+"}");
}

@Around("pointCut()")
public Object logAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around: 执行目标方法之前");
Object proceed = proceedingJoinPoint.proceed();
System.out.println("Around: 执行目标方法之后");
return proceed;
}

}

配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
@EnableAspectJAutoProxy
@Configuration
public class MainConfigOfAOP {

//业务逻辑类加入容器中
@Bean
public MathCalculator calculator(){
return new MathCalculator();
}

//切面类加入到容器中
@Bean
public LogAspects logAspects(){
return new LogAspects();
}
}

业务逻辑类

1
2
3
4
5
6
7
8
9
public class MathCalculator {

public int div(int i,int j){
System.out.println("MathCalculator...div...");
return i/j;
}

}

测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
 public class IOCTest_AOP {

@Test
public void test01(){
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfAOP.class);

//1、不要自己创建对象
// MathCalculator mathCalculator = new MathCalculator();
// mathCalculator.div(1, 1);
MathCalculator mathCalculator = applicationContext.getBean(MathCalculator.class);

mathCalculator.div(1, 1);

applicationContext.close();
}

}

结果

3.@EnableAspectJAutoProxy到底干了啥?

开启SrpingAOP功能时,有使用一个注解为@EnableAspectJAutoProxy,查看该注解时,可以看到这个注解Import了一个类

跟进这个类,可以看到他是继承了ImportBeanDefinitionRegistrar,并且实现了ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法,为了是导入自定义组件

再跟进这个方法


整个@EnableAspectJAutoProxy注解作用就是为了注册一个id为org.springframework.aop.config.internalAutoProxyCreator,class为AnnotationAwareAspectJAutoProxyCreator.class的BeanDefinition

AnnotationAwareAspectJAutoProxyCreatord结构

在它的所有父类接口中,有一个抽象类AbstractAutoProxyCreator

继承了:ProxyProcessorSupport类
实现了:SmartInstantiationAwareBeanPostProcessor和BeanFactoryAware接口

具体类图如下:
主要为了说明类之间的继承关系,方法的返回值和参数未在图中列出

这里,我们要重点关注两个实现

  • SmartInstantiationAwareBeanPostProcessor:后置处理器(在我们bean初始化完成前后要做的事情)
    postProcessBeforeInstantiation()和postProcessAfterInstantiation()两个方法做了什么事?
  • AbstractAdvisorAutoProxyCreator重写的setBeanFactory()做了什么事?
  • AnnotationAwareAspectJAutoProxyCreator的initBeanFactory做了什么事?

重点:AnnotationAwareAspectJAutoProxyCreator这个类是贯穿SpringAOP整个功能的类
重点:AnnotationAwareAspectJAutoProxyCreator这个类是贯穿SpringAOP整个功能的类
重点:AnnotationAwareAspectJAutoProxyCreator这个类是贯穿SpringAOP整个功能的类

4 Spring的refresh()

我们进入AnnotationConfigApplicationContext的构造方法,this()和register(annotatedClasses)主要初始化spring的基础组件和
注册一个或多个要处理的带注释的类,AOP功能主要在refresh()方法

进入refresh(),里面的逻辑有很多,但Spring-AOP的实现主要看两个方法

registerBeanPostProcessors(beanFactory); // 注册BeanPostProcessor(Bean的后置处理器)
finishBeanFactoryInitialization(beanFactory); // 初始化所有剩下的单实例bean;

因为AnnotationAwareAspectJAutoProxyCreator也是一个BeanPostProcessor,所以AnnotationAwareAspectJAutoProxyCreator的注册就是在registerBeanPostProcessors(beanFactory)方法中实现的,在这一节中,会分很多小节,对registerBeanPostProcessors和finishBeanFactoryInitialization两个方法中的细节进行介绍

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
//1 刷新前的预处理
prepareRefresh();
//2 获取BeanFactory;刚创建的默认DefaultListableBeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
//3 BeanFactory的预准备工作(BeanFactory进行一些设置)
prepareBeanFactory(beanFactory);
try {
// 4 BeanFactory准备工作完成后进行的后置处理工作;
// 4.1)、抽象的方法,当前未做处理。子类通过重写这个方法来在BeanFactory创建并预准备完成以后做进一步的设置
postProcessBeanFactory(beanFactory);
/**************************以上是BeanFactory的创建及预准备工作 ****************/

// 5 执行BeanFactoryPostProcessor的方法;
//BeanFactoryPostProcessor:BeanFactory的后置处理器。在BeanFactory标准初始化之后执行的;
//它的重要两个接口:BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor
invokeBeanFactoryPostProcessors(beanFactory);
//6 注册BeanPostProcessor(Bean的后置处理器)
registerBeanPostProcessors(beanFactory);
// 7 initMessageSource();初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
initMessageSource();
// 8 初始化事件派发器
initApplicationEventMulticaster();
// 9 子类重写这个方法,在容器刷新的时候可以自定义逻辑;
onRefresh();
// 10 给容器中将所有项目里面的ApplicationListener注册进来
registerListeners();
// 11.初始化所有剩下的单实例bean;
finishBeanFactoryInitialization(beanFactory);
// 12.完成BeanFactory的初始化创建工作;IOC容器就创建完成;
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
// Destroy already created singletons to avoid dangling resources.
destroyBeans();
// Reset 'active' flag.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}

4.1 registerBeanPostProcessors方法

registerBeanPostProcessors()的主要作用是注册bean的后置处理器来方便拦截bean的创建
下面的源码中,我对一些关键点都做了注释,也会对一些关键方法单独做一个小节来介绍

  • 1)、先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor

基于章节2的测试,从截图看出,ioc容器有四个BeanPostProcessor
AutowiredAnnotationBeanPostProcessor(处理被@Autowired注解修饰的bean并注入)
RequiredAnnotationBeanPostProcessor(处理被@Required注解修饰的方法)
CommonAnnotationBeanPostProcessor(处理@PreDestroy、@PostConstruct、@Resource等多个注解的作用)等
AnnotationAwareAspectJAutoProxyCreator(处理@EnableAspectJAutoProxy注解,也就是AOP)

  • 2)、给容器中加别的BeanPostProcessor
    
  • 3)、优先注册实现了PriorityOrdered接口的BeanPostProcessor
    
  • 4)、再给容器中注册实现了Ordered接口的BeanPostProcessor  
    
  • 5)、注册没实现优先级接口的BeanPostProcessor  
    
  • 6)、把BeanPostProcessor注册到BeanFactory中;
    

在3、4、5步骤中,注册BeanPostProcessor的时候都会调用beanFactory.getBean(ppName, BeanPostProcessor.class)方法,beanFactory.getBean方法会着重介绍,bean的获取和创建都是基于这个方法
AnnotationAwareAspectJAutoProxyCreator就是在第4步创建的,因为它的父类ProxyProcessorSupport实现了Ordered接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
public static void registerBeanPostProcessors(
ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {

// 1.先获取ioc容器已经定义了的需要创建对象的所有BeanPostProcessor
String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);

// Register BeanPostProcessorChecker that logs an info message when
// a bean is created during BeanPostProcessor instantiation, i.e. when
// a bean is not eligible for getting processed by all BeanPostProcessors.
// 2.给容器中加其他的BeanPostProcessor
int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));

// 3.优先注册实现了PriorityOrdered接口的BeanPostProcessor;
// Separate between BeanPostProcessors that implement PriorityOrdered,
// Ordered, and the rest.
List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
List<String> orderedPostProcessorNames = new ArrayList<>();
List<String> nonOrderedPostProcessorNames = new ArrayList<>();
for (String ppName : postProcessorNames) {
if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {

// 3.1在beanFactory中获取BeanPostProcessor
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
priorityOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
orderedPostProcessorNames.add(ppName);
}
else {
nonOrderedPostProcessorNames.add(ppName);
}
}

// First, register the BeanPostProcessors that implement PriorityOrdered.
sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);

// 4.优先注册实现了PriorityOrdered接口的BeanPostProcessor
// Next, register the BeanPostProcessors that implement Ordered.
List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>();
for (String ppName : orderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
orderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}
sortPostProcessors(orderedPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, orderedPostProcessors);

// 5.注册没实现优先级接口的BeanPostProcessor
// Now, register all regular BeanPostProcessors.
List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>();
for (String ppName : nonOrderedPostProcessorNames) {
BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
nonOrderedPostProcessors.add(pp);
if (pp instanceof MergedBeanDefinitionPostProcessor) {
internalPostProcessors.add(pp);
}
}

registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);

// 6)、把BeanPostProcessor注册到BeanFactory中;
// Finally, re-register all internal BeanPostProcessors.
sortPostProcessors(internalPostProcessors, beanFactory);
registerBeanPostProcessors(beanFactory, internalPostProcessors);

// Re-register post-processor for detecting inner beans as ApplicationListeners,
// moving it to the end of the processor chain (for picking up proxies etc).
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

大致脑图如下:

4.2 getBean

AnnotationAwareAspectJAutoProxyCreator是一个BeanPostProcessor
注册BeanPostProcessor,实际上就是创建BeanPostProcessor对象,保存在容器中;
这里以AnnotationAwareAspectJAutoProxyCreator为例:

从beanFactory.getBean()方法获取bean实例的步骤大致如下:

  • 1)、创建Bean的实例:AbstractBeanFactory.getBean()->doGetBean()
    • 1)、将创建的Bean添加到缓存中singletonObjects(),如果能获取到说明这个Bean之前被创建过(所有创建过的单实例Bean都会被缓存起来)
    • 2)、缓存中获取不到,开始Bean的创建对象流程
    • 3)、标记当前bean已经被创建
    • 4)、获取Bean的定义信息
    • 5)、获取当前Bean依赖的其他Bean;如果有按照getBean()把依赖的Bean先创建出来
    • 6)、启动单实例的bean的创建流程
    • 7)、创建Bean ,见4.3
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
      @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {

      final String beanName = transformedBeanName(name);
      Object bean;

      // 1)、将创建的Bean添加到缓存中singletonObjects(),如果没有获取到创建bean
      // Eagerly check singleton cache for manually registered singletons.
      Object sharedInstance = getSingleton(beanName);
      if (sharedInstance != null && args == null) {
      if (logger.isDebugEnabled()) {
      if (isSingletonCurrentlyInCreation(beanName)) {
      logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
      "' that is not fully initialized yet - a consequence of a circular reference");
      }
      else {
      logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
      }
      }
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
      }
      // 2)、缓存中获取不到,开始Bean的创建对象流程
      else {
      // Fail if we're already creating this bean instance:
      // We're assumably within a circular reference.
      if (isPrototypeCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(beanName);
      }

      // 获取父beanFatory 检查这个bean是否创建了
      // Check if bean definition exists in this factory.
      BeanFactory parentBeanFactory = getParentBeanFactory();
      if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
      // Not found -> check parent.
      String nameToLookup = originalBeanName(name);
      if (parentBeanFactory instanceof AbstractBeanFactory) {
      return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
      nameToLookup, requiredType, args, typeCheckOnly);
      }
      else if (args != null) {
      // Delegation to parent with explicit args.
      return (T) parentBeanFactory.getBean(nameToLookup, args);
      }
      else {
      // No args -> delegate to standard getBean method.
      return parentBeanFactory.getBean(nameToLookup, requiredType);
      }
      }

      // 3)、标记当前bean已经被创建
      if (!typeCheckOnly) {
      markBeanAsCreated(beanName);
      }

      try {
      // 4)、获取Bean的定义信息
      final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
      checkMergedBeanDefinition(mbd, beanName, args);

      // 5)、获取当前Bean依赖的其他Bean;如果有按照getBean()把依赖的Bean先创建出来
      // Guarantee initialization of beans that the current bean depends on.
      String[] dependsOn = mbd.getDependsOn();
      if (dependsOn != null) {
      for (String dep : dependsOn) {
      if (isDependent(beanName, dep)) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
      "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'");
      }
      registerDependentBean(dep, beanName);
      try {
      getBean(dep);
      }
      catch (NoSuchBeanDefinitionException ex) {
      throw new BeanCreationException(mbd.getResourceDescription(), beanName,
      "'" + beanName + "' depends on missing bean '" + dep + "'", ex);
      }
      }
      }

      // 6)、启动单实例的bean的创建流程
      if (mbd.isSingleton()) {
      // //获取到单实例bean后,添加到缓存中 singletonObjects()
      sharedInstance = getSingleton(beanName, () -> {
      try {
      // 7)、创建单实例Bean ,见4.2.1
      return createBean(beanName, mbd, args);
      }
      catch (BeansException ex) {
      // Explicitly remove instance from singleton cache: It might have been put there
      // eagerly by the creation process, to allow for circular reference resolution.
      // Also remove any beans that received a temporary reference to the bean.
      destroySingleton(beanName);
      throw ex;
      }
      });
      bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
      }

      else if (mbd.isPrototype()) {
      // It's a prototype -> create a new instance.
      Object prototypeInstance = null;
      try {
      beforePrototypeCreation(beanName);
      prototypeInstance = createBean(beanName, mbd, args);
      }
      finally {
      afterPrototypeCreation(beanName);
      }
      bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
      }

      else {
      String scopeName = mbd.getScope();
      final Scope scope = this.scopes.get(scopeName);
      if (scope == null) {
      throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'");
      }
      try {
      Object scopedInstance = scope.get(beanName, () -> {
      beforePrototypeCreation(beanName);
      try {
      return createBean(beanName, mbd, args);
      }
      finally {
      afterPrototypeCreation(beanName);
      }
      });
      bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
      }
      catch (IllegalStateException ex) {
      throw new BeanCreationException(beanName,
      "Scope '" + scopeName + "' is not active for the current thread; consider " +
      "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
      ex);
      }
      }
      }
      catch (BeansException ex) {
      cleanupAfterBeanCreationFailure(beanName);
      throw ex;
      }
      }

      // Check if required type matches the type of the actual bean instance.
      if (requiredType != null && !requiredType.isInstance(bean)) {
      try {
      T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
      if (convertedBean == null) {
      throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
      return convertedBean;
      }
      catch (TypeMismatchException ex) {
      if (logger.isDebugEnabled()) {
      logger.debug("Failed to convert bean '" + name + "' to required type '" +
      ClassUtils.getQualifiedName(requiredType) + "'", ex);
      }
      throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
      }
      }
      return (T) bean;
      }
  • 2)、populateBean;给bean的各种属性赋值
    
  • 3)、initializeBean:初始化bean;
    
    • 1)、invokeAwareMethods():处理Aware接口的方法回调
      
    • 2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
    • 3)、invokeInitMethods();执行自定义的初始化方法
    • 4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization();
  • 4)、BeanPostProcessor(AnnotationAwareAspectJAutoProxyCreator)创建成功;–>aspectJAdvisorsBuilder

4.2.1 createBean

AbstractAutowireCapableBeanFactory.createBean() 这个类中,有以下几个步骤:

  • 1)、解析bean的类型
  • 2、让BeanPostProcessor先拦截返回代理对象–>调用resolveBeforeInstantiation方法
  • 3)、没有对象,创建一个bean,调用doCreateBean方法
  • 4)、返回创建的bean
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    @Override
    protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)
    throws BeanCreationException {

    if (logger.isDebugEnabled()) {
    logger.debug("Creating instance of bean '" + beanName + "'");
    }
    RootBeanDefinition mbdToUse = mbd;

    // 1)、解析bean的类型
    // Make sure bean class is actually resolved at this point, and
    // clone the bean definition in case of a dynamically resolved Class
    // which cannot be stored in the shared merged bean definition.
    Class<?> resolvedClass = resolveBeanClass(mbd, beanName);
    if (resolvedClass != null && !mbd.hasBeanClass() && mbd.getBeanClassName() != null) {
    mbdToUse = new RootBeanDefinition(mbd);
    mbdToUse.setBeanClass(resolvedClass);
    }

    // Prepare method overrides.
    try {
    mbdToUse.prepareMethodOverrides();
    }
    catch (BeanDefinitionValidationException ex) {
    throw new BeanDefinitionStoreException(mbdToUse.getResourceDescription(),
    beanName, "Validation of method overrides failed", ex);
    }

    try {
    // 2、让BeanPostProcessor先拦截返回代理对象
    // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
    Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
    if (bean != null) {
    return bean;
    }
    }
    catch (Throwable ex) {
    throw new BeanCreationException(mbdToUse.getResourceDescription(), beanName,
    "BeanPostProcessor before instantiation of bean failed", ex);
    }

    try {
    // 3)、没有对象 创建一个bean
    Object beanInstance = doCreateBean(beanName, mbdToUse, args);
    if (logger.isDebugEnabled()) {
    logger.debug("Finished creating instance of bean '" + beanName + "'");
    }
    // 4)、返回创建的bean
    return beanInstance;
    }
    catch (BeanCreationException | ImplicitlyAppearedSingletonException ex) {
    // A previously detected exception with proper bean creation context already,
    // or illegal singleton state to be communicated up to DefaultSingletonBeanRegistry.
    throw ex;
    }
    catch (Throwable ex) {
    throw new BeanCreationException(
    mbdToUse.getResourceDescription(), beanName, "Unexpected exception during bean creation", ex);
    }
    }

4.2.2 resolveBeforeInstantiation返回代理对象

1
2
3
4
5
6
         // 2、让BeanPostProcessor先拦截返回代理对象
// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.
Object bean = resolveBeforeInstantiation(beanName, mbdToUse);
if (bean != null) {
return bean;
}

resolveBeforeInstantiation方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
@Nullable
protected Object resolveBeforeInstantiation(String beanName, RootBeanDefinition mbd) {
Object bean = null;
if (!Boolean.FALSE.equals(mbd.beforeInstantiationResolved)) {
// Make sure bean class is actually resolved at this point.
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
Class<?> targetType = determineTargetType(beanName, mbd);
if (targetType != null) {
bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
if (bean != null) {
bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
}
}
}
mbd.beforeInstantiationResolved = (bean != null);
}
return bean;
}

applyBeanPostProcessorsBeforeInstantiation方法

1
2
3
4
5
6
7
8
9
10
11
12
13
@Nullable
protected Object applyBeanPostProcessorsBeforeInstantiation(Class<?> beanClass, String beanName) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
Object result = ibp.postProcessBeforeInstantiation(beanClass, beanName);
if (result != null) {
return result;
}
}
}
return null;
}

这里源码的注释是“让beanpostprocessor有机会返回代理而不是目标bean实例”,如果成功返回代理对象,则直接返回,否则再执行doCreateBean来创建实例。从开始进入到applyBeanPostProcessorsBeforeInstantiation这个方法中,会但是对于AbstractAutoProxyCreator这个类来说,在执行applyBeanPostProcessorsBeforeInstantiation方法时,它并没有创建,所以并不会执行AbstractAutoProxyCreator的applyBeanPostProcessorsBeforeInstantiation方法和applyBeanPostProcessorsAfterInitialization,返回null,在后面的逻辑中,如果resolveBeforeInstantiation返回null,会执行doCreateBean方法,创建AbstractAutoProxyCreator

4.2.3 doCreateBean创建实例

当resolveBeforeInstantiation返回null时,执行doCreateBean
doCreateBean主要流程是:

  • 1)、创建Bean的实例
  • 2)、populateBean;给bean的各种属性赋值
    
  • 3)、initializeBean:初始化bean;
    

它的主要步骤在于initializeBean方法上

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args)
throws BeanCreationException {

// Instantiate the bean.
BeanWrapper instanceWrapper = null;
if (mbd.isSingleton()) {

// 1)、【创建Bean实例】利用工厂方法或者对象的构造器创建出Bean实例;
instanceWrapper = this.factoryBeanInstanceCache.remove(beanName);
}
if (instanceWrapper == null) {
instanceWrapper = createBeanInstance(beanName, mbd, args);
}
final Object bean = instanceWrapper.getWrappedInstance();
Class<?> beanType = instanceWrapper.getWrappedClass();
if (beanType != NullBean.class) {
mbd.resolvedTargetType = beanType;
}

// Allow post-processors to modify the merged bean definition.
synchronized (mbd.postProcessingLock) {
if (!mbd.postProcessed) {
try {
//调用MergedBeanDefinitionPostProcessor的postProcessMergedBeanDefinition(mbd, beanType,
beanName);
//判断是否为:MergedBeanDefinitionPostProcessor 类型的,如果是,调用方法
//MergedBeanDefinitionPostProcessor 后置处理器是在bean实例换之后调用的
applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName);
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Post-processing of merged bean definition failed", ex);
}
mbd.postProcessed = true;
}
}

// 判断bean 是否为单实例的,如果是单实例的添加到缓存中
// Eagerly cache singletons to be able to resolve circular references
// even when triggered by lifecycle interfaces like BeanFactoryAware.
boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences &&
isSingletonCurrentlyInCreation(beanName));
if (earlySingletonExposure) {
if (logger.isDebugEnabled()) {
logger.debug("Eagerly caching bean '" + beanName +
"' to allow for resolving potential circular references");
}
//添加bean到缓存中
addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean));
}

// Initialize the bean instance.
Object exposedObject = bean;
try {
// 2)、给bean的各种属性赋值
populateBean(beanName, mbd, instanceWrapper);

// 3)、初始化bean
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
}
else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}

if (earlySingletonExposure) {
Object earlySingletonReference = getSingleton(beanName, false);
if (earlySingletonReference != null) {
if (exposedObject == bean) {
exposedObject = earlySingletonReference;
}
else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {
String[] dependentBeans = getDependentBeans(beanName);
Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);
for (String dependentBean : dependentBeans) {
if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {
actualDependentBeans.add(dependentBean);
}
}
if (!actualDependentBeans.isEmpty()) {
throw new BeanCurrentlyInCreationException(beanName,
"Bean with name '" + beanName + "' has been injected into other beans [" +
StringUtils.collectionToCommaDelimitedString(actualDependentBeans) +
"] in its raw version as part of a circular reference, but has eventually been " +
"wrapped. This means that said other beans do not use the final version of the " +
"bean. This is often the result of over-eager type matching - consider using " +
"'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example.");
}
}
}
}

// Register bean as disposable.
try {
registerDisposableBeanIfNecessary(beanName, bean, mbd);
}
catch (BeanDefinitionValidationException ex) {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);
}

return exposedObject;
}

4.3.2 initializeBean初始化bean

  • 1)、invokeAwareMethods():处理Aware接口的方法回调
  • 2)、applyBeanPostProcessorsBeforeInitialization():应用后置处理器的postProcessBeforeInitialization()
  • 3)、invokeInitMethods();执行自定义的初始化方法    
    
  • 4)、applyBeanPostProcessorsAfterInitialization();执行后置处理器的postProcessAfterInitialization()    
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
    AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
    invokeAwareMethods(beanName, bean);
    return null;
    }, getAccessControlContext());
    }
    else {
    // 1)、处理Aware接口的方法回调
    invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
    // 2)、应用后置处理器的postProcessBeforeInitialization()
    wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
    // 3)、执行自定义的初始化方法
    invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
    throw new BeanCreationException(
    (mbd != null ? mbd.getResourceDescription() : null),
    beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
    // 4)、执行后置处理器的postProcessAfterInitialization()
    wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }

    return wrappedBean;
    }

5 finishBeanFactoryInitialization

第四章节主要是介绍创建和注册AnnotationAwareAspectJAutoProxyCreator后置处理器的过程
而finishBeanFactoryInitialization(beanFactory)是完成BeanFactory初始化工作、创建剩余的非懒加载单实例bean,也就是我们的业务bean:MathCalculator的创建过程是在这一步完成的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
/**
* Finish the initialization of this context's bean factory,
* initializing all remaining singleton beans.
*/
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
// 类型装换器
// Initialize conversion service for this context.
if (beanFactory.containsBean(CONVERSION_SERVICE_BEAN_NAME) &&
beanFactory.isTypeMatch(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class)) {
beanFactory.setConversionService(
beanFactory.getBean(CONVERSION_SERVICE_BEAN_NAME, ConversionService.class));
}

// Register a default embedded value resolver if no bean post-processor
// (such as a PropertyPlaceholderConfigurer bean) registered any before:
// at this point, primarily for resolution in annotation attribute values.
if (!beanFactory.hasEmbeddedValueResolver()) {
beanFactory.addEmbeddedValueResolver(strVal -> getEnvironment().resolvePlaceholders(strVal));
}

// Initialize LoadTimeWeaverAware beans early to allow for registering their transformers early.
String[] weaverAwareNames = beanFactory.getBeanNamesForType(LoadTimeWeaverAware.class, false, false);
for (String weaverAwareName : weaverAwareNames) {
getBean(weaverAwareName);
}

// Stop using the temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(null);

// Allow for caching all bean definition metadata, not expecting further changes.
beanFactory.freezeConfiguration();

// Instantiate all remaining (non-lazy-init) singletons.
beanFactory.preInstantiateSingletons();
}

beanFactory.preInstantiateSingletons()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Override
public void preInstantiateSingletons() throws BeansException {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Pre-instantiating singletons in " + this);
}

// Iterate over a copy to allow for init methods which in turn register new bean definitions.
// While this may not be part of the regular factory bootstrap, it does otherwise work fine.
List<String> beanNames = new ArrayList<>(this.beanDefinitionNames);

// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
Object bean = getBean(FACTORY_BEAN_PREFIX + beanName);
if (bean instanceof FactoryBean) {
final FactoryBean<?> factory = (FactoryBean<?>) bean;
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged((PrivilegedAction<Boolean>)
((SmartFactoryBean<?>) factory)::isEagerInit,
getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
}
else {
// 调用父类AbstractBeanFactory的doGetBean方法
getBean(beanName);
}
}
}

// Trigger post-initialization callback for all applicable beans...
for (String beanName : beanNames) {
Object singletonInstance = getSingleton(beanName);
if (singletonInstance instanceof SmartInitializingSingleton) {
final SmartInitializingSingleton smartSingleton = (SmartInitializingSingleton) singletonInstance;
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
smartSingleton.afterSingletonsInstantiated();
return null;
}, getAccessControlContext());
}
else {
smartSingleton.afterSingletonsInstantiated();
}
}
}
}

在这个方法里面关注最后一个方法,beanFactory.preInstantiateSingletons()的作用就是创建剩余的非懒加载单实例bean,如下图所示:


我们的业务calculator,LogAspects就是在这里创建的,会调用父类的AbstractBeanFactory的doGetBean方法,执行流程和章节4.2的getBean方法是一个流程

但是创建calculator,LogAspects和创建AnnotationAwareAspectJAutoProxyCreator是有点不同的,在4.2.2章节,我们有提到,创建bean时,resolveBeforeInstantiation(beanName, mbdToUse)方法会尝试返回它的代理对象返回,否则执行doCreateBean创建bean对象,因为AnnotationAwareAspectJAutoProxyCreator已经在第4章节的registerBeanPostProcessors方法中所创建,所以在每一个bean创建之前,都会调用AnnotationAwareAspectJAutoProxyCreator的postProcessBeforeInstantiation()方法和postProcessAfterInitialization方法

5.1 postProcessBeforeInstantiation()

AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator的postProcessBeforeInstantiation方法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
Object cacheKey = getCacheKey(beanClass, beanName);

if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
// 1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean)
if (this.advisedBeans.containsKey(cacheKey)) {
return null;
}
// 2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean, 或者是否是切面(@Aspect)
// 3)、是否需要跳过
if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {
this.advisedBeans.put(cacheKey, Boolean.FALSE);
return null;
}
}

// Create proxy here if we have a custom TargetSource.
// Suppresses unnecessary default instantiation of the target bean:
// The TargetSource will handle target instances in a custom fashion.
TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
if (targetSource != null) {
if (StringUtils.hasLength(beanName)) {
this.targetSourcedBeans.add(beanName);
}
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
this.proxyTypes.put(cacheKey, proxy.getClass());
return proxy;
}

return null;
}


这个方法以MathCalculator和LogAspect的创建为例子:

  • 1)、判断当前bean是否在advisedBeans中(保存了所有需要增强bean),第一次创建肯定是没有的
  • 2)、判断当前bean是否是基础类型的Advice、Pointcut、Advisor、AopInfrastructureBean, 			或者是否是切面(@Aspect)
    

  • 3)、是否需要跳过
    
    • 1)、获取候选的增强器(切面里面的通知方法)【List<Advisor> candidateAdvisors】
             每一个封装的通知方法的增强器是 InstantiationModelAwarePointcutAdvisor;
             判断每一个增强器是否是 AspectJPointcutAdvisor 类型的;返回true
      
    • 2)、永远返回false
      

5.2 postProcessAfterInitialization()

AnnotationAwareAspectJAutoProxyCreator的父类AbstractAutoProxyCreator的postProcessAfterInitialization方法是在
章节4.3.2 initializeBean初始化bean的applyBeanPostProcessorsAfterInitialization调用的

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
/**
* Initialize the given bean instance, applying factory callbacks
* as well as init methods and bean post processors.
* <p>Called from {@link #createBean} for traditionally defined beans,
* and from {@link #initializeBean} for existing bean instances.
* @param beanName the bean name in the factory (for debugging purposes)
* @param bean the new bean instance we may need to initialize
* @param mbd the bean definition that the bean was created with
* (can also be {@code null}, if given an existing bean instance)
* @return the initialized bean instance (potentially wrapped)
* @see BeanNameAware
* @see BeanClassLoaderAware
* @see BeanFactoryAware
* @see #applyBeanPostProcessorsBeforeInitialization
* @see #invokeInitMethods
* @see #applyBeanPostProcessorsAfterInitialization
*/
protected Object initializeBean(final String beanName, final Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareMethods(beanName, bean);
return null;
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}

Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
// 注意:这里执行的postProcessBeforeInstantiation并不是5.1章节所执行的bean,他直接返回了传过去的wrappedBean
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}

try {
invokeInitMethods(beanName, wrappedBean, mbd);
}
catch (Throwable ex) {
throw new BeanCreationException(
(mbd != null ? mbd.getResourceDescription() : null),
beanName, "Invocation of init method failed", ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}

return wrappedBean;
}

postProcessAfterInitialization()代码如下:

可以看出,返回了一个wrapIfNecessary(bean, beanName, cacheKey);//如果需要包装
wrapIfNecessary(bean, beanName, cacheKey);代码如下

前面的三个if在5.1已经有过介绍,主要看Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null),获取当前bean的所有增强器(通知方法),这里的当前bean也就是MathCalculator,这个方法做了如下几个步骤

  • 1、找到候选的所有的增强器(找哪些通知方法是需要切入当前bean方法的)
    
  • 2、获取到能在bean使用的增强器。
    
  • 3、给增强器排序
    
  • 4、保存当前bean在advisedBeans中;
    

如果当前bean需要增强,会创建当前bean的代理对象:Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {

    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
        AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }

    ProxyFactory proxyFactory = new ProxyFactory();
    proxyFactory.copyFrom(this);

    if (!proxyFactory.isProxyTargetClass()) {
        if (shouldProxyTargetClass(beanClass, beanName)) {
            proxyFactory.setProxyTargetClass(true);
        }
        else {
            evaluateProxyInterfaces(beanClass, proxyFactory);
        }
    }
    // 1)、获取所有增强器(通知方法)
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    2)、保存到proxyFactory
    proxyFactory.addAdvisors(advisors);
    proxyFactory.setTargetSource(targetSource);
    customizeProxyFactory(proxyFactory);

    proxyFactory.setFrozen(this.freezeProxy);
    if (advisorsPreFiltered()) {
        proxyFactory.setPreFiltered(true);
    }
    3)、创建代理对象:Spring自动决定
    return proxyFactory.getProxy(getProxyClassLoader());
}
  • 1)、获取所有增强器(通知方法)
    
  • 2)、保存到proxyFactory
    
  • 3)、创建代理对象:Spring自动决定
    

更多精彩内容:mrxccc

JUC-各种线程池汇总

1 准备知识

介绍线程池之前先简要了解一下Executor,ExecutorService,Future,Callable,Executors是什么,和线程池又有什么关系

1.1 Executor

它是线程池顶级接口。它定义了一个方法**void execute(Runnable)**。

这个方法是用于处理任务的一个服务方法,调用者提供Runnable接口的实现,线程池通过线程执行这个Runnable,该服务方法是无返回值的

1.2 ExecutorService


ExecutorService是Executor接口的子接口,它提供了一个新的服务方法submit,是有返回值的,返回值类型为Future类型(关于Future见1.3),它提供返回值主要是由Callable的call方法提供返回值(Callable见1.4) ,所有的线程池类型都实现这个接口

1.3 Future

顾名思义,Future->未来,代表线程任务执行结束后的结果。
获取线程执行结果的方式是通过get方法获取的,get有两种方式,有参和无参

无参T get()->阻塞等待线程执行结束,并得到结果。
有参T get(long, TimeUnit)->阻塞固定时长,等待线程执行结束后的结果,如果在阻塞时长范围内,线程未执行结束,抛出异常。

1.4 Callable

Callable类似Runnable接口,它有一个call方法,它的作用和Runnable中的run方法完全一致,但也有区别
Callable的call->有返回值,可以抛出任意异常
Runnable的run-> 无返回值,不能抛出未检查的异常

call方法的返回值就是Future中get方法的返回值

1.5 Executors

Executors是一个工具类,类似Collection和Collections的关系,可以更简单的创建若干种线程池,通过Executors可以直接得到想要的线程池

2 线程池

线程池可以自动创建也可以手动创建,自动创建体现在Executors工具类中,常见的可以创建newFixedThreadPool、newCachedThreadPool、newSingleThreadExecutor、newScheduledThreadPool;

手动创建体现在可以灵活设置线程池的各个参数,体现在代码中即ThreadPoolExecutor类构造器上各个实参的不同:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static ExecutorService newFixedThreadPool(int var0) {
return new ThreadPoolExecutor(var0, var0, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue());
}

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

public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, 2147483647, 60L, TimeUnit.SECONDS, new SynchronousQueue());
}

public static ScheduledExecutorService newScheduledThreadPool(int var0) {
return new ScheduledThreadPoolExecutor(var0);
}

1
2
3
4
5
6
7
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue,
ThreadFactory threadFactory,
RejectedExecutionHandler handler) {……}

线程池中的七大参数如下:

(1)corePoolSize:线程池中的常驻核心线程数。

(2)maximumPoolSize:线程池能够容纳同时执行的最大线程数,此值大于等于1。

(3)keepAliveTime:多余的空闲线程存活时间,当空间时间达到keepAliveTime值时,多余的线程会被销毁直到只剩下corePoolSize个线程为止。

(4)unit:keepAliveTime的单位。

(5)workQueue:任务队列,被提交但尚未被执行的任务。

(6)threadFactory:表示生成线程池中工作线程的线程工厂,用户创建新线程,一般用默认即可。

(7)handler:拒绝策略,表示当线程队列满了并且工作线程大于等于线程池的最大显示数(maxnumPoolSize)时如何来拒绝请求执行的runnable的策略。

流程分析

  • 线程池中线程数小于corePoolSize时,新任务将创建一个新线程执行任务,不论此时线程池中存在空闲线程;
  • 线程池中线程数达到corePoolSize时,新任务将被放入workQueue中,等待线程池中任务调度执行;
  • 当workQueue已满,且maximumPoolSize>corePoolSize时,新任务会创建新线程执行任务;
  • 当workQueue已满,且提交任务数超过maximumPoolSize,任务由RejectedExecutionHandler处理;
  • 当线程池中线程数超过corePoolSize,且超过这部分的空闲时间达到keepAliveTime时,回收该线程;
  • 如果设置allowCoreThreadTimeOut(true)时,线程池中corePoolSize范围内的线程空闲时间达到keepAliveTime也将回收;

一:corePoolSize 详细描述

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

二:最大线程数(maximumPoolSize):该参数定义了一个线程池中最多能容纳多少个线程。当一个任务提交到线程池中时,如果线程数量达到了核心线程数,并且任务队列已满,不能再向任务队列中添加任务时,这时会检查任务是否达到了最大线程数,如果未达到,则创建新线程,执行任务,否则,执行拒绝策略。可以通过源码来看一下。如下:可以看出,当调用submit(Runnable task)方法,将任务提交到线程池中时,会调用execute()方法去执行任务,在该方法内,会进行核心线程数,任务队列的判断,最后决定是执行或者是拒绝。总结起来就是:最大线程数参数,是在已经达到核心线程池参数,并且任务队列已经满的情况下,才去判断该参数。

三:keepAliveTime 详细描述

只有当线程池中的线程数大于corePoolSize时keepAliveTime才会起作用,直到线程中的线程数不大于corepoolSIze。

四:系统默认的拒绝策略有以下几种:

    • AbortPolicy:为线程池默认的拒绝策略,该策略直接抛异常处理。
    • DiscardPolicy:直接抛弃不处理。
    • DiscardOldestPolicy:丢弃队列中最老的任务。
    • CallerRunsPolicy:将任务分配给当前执行execute方法线程来处理。

线程池状态: Running, ShuttingDown, Termitnaed

  • Running - 线程池正在执行中。活动状态。
  • ShuttingDown - 线程池正在关闭过程中。优雅关闭。一旦进入这个状态,线程池不再接收新的任务,处理所有已接收的任务,处理完毕后,关闭线程池。
  • Terminated - 线程池已经关闭。

2.1 固定容量线程池FixedThreadPool

FixedThreadPool是固定容量线程池,创建线程池的时候容量固定,使用的是BlockingQueue作为任务的载体,线程池默认的容量上限是Integer.MAX_VALUE

  • 特点:当任务数量大于线程池容量的时候,没有运行的任务保存在任务队列中,当线程有空闲的,自动从队列中取出任务执行
  • 使用场景: 大多数情况下,使用的线程池,首选推荐FixedThreadPool。OS系统和硬件是有线程支持上限。不能随意的无限制提供线程池。

下面是一个无返回值的小案例:
案例中创建了一个线程池,容量为5,执行6个任务,分析调用shutdown方法后,分析任务的执行情况

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 线程池
* 固定容量线程池
*/
package com.bernardlowe.concurrent.t08;

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

public class Test_02_FixedThreadPool {

public static void main(String[] args) {
ExecutorService service =
Executors.newFixedThreadPool(5);
for(int i = 0; i < 6; i++){
service.execute(new Runnable() {
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - test executor");
}
});
}

System.out.println("初始状态:" + service);

System.out.println("开始调用shutdown方法=====");
service.shutdown();
// 是否已经结束, 相当于回收了资源。
System.out.println("是否terminated:" + service.isTerminated());
// 是否已经关闭, 是否调用过shutdown方法
System.out.println("是否shutdown:" + service.isShutdown());
System.out.println("shutdown后的状态:" + service);

try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}

// service.shutdown();
System.out.println("2秒过后任务全部执行完====");
System.out.println("是否terminated:" + service.isTerminated());
System.out.println("是否shutdown:" + service.isShutdown());
System.out.println("任务全部执行完过后状态:" + service);
}

}


结果:

从图中可以分析出以下几个过程
在初始状态:五个执行线程,1个任务在等待队列,0个完成任务

调用shutdown方法后:线程池未关闭(terminated为false),调用了shutdown(不再接收新任务),0个完成任务

两秒后任务执行完毕:线程池已关闭(terminated为true),调用了shutdown(不再接收新任务),6个完成任务

下面是一个有返回值的小案例:
案例中创建了一个线程池,容量为1,submit方法传了一个Callable,future通过get获取线程的返回值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 线程池
* 固定容量线程池(有返回值)
*/
package com.bernardlowe.concurrent.t08;

import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;
import java.util.concurrent.TimeUnit;

public class Test_03_Future {

public static void main(String[] args) throws InterruptedException, ExecutionException {

ExecutorService service = Executors.newFixedThreadPool(1);

Future<String> future = service.submit(new Callable<String>() {
@Override
public String call() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
return Thread.currentThread().getName() + " - test executor";
}
});
System.out.println("线程是否结束: " + future.isDone()); // 查看线程是否结束, 任务是否完成。 call方法是否执行结束

System.out.println("call方法的返回值: " + future.get()); // 获取call方法的返回值。
System.out.println("线程是否结束: " + future.isDone());

// 关闭线程池
service.shutdown();
}

}

结果:

2.2 CachedThreadPool

缓存的线程池, 容量不限(Integer.MAX_VALUE),自动扩容
容量管理策略:如果线程池中的线程数量不满足任务执行,创建新的线程。每次有新任务无法即时处理的时候,都会创建新的线程。当线程池中的线程空闲时长达到一定的临界值(默认60秒),自动释放线程,这里通过Executors.newCachedThreadPool()方法得到的线程池无法修改空闲时间,具体原因见下图,但可以通过自定义线程池ThreadPoolExecutor修改,具体方法见2.5,这里就不解释了


应用场景: 内部应用或测试应用。

  • 内部应用,有条件的内部数据瞬间处理时应用,如:电信平台夜间执行数据整理(有把握在短时间内处理完所有工作,且对硬件和软件有足够的信心)。
  • 测试应用,在测试的时候,尝试得到硬件或软件的最高负载量,用于提供FixedThreadPool容量的指导

案例演示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
/**
* 线程池
* 无容量限制的线程池(最大容量默认为Integer.MAX_VALUE)
*/
package com.bernardlowe.concurrent.t08;

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

public class Test_05_CachedThreadPool {

public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();

System.out.println(service);

for(int i = 0; i < 5; i++){
service.execute(new Runnable() {
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - test executor");
}
});
}

System.out.println(service);

try {
TimeUnit.SECONDS.sleep(65);
} catch (InterruptedException e) {
e.printStackTrace();
}

System.out.println(service);
}

}

2.3 计划任务线程池ScheduledThreadPool

ScheduledThreadPool是计划任务线程池,可以根据计划自动执行任务的线程池,底层实现是一个DelayedWorkQueue,它的一个主要方法scheduleAtFixedRate

有以下几个参数:

  • command - 要执行的任务
  • initialDelay - 第一次任务执行的间隔。
  • period - 多次任务执行的间隔。
  • unit - 多次任务执行间隔的时间单位。

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
/**
* 线程池
* 计划任务线程池。
*/
package com.bernardlowe.concurrent.t08;

import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

public class Test_07_ScheduledThreadPool {

public static void main(String[] args) {
ScheduledExecutorService service = Executors.newScheduledThreadPool(3);
System.out.println(service);

// 定时完成任务。 scheduleAtFixedRate(Runnable, start_limit, limit, timeunit)
// runnable - 要执行的任务。
service.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName());
}
}, 0, 300, TimeUnit.MILLISECONDS);

}

}

2.4 单一容量的线程池SingleThreadExecutor

单一容量的线程池,用法和FixedThreadPool类似,但和newFixedThreadPool不一样的是newSingleThreadExecutor创建的线程池又被一个FinalizableDelegatedExecutorService包装了一下
总结一下SingleThreadExecutor:

  • 单线任务处理的线程池
  • shutdown方法必然会被调用
  • 不具备ThreadPoolExecutor所有功能的线程池
    具体可以看看这篇文章:https://www.jianshu.com/p/2b7d853322bb

2.5 分支合并线程池ForkJoinPool

分支合并线程池(mapduce类似的设计思想),可以递归完成复杂任务,适合用于处理复杂任务
要求可分支合并的任务必须是ForkJoinTask类型的子类型
ForkJoinTask类型提供了两个抽象子类型:
RecursiveTask有返回结果的分支合并任务
RecursiveAction无返回结果的分支合并任务

案例:
这个案例做了一个以ForkJoinPool实现的数据累加,当计算数字区间大于MAX_SIZE=50000时,开启新的线程任务的计算,最后合并统计结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
/**
* 线程池
* 分支合并线程池。
*/
package com.bernardlowe.concurrent.t08;

import java.io.IOException;
import java.util.Random;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;

public class Test_08_ForkJoinPool {

final static int[] numbers = new int[1000000];
final static int MAX_SIZE = 500000;
final static Random r = new Random();


static{
for(int i = 0; i < numbers.length; i++){
numbers[i] = r.nextInt(1000);
}
}

static class AddTask extends RecursiveTask<Long>{ // RecursiveAction
int begin, end;
public AddTask(int begin, int end){
this.begin = begin;
this.end = end;
}

//
protected Long compute(){
if((end - begin) < MAX_SIZE){
long sum = 0L;
for(int i = begin; i < end; i++){
sum += numbers[i];
}
// System.out.println("form " + begin + " to " + end + " sum is : " + sum);
return sum;
}else{
int middle = begin + (end - begin)/2;
AddTask task1 = new AddTask(begin, middle);
AddTask task2 = new AddTask(middle, end);
task1.fork();// 就是用于开启新的任务的。 就是分支工作的。 就是开启一个新的线程任务。
task2.fork();
// join - 合并。将任务的结果获取。 这是一个阻塞方法。一定会得到结果数据。
return task1.join() + task2.join();
}
}
}

public static void main(String[] args) throws InterruptedException, ExecutionException, IOException {
long result = 0L;
for(int i = 0; i < numbers.length; i++){
result += numbers[i];
}
System.out.println(result);

ForkJoinPool pool = new ForkJoinPool();
AddTask task = new AddTask(0, numbers.length);

Future<Long> future = pool.submit(task);
System.out.println(future.get());

}

}

结果:该任务分类四个线程任务进行计算,最后汇总

2.5 ThreadPoolExecutor

ThreadPoolExecutor线程池的底层实现,除ForkJoinPool外,其他常用线程池底层都是使用ThreadPoolExecutor实现的,其中有一个构造方法如下:

  • corePoolSize:核心容量,创建线程池的时候,默认有多少线程。也是线程池保持的最少线程数
  • maximumPoolSize: 最大容量,线程池最多有多少线程
  • keepAliveTime: 生命周期,0为永久。当线程空闲多久后,自动回收
  • unit: 生命周期单位,为生命周期提供单位,如:秒,毫秒
  • workQueue 任务队列,阻塞队列。注意,泛型必须是Runnable

案例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* 线程池
* 固定容量线程池
*/
package com.bernardlowe.concurrent.t08;

import java.util.ArrayList;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

public class Test_09_ThreadPoolExecutor {

public static void main(String[] args) {
// 模拟fixedThreadPool, 核心线程5个,最大容量5个,线程的生命周期无限。
ExecutorService service =
new ThreadPoolExecutor(5, 5, 0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());

for(int i = 0; i < 6; i++){
service.execute(new Runnable() {
@Override
public void run() {
try {
TimeUnit.MILLISECONDS.sleep(500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - test executor");
}
});
}

System.out.println(service);

service.shutdown();
System.out.println(service.isTerminated());
System.out.println(service.isShutdown());
System.out.println(service);

try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}

service.shutdown();
System.out.println(service.isTerminated());
System.out.println(service.isShutdown());
System.out.println(service);

}
}

更多精彩内容:mrxccc

spring常用注解

@[toc]

1.常用注解及作用

1.1 @Configuration

声明当前类是一个配置类(相当于一个Spring配置的xml文件)

1.2 @ComponentScan

自动扫描指定包下所有使用@Service,@Component,@Controller,@Repository的类并注册
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@ComponentScan(value="com.atguigu",includeFilters = {
@Filter(type=FilterType.ANNOTATION,classes={Controller.class}),
@Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),
@Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})
},useDefaultFilters = false)
//excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件
//includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件,
需设置useDefaultFilters = false
//FilterType.ANNOTATION:按照注解
//FilterType.ASSIGNABLE_TYPE:按照给定的类型;
//FilterType.ASPECTJ:使用ASPECTJ表达式
//FilterType.REGEX:使用正则指定
//FilterType.CUSTOM:使用自定义规则

1.3 @ComponentScans

可包含多个@ComponentScan
示例:@ComponentScans(value = {@ComponentScan,@ComponentScan,@ComponentScan})

1.4 @Lazy

单实例bean:默认在容器启动的时候创建对象;
懒加载:容器启动不创建对象。第一次使用(获取)Bean创建对象,并初始化;

1.5 @Conditional({Condition})

示例:@Conditional({WindowsCondition.class}),WindowsCondition是实现了Condition接口的类
按照一定的条件进行判断,满足条件给容器中注册bean,可定义在类名或者方法名上

1.6 注入组件

容器中注入组件的方式
1 @Bean:导入第三方的类或包的组件
2.ComponentScan:包扫描+组件的标注注解
3.@Import
4.使用Spring提供的FactoryBean(工厂Bean)进行注册

1.6.1 @Import()快速导入组件

可导入单个组件或者多个组件,id默认为全类名
示例:@Import(value={Dog.class,Cat.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})

1.6.2.使用ImportSelector自定义需要导入的组件

ImportSelector是一个接口,接口定义的方法为一个包含组件全类名的字符串数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
//自定义逻辑返回需要导入的组件
public class MyImportSelector implements ImportSelector {

//返回值,就是到导入到容器中的组件全类名
//AnnotationMetadata:当前标注@Import注解的类的所有注解信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// TODO Auto-generated method stub
//importingClassMetadata
//方法不要返回null值,否则会报空指针异常
return new String[]{"com.bernard.bean.Blue","com.bernard.bean.Yellow"};
}

}

1.6.3.使用ImportBeamDefinitionRegistrar返回自定义组件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
    public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:BeanDefinition注册类;
* 把所有需要添加到容器中的bean;
* 调用BeanDefinitionRegistry.registerBeanDefinition手工注册进来
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

boolean definition = registry.containsBeanDefinition("com.atguigu.bean.Red");
boolean definition2 = registry.containsBeanDefinition("com.atguigu.bean.Blue");
if(definition && definition2){
//指定Bean定义信息;(Bean的类型,Bean。。。)
RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);
//注册一个Bean,指定bean名
registry.registerBeanDefinition("rainBow", beanDefinition);
}
}

}

测试类
打印出所有注册到容器的bean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class MyImportTest {
@Test
public void test01(){
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext(MyImportConfig.class);

String[] beanDefinitionNames = annotationConfigApplicationContext.getBeanDefinitionNames();

for (int i = 0; i < beanDefinitionNames.length; i++) {
String beanDefinitionName = beanDefinitionNames[i];
System.out.println(beanDefinitionName);
}

}
}

1.6.4.使用Spring提供的 FactoryBean(工厂Bean);

1)、默认获取到的是工厂bean调用getObject创建的对象
2)、要获取工厂Bean本身,我们需要给id前面加一个&,如:“&colorFactoryBean”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
//创建一个Spring定义的FactoryBean
public class ColorFactoryBean implements FactoryBean<Color> {

//返回一个Color对象,这个对象会添加到容器中
@Override
public Color getObject() throws Exception {
// TODO Auto-generated method stub
System.out.println("ColorFactoryBean...getObject...");
return new Color();
}

@Override
public Class<?> getObjectType() {
// TODO Auto-generated method stub
return Color.class;
}

//是单例?
//true:这个bean是单实例,在容器中保存一份
//false:多实例,每次获取都会创建一个新的bean;
@Override
public boolean isSingleton() {
// TODO Auto-generated method stub
return false;
}

}

注册示例:

1
2
3
4
@Bean
public ColorFactoryBean colorFactoryBean(){
return new ColorFactoryBean();
}

获取示例

1
2
3
//工厂Bean获取的是调用getObject创建的对象
Object bean2 = applicationContext.getBean("colorFactoryBean");//获取getObject返回的bean
Object bean4 = applicationContext.getBean("&colorFactoryBean");//获取工厂bean本身

1.7 @Value

使用@Value可以为属性字段赋值
1、基本数值
2、可以写SpEL; #{}
3、可以写${};取出配置文件【properties】中的值(在运行环境变量里面的值)

配置类
使用@PropertySource读取外部配置文件中的k/v保存到运行的环境变量中;
加载完外部的配置文件以后使用${}取出配置文件的值

1
2
3
4
5
6
7
8
9
10
@PropertySource(value={"classpath:/person.properties"})
@Configuration
public class MainConfigOfPropertyValues {

@Bean
public Person person(){
return new Person();
}

}

bean

1
2
3
4
5
6
7
8
9
10
public class Person {
@Value("张三")
private String name;

@Value("#{20-2}")
private Integer age;

@Value("${person.nickName}")
private String nickName;
}

1.8 @Autowired、@Resource、@Primary

  • 默认优先按照类型去容器中找对应的组件:applicationContext.getBean(BookDao.class);找到就赋值
  • 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找applicationContext.getBean(“bookDao”)
  • 自动装配默认一定要将属性赋值好,没有就会报错,如果注入容器中不存在的bean,可以使用@Autowired(required=false);
  • @Qualifier(“组件id”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名
  • @Primary:让Spring进行自动装配的时候,默认使用首选的bean(使用该方式,注入组件时就不能使用@Qualifier了);
  • @Inject:需要导入javax.inject的包,和Autowired的功能一样。没有required=false的功能;
  • @Resource可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的;没有能支持@Primary和@Autowired(reqiured=false)功能;

@Autowired:构造器,参数,方法,属性;都是从容器中获取参数组件的值

  • 如果@Autowired标注在方法/或参数位置上,Spring创建当前对象的时候,就会调用该方法完成赋值,方法使用的参数,会从ioc容器中获取,默认不写@Autowired效果是一样的,都能自动装配
  • 如果@Autowired标注在构造器上:如果组件只有一个有参构造器,这个有参构造器的@Autowired可以省略,参数位置的组件还是可以自动从容器中获取
  • 如果@Bean标注的的方法创建对象时,方法参数的值从容器中获取

补充:
@Autowired:Spring定义的;
@Resource、@Inject都是java规范

1.9 @Profile

指定组件在哪个环境的情况下才能被注册到容器中,不指定,任何环境下都能注册这个组件

  • 1)、加了环境标识的bean,只有这个环境被激活的时候才能注册到容器中。默认是default环境
  • 2)、写在配置类上,只有是指定的环境的时候,整个配置类里面的所有配置才能开始生效
  • 3)、没有标注环境标识的bean,在任何环境下都是加载的;
    java配置MainConfigOfProfile.java
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    @PropertySource("classpath:/dbconfig.properties")
    @Configuration
    public class MainConfigOfProfile implements EmbeddedValueResolverAware{

    @Value("${db.user}")
    private String user;

    private StringValueResolver valueResolver;

    private String driverClass;

    @Bean
    public Yellow yellow(){
    return new Yellow();
    }

    @Profile("test")
    @Bean("testDataSource")
    public DataSource dataSourceTest(@Value("${db.password}")String pwd) throws Exception{
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setUser(user);
    dataSource.setPassword(pwd);
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/test");
    dataSource.setDriverClass(driverClass);
    return dataSource;
    }

    @Profile("dev")
    @Bean("devDataSource")
    public DataSource dataSourceDev(@Value("${db.password}")String pwd) throws Exception{
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setUser(user);
    dataSource.setPassword(pwd);
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/ssm_crud");
    dataSource.setDriverClass(driverClass);
    return dataSource;
    }

    @Profile("prod")
    @Bean("prodDataSource")
    public DataSource dataSourceProd(@Value("${db.password}")String pwd) throws Exception{
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
    dataSource.setUser(user);
    dataSource.setPassword(pwd);
    dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/scw_0515");

    dataSource.setDriverClass(driverClass);
    return dataSource;
    }

    @Override
    public void setEmbeddedValueResolver(StringValueResolver resolver) {
    // TODO Auto-generated method stub
    this.valueResolver = resolver;
    driverClass = valueResolver.resolveStringValue("${db.driverClass}");
    }

    }
    激活方式:
  • 1、使用命令行动态参数: 在虚拟机参数位置加载 -Dspring.profiles.active=test
  • 2、java代码的方式激活某种环境,分为四步
    • 1)、创建一个applicationContext
    • 2)、设置需要激活的环境
    • 3)、注册主配置类
    • 4)、启动刷新容器

激活测试代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void test01(){
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext();
//1、创建一个applicationContext
//2、设置需要激活的环境
applicationContext.getEnvironment().setActiveProfiles("dev");
//3、注册主配置类
applicationContext.register(MainConfigOfProfile.class);
//4、启动刷新容器
applicationContext.refresh();


String[] namesForType = applicationContext.getBeanNamesForType(DataSource.class);
for (String string : namesForType) {
System.out.println(string);
}

Yellow bean = applicationContext.getBean(Yellow.class);
System.out.println(bean);
applicationContext.close();
}

1.10 @Scope

在注册bean时,spring默认是单实例的,即scope=”singleton”,除此之外,常见的scope还有prototype、request、session作用域

  • prototype:多实例的:ioc容器启动并不会去调用方法创建对象放在容器中。
  • request:同一次请求创建一个实例
  • session:同一个session创建一个实例

更多精彩内容:mrxccc

ReentrantLock与synchronized对比

1 ReentrantLock与synchronized对比

ReentrantLock与synchronized都是为了同步加锁,但ReentrantLock相对效率比synchronized高,量级较轻。
synchronized在JDK1.5版本开始,尝试优化。到JDK1.7版本后,优化效率已经非常好了。在绝对效率上,不比reentrantLock差多少。使用ReentrantLock,必须手工释放锁标记。一般都是在finally代码块中定义释放锁标记的unlock方法。

2.示例用法

2.1 基本用法

lock()与unlock()就像synchronized同步代码块的开始与结束,使用ReentrantLocky一定要记得unlock()解锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
package com.bernardlowe.concurrent.t03;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test_01 {
Lock lock = new ReentrantLock();

void m1(){
try{
lock.lock(); // 加锁
for(int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock(); // 解锁
}
}

void m2(){
lock.lock();
System.out.println("m2() method");
lock.unlock();
}

public static void main(String[] args) {
final Test_01 t = new Test_01();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}

2.2 尝试锁

尝试锁,顾名思义是尝试获取锁标记trylock(),有两种方式

  • 无参尝试锁:会根据是否能获取当前锁标记返回对应值

    boolean tryLock();

  • 有参阻塞尝试锁, 阻塞尝试锁,阻塞参数代表等待超时时间。

    boolean tryLock(long time, TimeUnit unit) throws InterruptedException;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
/**
* 尝试锁
*/
package com.bernardlowe.concurrent.t03;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test_02 {
Lock lock = new ReentrantLock();

void m1(){
try{
lock.lock();
for(int i = 0; i < 10; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}

void m2(){
boolean isLocked = false;
try{
// 尝试锁, 如果有锁,无法获取锁标记,返回false。
// 如果获取锁标记,返回true
// isLocked = lock.tryLock();

// 阻塞尝试锁,阻塞参数代表的时长,尝试获取锁标记。
// 如果超时,不等待。直接返回。
isLocked = lock.tryLock(5, TimeUnit.SECONDS);

if(isLocked){
System.out.println("m2() method synchronized");
}else{
System.out.println("m2() method unsynchronized");
}
}catch(Exception e){
e.printStackTrace();
}finally{
if(isLocked){
// 尝试锁在解除锁标记的时候,一定要判断是否获取到锁标记。
// 如果当前线程没有获取到锁标记,会抛出异常。
lock.unlock();
}
}
}

public static void main(String[] args) {
final Test_02 t = new Test_02();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}
}

2.3 可打断

先解释下线程的几种状态:
阻塞状态: 包括普通阻塞,等待队列,锁池队列。
普通阻塞: sleep(10000), 可以被打断。调用thread.interrupt()方法,可以打断阻塞状态,抛出异常。
等待队列: wait()方法被调用,也是一种阻塞状态,只能由notify唤醒。无法打断
锁池队列: 无法获取锁标记。不是所有的锁池队列都可被打断

  • 使用ReentrantLock的lock方法,获取锁标记的时候,如果需要阻塞等待锁标记,无法被打断。
  • 使用ReentrantLock的lockInterruptibly方法,获取锁标记的时候,如果需要阻塞等待,可以被打断。

示例代码
主线程启动了两个线程t1,t2,t1调用m1(),t2调用m2()
当主线程调用interrupt()方法,可以打断t2线程的阻塞等待锁,抛出异常

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
package com.bernardlowe.concurrent.t03;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class Test_03 {
Lock lock = new ReentrantLock();

void m1(){
try{
lock.lock();
for(int i = 0; i < 5; i++){
TimeUnit.SECONDS.sleep(1);
System.out.println("m1() method " + i);
}
}catch(InterruptedException e){
e.printStackTrace();
}finally{
lock.unlock();
}
}

void m2(){
try{
lock.lockInterruptibly(); // 可尝试打断,阻塞等待锁。可以被其他的线程打断阻塞状态
System.out.println("m2() method");
}catch(InterruptedException e){
System.out.println("m2() method interrupted");
}finally{
try{
lock.unlock();
}catch(Exception e){
e.printStackTrace();
}
}
}

public static void main(String[] args) {
final Test_03 t = new Test_03();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
});
t2.start();
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
t2.interrupt();// 打断线程休眠。非正常结束阻塞状态的线程,都会抛出异常。
}
}

结果如图

2.4 公平锁

操作系统cpu,为了保证效率,线程的执行机制是竞争机制,或者说是随机机制,是不公平的,使用ReentrantLock实现公平锁,是非常简单的,只需要在创建ReentrantLock的时候传一个参数ReentrantLock lock = new ReentrantLock(true);
示例代码:
TestReentrantlock是公平锁
TestSync是非公平锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
/**
* 公平锁
*/
package com.bernardlowe.concurrent.t03;

import java.util.concurrent.locks.ReentrantLock;

public class Test_04 {

public static void main(String[] args) {
TestReentrantlock t = new TestReentrantlock();
//TestSync t = new TestSync();
Thread t1 = new Thread(t);
Thread t2 = new Thread(t);
t1.start();
t2.start();
}
}

class TestReentrantlock extends Thread{
// 定义一个公平锁
private static ReentrantLock lock = new ReentrantLock(true);
public void run(){
for(int i = 0; i < 5; i++){
lock.lock();
try{
System.out.println(Thread.currentThread().getName() + " get lock");
}finally{
lock.unlock();
}
}
}

}

class TestSync extends Thread{
public void run(){
for(int i = 0; i < 5; i++){
synchronized (this) {
System.out.println(Thread.currentThread().getName() + " get lock in TestSync");
}
}
}
}

公平锁结果:

非公平锁结果:

更多精彩内容:mrxccc

JAVA案例分析内存可见性与原子性操作

1 案例之变量内存可见性

代码解析:新起一个子线程执行m()方法,1秒后主线程将b置为false,子线程是否会停止执行死循环while(b){},打印“end”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_09 {

boolean b = true;

void m(){
System.out.println("start");
while(b){}
System.out.println("end");
}

public static void main(String[] args) {
final Test_09 t = new Test_09();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}).start();

try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

t.b = false;
}

}

**结果:1秒钟过后并不会停止执行死循环while(b){},打印“end” **
这时候,如果将boolean b = true;这段代码前加一个volatile关键字
volatile boolean b = true;,就会达到预想中的效果


思考:为什么加上这个关键字,其他线程就会读取到已经改变的变量的值了?

是因为在CPU计算过程中,会将计算过程需要的数据加载到CPU计算缓存中,当CPU计算中断时,有可能刷新缓存,重新读取内存中的数据。在线程运行的过程中,如果某变量被其他线程修改,可能造成数据不一致的情况,从而导致结果错误。
而volatile修饰的变量是线程可见的,当JVM解释volatile修饰的变量时,会通知CPU,在计算过程中,每次使用变量参与计算时,都会检查内存中的数据是否发生变化,而不是一直使用CPU缓存中的数据,可以保证计算结果的正确。

但是这样还有一个问题,volatile只能保证可见性,不能保证原子性

2 案例之变量的原子性

下面再看一个示例:
预期结果:起10个线程,每个线程都对count增加10000,预期结果为count=100000

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package com.bernardlowe.concurrent.t01;

import java.util.ArrayList;
import java.util.List;

public class Test_10 {

volatile int count = 0;
/*synchronized*/ void m(){
for(int i = 0; i < 10000; i++){
count++;
}
}

public static void main(String[] args) {
final Test_10 t = new Test_10();
List<Thread> threads = new ArrayList<>();
for(int i = 0; i < 10; i++){
threads.add(new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}));
}
for(Thread thread : threads){
thread.start();
}
for(Thread thread : threads){
try {
thread.join();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
System.out.println(t.count);
}
}

但结果并不是


原因是volatile只是通知底层计算时,CPU检查内存数据,而不是让一个变量在多个线程中同步。

这时候可以给m()方法增加一个synchronized关键字,可以达到预期的效果,即synchronized void m()

还有另一种方法可以保证原子性,在上面代码将count声明为AtomicInteger原子操作,结果仍然是100000

1
2
3
4
5
6
7
// 其中的每个方法都是原子操作。可以保证线程安全。
AtomicInteger count = new AtomicInteger(0);
void m(){
for(int i = 0; i < 10000; i++){
count.incrementAndGet();
}
}

这里不仅仅可声明Integer类型,java.util.concurrent.atomic包里面还有其他类型的

更多精彩内容:mrxccc

synchronized关键字

1.为什么要使用synchronized?

在并发编程中存在线程安全问题,主要原因有:
1.存在共享数据
2.多线程共同操作共享数据。关键字synchronized可以保证在同一时刻,只有一个线程可以执行某个方法或某个代码块,同时synchronized可以保证一个线程的变化可见(可见性)

2.synchronized锁什么,加锁的目的是什么?

synchronized锁的是对象,锁的的对象可能是this、临界资源对象、class类对象
加锁的目的是保证操作的原子性

3.代码示例

3.1锁this和临界资源对象

本例中:

  • testSync1是synchronized代码块,锁的是object,临界资源对象
  • testSync2和testSync3都是锁的是当前对象this
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    /**
    * synchronized关键字
    * 锁对象。synchronized(this)和synchronized方法都是锁当前对象。
    */
    package com.bernardlowe.concurrent.t01;

    import java.util.concurrent.TimeUnit;

    public class Test_01 {
    private int count = 0;
    private Object object = new Object();

    // 1.synchronized代码块,锁的是object,临界资源对象
    public void testSync1(){
    synchronized(object){
    System.out.println(Thread.currentThread().getName()
    + " count = " + count++);
    }
    }

    // 2.synchronized代码块,锁的是当前对象this
    public void testSync2(){
    synchronized(this){
    System.out.println(Thread.currentThread().getName()
    + " count = " + count++);
    }
    }

    // 2.synchronized代码,锁的也是this
    public synchronized void testSync3(){
    System.out.println(Thread.currentThread().getName()
    + " count = " + count++);
    try {
    TimeUnit.SECONDS.sleep(3);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }

    public static void main(String[] args) {
    final Test_01 t = new Test_01();
    new Thread(new Runnable() {
    @Override
    public void run() {
    t.testSync3();
    }
    }).start();
    new Thread(new Runnable() {
    @Override
    public void run() {
    t.testSync3();
    }
    }).start();
    }

    }

3.2锁class类对象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
/**
* synchronized关键字
* 同步方法 - static
* 静态同步方法,锁的是当前类型的类对象。在本代码中就是Test_02.class
*/
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_02 {
private static int staticCount = 0;

public static synchronized void testSync4(){
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}

public static void testSync5(){
synchronized(Test_02.class){
System.out.println(Thread.currentThread().getName()
+ " staticCount = " + staticCount++);
}
}

}

3.3 什么时候锁临界资源,什么时候锁当前对象?

如果在加锁的时候需要对当前对象的访问限定,建议锁临界资源(即锁一个临界资源),如果对当前锁级别比较高的话,就锁当前对象。建议都以同步代码块的方式进行开发,这样可以避免锁的范围太高

同步方法和非同步方法
提问:同步方法是否影响其他线程调用非同步方法,或调用其他锁资源的同步方法?

代码示例
m1是非同步方法,m2是同步方法,m3同步代码块,锁的临界资源,这段代码的目的是为了证明在调用同步方法m1时,m2,m3是否能够执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
/**
* synchronized关键字
* 同步方法 - 同步方法和非同步方法的调用
* 同步方法只影响锁定同一个锁对象的同步方法。不影响其他线程调用非同步方法,或调用其他锁资源的同步方法。
*/
package com.bernardlowe.concurrent.t01;

public class Test_04 {
Object o = new Object();
public synchronized void m1(){ // 重量级的访问操作。
System.out.println("public synchronized void m1() start");
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public synchronized void m1() end");
}

public void m3(){
synchronized(o){
System.out.println("public void m3() start");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public void m3() end");
}
}

public void m2(){
System.out.println("public void m2() start");
try {
Thread.sleep(1500);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("public void m2() end");
}

public static class MyThread01 implements Runnable{
public MyThread01(int i, Test_04 t){
this.i = i;
this.t = t;
}
int i ;
Test_04 t;
public void run(){
if(i == 0){
t.m1();
}else if (i > 0){
t.m2();
}else {
t.m3();
}
}
}

public static void main(String[] args) {
Test_04 t = new Test_04();
new Thread(new MyThread01(0, t)).start();
new Thread(new MyThread01(1, t)).start();
new Thread(new MyThread01(-1, t)).start();
}

}

执行结果

可以看出,在执行同步方法m1时,m2,m3不受影响,依然可以执行
结论:同步方法只影响锁定同一个锁对象的同步方法。不影响其他线程调用非同步方法,或调用其他锁资源的同步方法。
但有一点需要注意,尽量在商业开发中避免同步方法。使用同步代码块。 细粒度解决同步问题。

3.3 重入锁

这里重入锁分为两类:

  • 1.在同步方法里面调用其他同步方法
  • 2.子类同步方法覆盖父类同步方法

下面来看第一种:在同步方法里面调用其他同步方法
思考:调用m1()方法,m2()方法是否会执行?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
*synchronized关键字
*同步方法 - 调用其他同步方法
*/
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_06 {

synchronized void m1(){ // 锁this
System.out.println("m1 start");
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
m2();
System.out.println("m1 end");
}
synchronized void m2(){ // 锁this
System.out.println("m2 start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("m2 end");
}

public static void main(String[] args) {

new Test_06().m1();

}

}


结果:在m1()方法调用时,m2()方法依然可执行。多次调用同步代码,锁定同一个锁对象,是可重入的

第二种情况:子类同步方法覆盖父类同步方法
思考:子类同步方法m()中,调用父类同步方法m(),是否可重入?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
/**
* synchronized关键字
* 同步方法 - 继承
* 子类同步方法覆盖父类同步方法。可以指定调用父类的同步方法。
* 相当于锁的重入。
*/
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_07 {

synchronized void m(){
System.out.println("Super Class m start");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Super Class m end");
}

public static void main(String[] args) {
new Sub_Test_07().m();
}

}

class Sub_Test_07 extends Test_07{
synchronized void m(){
System.out.println("Sub Class m start");
super.m();
System.out.println("Sub Class m end");
}
}


结果:子类同步方法m()中,调用了父类同步方法m(),可以重入

3.4 锁与异常

思考:当同步方法或同步代码块中发生异常,是否会影响其他线程的执行?
下面来看一段代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
/**
* synchronized关键字
* 同步方法 - 锁与异常
*/
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_08 {
int i = 0;
synchronized void m(){
System.out.println(Thread.currentThread().getName() + " - start");
while(true){
i++;
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
// 当i==5时会抛出异常
if(i == 5){
i = 1/0;
}
}
}

public static void main(String[] args) {
final Test_08 t = new Test_08();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t1").start();

new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "t2").start();
}

}

这段代码中先运行了两个线程t1、t2,当其中一个线程发生异常时,另外一个线程是否能继续执行

结果:当同步方法中发生异常的时候,自动释放锁资源。不会影响其他线程的执行。

思考: 同步业务逻辑中,如果发生异常如何处理?
比如上面会发生异常的代码中,可以这样

1
2
3
4
5
6
7
if(i == 5){
try {
i = 1/0;
} catch (Exception e) {
i = 0;
}
}

3.5 锁对象变更问题

代码示例:8
思考:当一个线程执行同步方法时,另一个线程修改了锁对象,是否还能执行同步代码块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
/**
* synchronized关键字
* 锁对象变更问题
*/
package com.bernardlowe.concurrent.t01;

import java.util.concurrent.TimeUnit;

public class Test_13 {
Object o = new Object();

void m(){
System.out.println(Thread.currentThread().getName() + " start");
synchronized (o) {
while(true){
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " - " + o);
}
}
}

public static void main(String[] args) {
final Test_13 t = new Test_13();
new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "thread1").start();
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
t.m();
}
}, "thread2");
t.o = new Object();
thread2.start();
}

}


结论:可以看出,其他线程依然可以执行同步方法
因为同步代码一旦加锁后,那么会有一个临时的锁引用执行锁对象,和真实的引用无直接关联。在锁未释放之前,修改锁对象引用,不会影响同步代码的执行。

注意:不要使用静态常量作为锁对象
如下代码,因为String常量池的问题,s1,s1是同一个对象,所以m1,m2方法锁的是也同一个对象,m1同步方法被执行后,m2方法不会被执行

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
/**
* synchronized关键字
* 常量问题
*/
package com.bernardlowe.concurrent.t01;

public class Test_14 {
String s1 = "hello";
String s2 = "hello";
// String s2 = new String("hello"); // new关键字,一定是在堆中创建一个新的对象。
void m1(){
synchronized (s1) {
System.out.println("m1()");
while(true){

}
}
}

void m2(){
synchronized (s2) {
System.out.println("m2()");
while(true){

}
}
}

public static void main(String[] args) {
final Test_14 t = new Test_14();
new Thread(new Runnable() {
@Override
public void run() {
t.m1();
}
}).start();

new Thread(new Runnable() {
@Override
public void run() {
t.m2();
}
}).start();
}

}

更多精彩内容:mrxccc