前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java Web技术经验总结(六)

Java Web技术经验总结(六)

作者头像
阿杜
发布2018-08-06 11:39:34
2970
发布2018-08-06 11:39:34
举报
文章被收录于专栏:阿杜的世界阿杜的世界
  1. synchronized的作用和原理:link
  • 使用经验:synchronized是一种互斥锁。在Java开发中,当某个变量需要在多个线程之间共享时,需要分析具体的场景:如果多个线程对该共享变量的读和写之间没有竞争关系,则可以考虑使用concurrent包下提供的并发数据结构,例如ConcurrentHashMap;但是,如果多个线程对共享变量之间的读和写动作之间有竞态关系,则需要将整个变量锁住。
  • 作用:(1)确保多线程之间互斥访问共享变量;(2)确保共享变量的修改能够及时可见;(3)有效解决重排序问题。
  • 原理:synchronized是Java的内置锁。JVM通过monitorentermonitorexit指令实现内置锁。
    • 每个对象都有一个监视器锁(monitor),当monitor被占用时,该对象就处于锁定状态,其他试图访问该对象的线程将阻塞;
    • 对于同一个线程来说,monitor是可重入的,重入的时候会将“占用数”+1;
    • 当一个线程试图访问某个变量时,如果发现该变量的monitor占用数为0,则可以占用该对象;如果>=1,则进入阻塞。
    • 执行monitorexit的线程必须是某个对象的monitor的所有者,当执行完该指令之后,如果占用数为0,则当前线程释放该monitor。
  1. volatile的原理:link
  • 当我们声明共享变量为volatile后,对这个变量的读/写将会很特别。理解volatile特性的一个好方法是:把对volatile变量的单个读/写,看成是使用同一个监视器锁对这些单个读/写操作做了同步。
  • volatile的强度比synchronized弱,即对于volatile变量的多个读/写操作之间的没有约束力。这个可以类比于我们用synchronized修饰某个HashMap对象和使用ConcurrentHashMap之间的关系。
  • 特性总结
    • 可见性。对一个volatile变量的读,总是能看到(任意线程)对这个volatile变量最后的写入。
    • 原子性:对任意单个volatile变量的读/写具有原子性,但类似于volatile++这种复合操作不具有原子性
  • 原理:加内存屏障,确保线程在读某个变量之前,将该线程的私有缓存失效,直接从内存中读;确保线程在写某个变量之后,将该线程私有缓存刷入内存。
  1. 分布式session服务的实现 在之前参加过的一个项目中,负责session服务的重写(C++转Java),站在更高的层面看,为什么需要这个session服务呢?是为了解决分布式系统中,多台机器之间的session同步问题(参考:分布式session中同步的那些事)。
  • 有状态的session和无状态的session之间如何选择?有状态的session,指的是用户的信息会被编码到sid中;无状态的session,则是该sid仅仅是随机字符串,没有包含任何有效信息。在分布式系统中,要根据业务特点选择有状态和无状态session:含有用户信息的,适用于网站登录等经常登入登出的场景;不含用户信息的,适用于用户登录操作不频繁,其他业务操作比较频繁的。
  • 分布式系统中的CAP理论
  1. Spring MVC中@ResponseBody和HttpMessageConverter的实现原理?或者,换个问法:Spring MVC中自动返回JSON、XML或者其他类型的数据的方式?这个问题我参考了SpringMVC关于json、xml自动转换的原理研究[附带源码分析],并根据自己目前所用的4.2.6.RELEASE版本过了一遍源码。
  • 配置方法,在xxxx-servlet.xml文件中添加mvc配置;然后使用@ResponseBody修饰Controller中的一个方法。
代码语言:javascript
复制
<mvc:annotation-driven/>
  • 原理分析
    • 在<mvc:annotation-driven/>上使用Command + B快捷键,跳转到该标签的定义文件,即spring-mvc-4.0.xsd,可以看到关于该标签的定义,在这个文件中有一行<xsd:element name="message-converters" minOccurs="0">,表示该标签内部可以提供一个嵌套标签<message-converters>,用于设置HttpMessageConverters。
    • 在Spring的容器中,对bean的处理分为两步:(1)读取元数据配置(XML文件、JavaConfig或者注解),生成BeanDefinition对象;(2)通过各种BeanDefinitionParser的具体实现,生成我们定义的bean对象。
    • 这里负责解析<mvc:annotation-driven />标签的解析器是AnnotationDrivenBeanDefinitionParser。在该类的parse方法中,实例化了RequestMappingHandlerMapping、ConfigurableWebBindingInitializer、RequestMappingHandlerAdapter等类。其中,RequestMappingHandlerMapping负责定义url请求和具体的Controller方法直接的映射关系;RequestMappingHandlerAdapter负责作为适配器模式出现,填平DispatchServlet与不同RequestMappingHandler之间的关系;
  • RequestMappingHandlerAdapter中有一个属性messageConverters,就是我们这里要讲到的消息转换器。在AnnotationDrivenBeanDefinitionParser这个类中有一个方法:getMessageConverters,代码如下:
