Java的volatile和MESI协议

使用过Java多线程的都知道,volatile可以确保多线程永远都可以读取到最新的变量。但是却无法保证一个操作的原子性
关于这部分可以先从硬件部分说起:

硬件

单核CPU

在现代计算机中,CPU的速度是远远快于内存的,如果不加以任何处理,此时CPU就会一直在等待内存的IO,从而导致一些资源的浪费。所以就有了高速缓冲区(cache),但是在Cache里面肯定是不会将内存的所有数据都拷贝一份,因为cacahe只有几十kb的大小。

这就造成了一个现象,也就是说每一个核心都会有一个共享的内存区域和一个自己Cache区域。

阅读更多

SpringBoot中在一个事物中更新多表的注意事项

现象:

具体表现为数据被update之后,在同一个事物里面再次查询,查询的是一个更新之后的值。

复现步骤

更新一个商品的信息,其步骤如下:

  1. 更新商品表的一些数据
  2. 在进展表中新加入一条申请
  3. 在操作日志表中新增各种变更的操作(记录变更之前的值,变更之后的值)
阅读更多

一次SpringBoot初始化引起的思考

在Spring中,经常会使用@Resource注解来自动装配一些Bean,但是在初始化的时候还是有一点小坑的,下面是一段代码,有三个类,分别是A,B,C。

类A:

1
2
3
4
5
6
7
8
9
10
11
@Service
public class A {

@Resource
B b;


public void someMethod(){
System.out.println(b.getC() == null);
}
}

类B:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Service
public class B {

@Resource
C c ;

public C cParam =getC();

public C getC(){
return c.getAnewC();
}

}
阅读更多

Spring的本质-Servlet初探

现在Java的web开发体系中,Spring以其轻量级,低耦合而占据了老大的地位,但是Spring的本质是什么,为什么在Spring里面不需要像以前写Servlet项目一样,需要配置web.xml。这些都需要我们去刨根问底的。

Servlet是什么

按照Servlet规范所解释的那样,Servlet是一个Web组件,就是类似于生物里面的病毒宿主一样,病毒还是那个病毒,但是离开了宿主之后就不能单独生存了。而宿主就是一个Servlet容器。(tomcat就是一个Servlet容器)

Servlet 是基于 Java 技术的 web 组件,容器托管的,用于生成动态内容。像其他基于 Java 的组件技术一样,
Servlet 也是基于平台无关的 Java 类格式,被编译为平台无关的字节码,可以被基于 Java 技术的 web server
动态加载并运行。容器,有时候也叫做 servlet 引擎,是 web server 为支持 servlet 功能扩展的部分。客户端
通过 Servlet 容器实现的请求/应答模型与 Servlet 交互

在Tomcat的源码包里面,Servlet其实是一个接口,如下所示:

阅读更多

Java通过getResourceAsStream()读取不到文件的原因

首先出现这个原因的时候,需要弄清楚工程目录和编译目录。

工程目录

以IDEA为例,在IDEA里面,我们写代码的地方就是一个工程目录,常见的例如src下面的各种java文件,这种目录就可以称之为一个工程目录,例如如下所示:

工程目录主要存放的是一些配置文件或者一些java文件之类的,而经jvm编译之后的目录便是编译目录了

编译目录

阅读更多

Json的底层实现一览

在开始了解Json的原理之前,首先看一段代码,在这里以阿里的FastJson为例。

1
2
3
4
5
6
7
8
public class JsonRun {
public static void main(String[] args) {
JSONObject jsonObject =new JSONObject();
jsonObject.put("id","a");
jsonObject.put("name","b");
System.out.println(jsonObject.toJSONString());
}
}

当看到上述代码的时候,可能一般的程序员都会想到的是输出为如下Json

{“id”:”a”,”name”:”b”}
但是运行这段程序,你会发现控制台打印出来的是如下代码:
{“name”:”b”,”id”:”a”}

那么为什么会出现这种情况呢,翻开FastJson的源码便知道了,首先定位到 JsonObject 这个类的构造函数,如下:

阅读更多

将Jackson替换成Fastjosn

记得有一次的面试是。如何在Spring中将JackSon 替换为 FastJson,emmmm…当时的回答是只需要替换 pom.xml,然后在使用的时候引入FastJosn就行了,但是在当时显然没有理解到面试官的意图,既然面试官强调的是如何替换,那么修改pom.xml很显然不是面试官所想要的答案,那还有什么答案呢?

有一个方法可能是面试官想要的,那就是重写Spring的HttpMesageConverter方法,在这个方法里面引入FastJson的配置,然后替换掉Spring默认的Jackson

