记一次parallelStream错误使用导致的NullPointerException

parallelStream

在 Java8 中,新增了一个很有用的功能就是 ,这个功能可以使我们可以快速的写出优雅的代码,其中 stream 是一个串行流(说法可能有误…就是不会采取多线程来进行处理)。还有一种就是 parallelStream 采用 ForkJoinPool 来实现并发,加快执行效率。

所以在使用 parallelStream 的时候一定要注意线程安全的问题,首先看一段代码:

在这段代码中,是首先判断 dataListList 里面对象的 name 属性是否是偶数,是的话则添加至偶数List,反之则添加至奇数List。

然后开始测试这段代码:

阅读更多

WeakHashMap的实现原理

简介

什么是WeakHashMap

WeakHashMap实现了 Map 接口,属于 Java 集合中的一员,其用法几乎和 HashMap 一致,但是由于它的 Entry 还继承了 WeakHashMap ,因此导致它的这个 Entry 在触发 FullGc 的时候是有可能可以被回收的。

以下测试,JVM参数统一为:-Xmx64M -Xms64M -XX:+PrintGCDetails

首先上一段代码:

阅读更多

代理模式之JDK为什么需要实现接口(下篇)

前言

在上篇主要介绍了 JDK 动态代理执行的一些流程,通过 Proxy 类来实现生成新的代理类,在这一篇主要讲的是 Proxy 是如何来生成以及缓存生成的代理类

二级缓存

在上一篇的结尾提到过了一个map,其结构如下:

ConcurrentMap<Object, ConcurrentMap<Object, Supplier>>

阅读更多

代理模式之JDK为什么需要实现接口(上篇)

简介

首先代理模式分为静态代理和动态代理,由于JDK采用的是动态代理,所以静态代理在这里不再介绍。

动态代理

JDK动态代理

首先JDK的动态代理要求真实的对象必须实现一个接口,而代理类则实现InvocationHandler接口来完成动态代理。如下代码:

阅读更多

重温Java中String

本文的内容都是基于 JDK1.8 来写的,主要是复习下 String 类的设计。

简介

String 是一个用于存储字符串的类,其内部是通过 char 数组来实现,在 Java 中,1byte = 8bit,1char = 2byte, 所以在 Java 中,String 的code point是16位。
String 类是由 final 关键字来修饰的,因此表明该类不可以被继承,同时 String 又实现了 Serializable、Comparable、CharSequence接口,表明 String 可以被序列化,以及使用cpmpareTo来比较两个字符串的值。

字符集编码

内码

阅读更多

SpringBoot中异步线程的处理

在工作或者学习的时候,我们都会接触到异步编程,大多数情况下都是通过新建一个线程池,然后调用submit方法或者execute方法来执行。如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public void simpleThreadPool(){
ExecutorService executor = new ThreadPoolExecutor(4,5,0, TimeUnit.SECONDS,new LinkedBlockingDeque<Runnable>());
executor.execute(new Runnable() {
@Override
public void run() {
System.out.println("run");
}
});
Callable<String> callable = new Callable<String>() {
@Override
public String call() throws Exception {
return "callable";
}
};
Future future = executor.submit(callable);
System.out.println(future.get());
}

在Springboot中其实也可以这样做,但是不利于后期的维护,加入后期需要把 runable 的方法修改为同步类型的,那么此时就需要大量的改动代码,如果说很多地方都用到的了的话,就会很容易漏掉了一处导致bug的产生。

Spring的解决方法

不需要返回值的异步

阅读更多

springboot多数据源-sqlSessionFactory

在SpringBoot中,动态的切换数据源的方式有两种,一种是通过AbstractRoutingDataSource来通过注解实现,另一种则是通过配置不同的SqlSessionFactory来读取不同文件夹的mapper,从而实现多数据源。
代码如下:

