CountDownLatch-注意事项

先看一段代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14

CountDownLatch countDownLatch = new CountDownLatch(params.size());
try {
params.forEach(regionNameMapping -> {
CompletableFuture.runAsync(() -> {
Region region = this.getRegionById(regionNameMapping.getRegionId());
regionNameMapping.getRegionNameConsumer().accept(region.getName());
countDownLatch.countDown();
});
});
countDownLatch.await();
} catch (Exception e) {
log.error(e.getMessage(), e);
}

上面的代码逻辑很简单,并发去执行getRegionById这个方法。然后await等待结果。
但是里面有一个隐患,当region查出来为null时,会出现NPE,就会导致countDown()无法被执行,于是程序就一直阻塞在

1
2

countDownLatch.await();
阅读更多

Golang中一些设定

  1. 格式化时间不是用yyyy MM DD HH mm ss sss等在其他语言中常见的符号。
    而是:
    2006-01-02 15:04:05

    1
    2
    3
    now := time.Now()
    nowRight := now.Format("2006-01-02 15:04:05")

    完整UTC为:2006-01-02T15:04:05-07:00

阅读更多

获取jar中的文件注意事项

获取resources下的文件Java有很多种方法。但是如果你的程序最终打成jar发布。那么需要注意你是否以流inputstream读取。
因为jar中的文件路径为jar!xxxxxx 简单的使用path 或者getResouces肯定获取不到。

为什么阿里禁止通过Executors创建线程池

Executors是通过new一个ThreadPoolExecutor来创建的线程池。来看看ThreadPoolExecutor的构造方法:

1
2
3
4
5
6
7
8
public ThreadPoolExecutor(int corePoolSize,
int maximumPoolSize,
long keepAliveTime,
TimeUnit unit,
BlockingQueue<Runnable> workQueue) {
this(corePoolSize, maximumPoolSize, keepAliveTime, unit, workQueue,
Executors.defaultThreadFactory(), defaultHandler);
}
阅读更多

对变量加锁后是否还需要使用volatile

大家都知道volatile保证了变量在线程间的可见性(主内存与CPU缓存(线程内存)间)。Lock与synchronized也可以保证可见性,还能保证原子性。
那么使用了Lock与synchronized之后,变量是否就不用加volatile了?

阅读更多

比特币、区块链、挖矿是什么

比特币跟人民币美元不一样, 不是银行卡里的一个数字。而是通过比特币账本追溯而来。

你去查询银行卡余额,ATM上很可能显示:余额5元。
而要看比特币余额,需要翻开比特币账本:张三转给李四5个比特币,李四转给王五3个比特币,王五转给你1个比特币,于是你有1个比特币。

阅读更多

记一个Spring Data JPA自定义分页查询BUG

官方给出的自定义分页查询的示例是这样的:

1
2
3
4
5
6
public interface UserRepository extends JpaRepository<User, Long> {
@Query(value = "SELECT * FROM USERS WHERE LASTNAME = ?1",
countQuery = "SELECT count(*) FROM USERS WHERE LASTNAME = ?1",
nativeQuery = true)
Page<User> findByLastname(String lastname, Pageable pageable);
}
阅读更多

Stream的TerminalOp执行原理

Stream在执行intermediate(例如 map、filter)操作时,会形成referencePipeline 双向链表。

TermianlOp执行时遍历链表:

1
2
3
for ( AbstractPipeline p=AbstractPipeline.this; p.depth > 0; p=p.previousStage) {
sink = p.opWrapSink(p.previousStage.combinedFlags, sink);
}

执行StatelessOp 中Sink的OnWrapSink。
(通过Sink 中ChainedReference 翻转)

所以Stream中的元素是依次应用intermediate操作。并不是所有元素应用完第一个intermediate操作,在应用下一个。

PS:使用Stream时,首先会构建一个HEAD-源阶段(Stream()),然后经历StatelessOp-中间阶段(map、filter),最终通过TermianlOp(reduce等)