替换方式有几种,一种是返回一个HttpMesageConverter,另一种是继承WebMvcConfigurerAdapter 来实现 configureMessageConverters

代码如下:

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
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
FastJsonConfig fastJsonConfig = new FastJsonConfig();
fastJsonConfig.setSerializerFeatures(
// 输出空置字段
SerializerFeature.WriteMapNullValue,
// list字段如果为null,输出为[],而不是null
SerializerFeature.WriteNullListAsEmpty,
// 数值字段如果为null,输出为0,而不是null
SerializerFeature.WriteNullNumberAsZero,
// Boolean字段如果为null,输出为false,而不是null
SerializerFeature.WriteNullBooleanAsFalse,
// 字符类型字段如果为null,输出为"",而不是null
SerializerFeature.WriteNullStringAsEmpty);

fastJsonConfig.setCharset(Charset.forName("UTF-8"));
fastJsonConfig.setDateFormat("yyyy-MM-dd hh:mm:ss");
FastJsonHttpMessageConverter Converter = new FastJsonHttpMessageConverter();
Converter.setFastJsonConfig(fastJsonConfig);
HttpMessageConverter<?> converter = Converter;
converters.add(0,converter);
super.configureMessageConverters(converters);
}
阅读更多

SpringCloud配置中心的使用

在实际的开发过程中,很可能会涉及到很多的开发环境,常见的例如 dev , product 等,在使用SpringCloud的时候可以通过配置中心微服务,结合 Git 管理工具实现配置的集中式管理。

配置中心

config配置中心的yml文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
spring:
application:
name: sc-config
cloud:
config:
server:
git:
uri: ${GitAddress:https://gitee.com/somersames/spring-cloud-demo-config}
search-paths: ${GitPath:dev}
eureka:
client:
service-url:
defaultZone: http://${EurekaHost:localhost}:${EurekaPort:8081}/eureka/
server:
port: 8888

这里的 ${A:B} 配置表示的是如果 A 获取不到就取 B 的值。
search-paths 则是代表从哪个文件夹中获取配置文件,可以使用 , 来进行分割。
然后在启动类中添加如下几个注解,一个配置中心的微服务便开启了。

阅读更多

HashMap得一点总结

HashMapp为什么在Hash的时候减1

在Java的Hashmap中有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
final Node<K,V> getNode(int hash, Object key) {
Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
if ((tab = table) != null && (n = tab.length) > 0 &&
(first = tab[(n - 1) & hash]) != null) {
if (first.hash == hash && // always check first node
((k = first.key) == key || (key != null && key.equals(k))))
return first;
if ((e = first.next) != null) {
if (first instanceof TreeNode)
return ((TreeNode<K,V>)first).getTreeNode(hash, key);
do {
if (e.hash == hash &&
((k = e.key) == key || (key != null && key.equals(k))))
return e;
} while ((e = e.next) != null);
}
}
return null;
}

上面有一行是 first = tab[(n - 1) & hash]) != null

HashMap为什么在传入另一个Map时加一

阅读更多

Error是真的不可以被捕获的吗?

在刚接触Java的时候经常听到的一句话便是在 Java 中,Exception 是可以捕获的,Error 是不可以捕获的。但是在随着学习的深入,会发现有些观点需要重新认识下了。
Throwable 这个类是自 JDK1.0 开始就存在于 Java 的语言之中。

Throwable

首先引用一段 Oracle 官方文档上对 Throwable 的介绍Java8 Thrwoable的介绍

The Throwable class is the superclass of all errors and exceptions in the Java language. Only objects that are instances of this class (or one of its subclasses) are thrown by the Java Virtual Machine or can be thrown by the Java throw statement. Similarly, only this class or one of its subclasses can be the argument type in a catch clause. For the purposes of compile-time checking of exceptions, Throwable and any subclass of Throwable that is not also a subclass of either RuntimeException or Error are regarded as checked exceptions.
Instances of two subclasses, Error and Exception, are conventionally used to indicate that exceptional situations have occurred. Typically, these instances are freshly created in the context of the exceptional situation so as to include relevant information (such as stack trace data).
太长,省略大部分了……

简单翻译下就是,Throwable 是 Error 和 Exception 的父类,并且只能是 Error 和 Exception 的实例才可以通过 throw 语句或者 Java虚拟机 抛出异常。Exception 或者 Error 是在出错的情况下新创建的,从而将出错的信息和数据包含进去。
另外在这个文档中还提到了一点就是当低层方法向高层方法抛出异常的时候,如果抛出的异常是受检查的异常,则

阅读更多