代码语言:javascript
复制
private ManagedList<?> getMessageConverters(Element element,
 Object source, ParserContext parserContext) {
   Element convertersElement = DomUtils.getChildElementByTagName(element, "message-converters");
   ManagedList<? super Object> messageConverters = new ManagedList<Object>();
   if (convertersElement != null) {
      messageConverters.setSource(source);
      for (Element beanElement : DomUtils.getChildElementsByTagName(convertersElement, "bean", "ref")) {
         Object object = parserContext.getDelegate().parsePropertySubElement(beanElement, null);
         messageConverters.add(object);
      }
   }
   if (convertersElement == null || Boolean.valueOf(convertersElement.getAttribute("register-defaults"))) {
      messageConverters.setSource(source);
      messageConverters.add(createConverterDefinition(ByteArrayHttpMessageConverter.class, source));
      RootBeanDefinition stringConverterDef = createConverterDefinition(StringHttpMessageConverter.class, source);
      stringConverterDef.getPropertyValues().add("writeAcceptCharset", false);
      messageConverters.add(stringConverterDef);
      messageConverters.add(createConverterDefinition(ResourceHttpMessageConverter.class, source));
      messageConverters.add(createConverterDefinition(SourceHttpMessageConverter.class, source));
      messageConverters.add(createConverterDefinition(AllEncompassingFormHttpMessageConverter.class, source));
      if (romePresent) {
         messageConverters.add(createConverterDefinition(AtomFeedHttpMessageConverter.class, source));
         messageConverters.add(createConverterDefinition(RssChannelHttpMessageConverter.class, source));
      }
      if (jackson2XmlPresent) {
         RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2XmlHttpMessageConverter.class, source);
         GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
         jacksonFactoryDef.getPropertyValues().add("createXmlMapper", true);
         jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
         messageConverters.add(jacksonConverterDef);
      }
      else if (jaxb2Present) {
         messageConverters.add(createConverterDefinition(Jaxb2RootElementHttpMessageConverter.class, source));
      }
      if (jackson2Present) {
         RootBeanDefinition jacksonConverterDef = createConverterDefinition(MappingJackson2HttpMessageConverter.class, source);
         GenericBeanDefinition jacksonFactoryDef = createObjectMapperFactoryDefinition(source);
         jacksonConverterDef.getConstructorArgumentValues().addIndexedArgumentValue(0, jacksonFactoryDef);
         messageConverters.add(jacksonConverterDef);
      }
      else if (gsonPresent) {
         messageConverters.add(createConverterDefinition(GsonHttpMessageConverter.class, source));
      }
   }
   return messageConverters;
}

这个函数中的关键是几个If...else...语句,通过判断指定的类是否存在,来决定是否添加对应的messageConverter(在4.0之后应该可以使用@Condition条件注解来优化这块代码)。

代码语言:javascript
复制
private static final boolean jackson2XmlPresent =
      ClassUtils.isPresent("com.fasterxml.jackson.dataformat.xml.XmlMapper", AnnotationDrivenBeanDefinitionParser.class.getClassLoader());
  • 另外一方面,在Spring MVC的请求处理流程中,由RequestMappingHandlerAdapter实现具体的handler的调用,即handleInternal函数,在这个函数中,该类将具体的方法调用委托给了HandlerMethod的invokeHandle方法处理;在这个方法中又接着向下委托给具体的ServletInvocableHandlerMethod类的invokeAndHandle方法处理。
    • 在ServletInvocableHandlerMethod这个类中维护了一个类:HandlerMethodReturnValueHandlerComposite。
代码语言:javascript
复制
private HandlerMethodReturnValueHandlerComposite returnValueHandlers;
代码语言:javascript
复制
- 通过returnValueHandlers调用handleReturnValue方法,利用多态特性,找到具体的HandlerMethodReturnValueHandler实现去处理。这里采用的是:*RequestResponseBodyMethodProcessor* ,即如下代码
代码语言:javascript
复制
@Override
public void handleReturnValue(Object returnValue, MethodParameter returnType,
      ModelAndViewContainer mavContainer, NativeWebRequest webRequest)
      throws IOException, HttpMediaTypeNotAcceptableException,
 HttpMessageNotWritableException {

         mavContainer.setRequestHandled(true);
         // Try even with null return value. ResponseBodyAdvice could get involved.
         writeWithMessageConverters(returnValue, returnType, webRequest);
}
代码语言:javascript
复制
- 具体的写HTTP响应的方法就是writeWithMessageConverters,这个方法的主要内容是:(1)获得客户端可接受的媒体类型列表,即从HTTP request中拿到Accept参数;(2)获得服务器中定义的可提供的媒体类型;(3)将这两个集合做交集,最终得到一个compatibleMediaTypes集合(如果该集合为空,则则抛出异常);(4)canwrite方法根据returnValueClass和selectedMediaType决定是否可以用某个转换器输出。
  1. SSM(Spring MVC、Spring、MyBatis)项目中进行单元测试时,如果希望配置Log4j,可以参考这篇文章:link
  2. 在项目中,遇到JVM中CPU过高的情况,如何处理?
  • 我一般遵循如下步骤排查:
    • 通过ps -ef | grep 'java'命令找到jvm的PID,例如12345;
    • 通过top -H -p12345命令查看每个线程的工作状态,截图;
    • 通过jstack -l 12345 > temp.txtdump线程栈
    • 将第二步中截图留下的前几个线程的线程号,转换成16进制,在temp.txt中查找,就能找到对应的线程栈。
  • 今天看到宏江前辈提供的一个脚本:检测最耗cpu的线程的脚本,准备下次遇到类似问题的时候试试。
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016.06.16 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档