DataSourceOneConfig

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
@Configuration
@MapperScan(basePackages = {"xyz.somersames.dao.one"} ,sqlSessionTemplateRef = "dataSourceOneSqlSessionTemplate")
public class DataSourceOneConfig {

@Primary
@Bean(name = "dataSourceOneT")
// 这里后面加一个T是防止Spring出现skiped mapperFactoryBean 错误,导致无法注入
@ConfigurationProperties(prefix = "spring.datasource.one")
public DataSource getDataSource(){
return new DruidDataSource();
}

@Primary
@Bean(name = "dataSourceOneSqlSessionFactory")
public SqlSessionFactory setSqlSessionFactory(@Qualifier("dataSourceOneT") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/one/*.xml"));
return bean.getObject();
}

@Primary
@Bean(name = "dataSourceOneTransactionManager")
public DataSourceTransactionManager setTransactionManager(@Qualifier("dataSourceOneT") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}

@Primary
@Bean(name = "dataSourceOneSqlSessionTemplate")
public SqlSessionTemplate setSqlSessionTemplate(@Qualifier("dataSourceOneSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

DataSourceTwoConfig

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
@Configuration
@MapperScan(basePackages = {"xyz.somersames.dao.two"} ,sqlSessionTemplateRef = "dataSourceTwoSqlSessionTemplate")
public class DataSourceTwoConfig {

@Bean(name = "dataSourceTwoT")
// 这里后面加一个T是防止Spring出现skiped mapperFactoryBean 错误,导致无法注入
@ConfigurationProperties(prefix = "spring.datasource.two")
public DataSource getDataSource(){
return new DruidDataSource();
}

@Bean(name = "dataSourceTwoSqlSessionFactory")
public SqlSessionFactory setSqlSessionFactory(@Qualifier("dataSourceTwoT") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapper/two/*.xml"));
return bean.getObject();
}

@Bean(name = "dataSourceTwoTransactionManager")
public DataSourceTransactionManager setTransactionManager(@Qualifier("dataSourceTwoT") DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}

@Bean(name = "dataSourceTwoSqlSessionTemplate")
public SqlSessionTemplate setSqlSessionTemplate(@Qualifier("dataSourceTwoSqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
}

阅读更多

SpringBoot2.x是怎样只初始化LettuceConnectionFactory的呢

SpringBoot1.5使用Redis和2.x的区别

在 SpringBoot1.5 的版本的时候,如果要创建一个 RedisTemplate 的话,那么可以直接使用如下代码:

1
2
3
4
5
6
7
@Bean
public RedisTemplate<String,Object> redisTemplate(JedisConnectionFactory jedisConnectionFactory){
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<>();
redisTemplate.setConnectionFactory(jedisConnectionFactory);
// 添加序列化代码
return redisTemplate;
}

然后在业务类中直接通过 @Autowired 注解来调用 redisTemplate,但是如果将 SpringBoot1.5 升级到 2.0 之后,你会发现这样写的话,SpringBoot 启动的时候会报错。报错内容如下:

1
2
3
4
5
The following candidates were found but could not be injected:
- Bean method 'redisConnectionFactory' in 'JedisConnectionConfiguration' not loaded because @ConditionalOnBean (types: org.springframework.data.redis.connection.RedisConnectionFactory; SearchStrategy: all) found beans of type 'org.springframework.data.redis.connection.RedisConnectionFactory' redisConnectionFactory

Consider revisiting the entries above or defining a bean of type 'org.springframework.data.redis.connection.jedis.JedisConnectionFactory' in your configuration.

阅读更多

Java8中的流式简单总结

在大量使用Java8中的流式操作之后,觉得用起来还挺舒服的,所以正好趁这个机会总结下。

使用Java8的lambda表达式的时候,需要先把集合转为一种流,也就是调用 stream 方法,但是 stream 却是 Collection 类里面的一个方法,也就是只有 Collection 的子类才可以使用,所以 Map 集合是使用不了的,同理,对于数组,可以通过Arrays.stream() 方法来讲数组转为一个Stream,这样也可以使用Stream里面的方法了,

将集合转为流

下面介绍几个很常用的方法来介绍流式操作的便捷性。

1
2
3
4
5
6
List<String> stringList = new ArrayList<>();
stringList.add("a1");
stringList.add("b1");
stringList.add("a2");
stringList.add("b2");
stringList.stream();
阅读更多

JDK1.7中的ConcurrentHashMap实现细节(二)

简介

在JDK1.7向JDK1.8升级的过程中,ConcurrentHashMap由原来的可重入锁和CAS锁直接被替换为synchronized关键字了,虽然说在功能上都是完全一致的,但是在这里一直都有一个疑惑,既然在1.7的使用过程中没什么问题,那到底是出于什么原因要将其替换呢。

JDK1.7中的ConcurrentHashMap

在JDK1.7中,其结构是由一个可重入锁Segment数组和每一个节点下的HashEntry数组来实现的。结构图如下:

由于 segment 是一个锁,所以如果在并发的过程中,多个线程尝试向一个 segment 中的 HashEntry 进行插入的时候,只能有一个线程会获取到锁,其他的线程会被阻塞直至锁被释放,所以这个容器是一个并发安全的。

阅读更多