FutureTask构建高效缓存

例子来源于Java并发编程实战,5.6 构建高效且可伸缩的结果缓存。

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
public class Memoizer<A, V> implements Computable<A, V> {
private final ConcurrentMap<A, Future<V>> cache = new ConcurrentHashMap<A, Future<V>>();
private final Computable<A, V> c;

public Memoizer(Computable<A, V> c) {
this.c = c;
}

public V compute(final A arg) throws InterruptedException {
while (true) {
Future<V> f = cache.get(arg);
if (f == null) {
Callable<V> eval = new Callable<V>() {
public V call() throws InterruptedException {
return c.compute(arg);
}
};
FutureTask<V> ft = new FutureTask<V>(eval);
f = cache.putIfAbsent(arg, ft);
if (f == null) {
f = ft;
ft.run();
}
}
try {
return f.get();
} catch (CancellationException e) {
cache.remove(arg, f);
} catch (ExecutionException e) {
throw LaunderThrowable.launderThrowable(e.getCause());
}
}
}
}
阅读更多

线程同步

一、奇偶交替

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
public class PrintTest {
static volatile int count = 1;
public static void printZero() {
System.out.print("0");
}
public static void printOdd() {
System.out.print(count);
}
public static void printEven() {
System.out.print(count);
}

public static void print(int n) throws InterruptedException {
ReentrantLock lock = new ReentrantLock();
Condition zeroCond = lock.newCondition();
Condition oddCond = lock.newCondition();
Condition evenCond = lock.newCondition();

Thread zero = new Thread(() -> {
lock.lock();
while (count <= n) {
zeroCond.await();
if (count <= n) {
printZero();
}
if (count % 2 == 1) {
evenCond.signal();
} else {
oddCond.signal();
}
}
lock.unlock();
});
Thread odd = new Thread(() -> {
lock.lock();
while (count <= n) {
oddCond.await();
if (count > n) {
return;
}
printEven();
count++;
zeroCond.signal();
}
lock.unlock();
});

Thread even = new Thread(() -> {
lock.lock();
while (count <= n) {
evenCond.await();
if (count > n) {
return;
}
printOdd();
count++;
zeroCond.signal();
}
lock.unlock();
});

zero.start();
odd.start();
even.start();

lock.lock();
zeroCond.signal();
lock.unlock();

zero.join();
odd.join();
even.join();
}
}
阅读更多

Spring注解Import

一、注解源码

1
2
3
4
5
6
7
8
9
10
11
12
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Import {

/**
* {@link Configuration}, {@link ImportSelector}, {@link ImportBeanDefinitionRegistrar}
* or regular component classes to import.
*/
Class<?>[] value();

}
阅读更多

HttpClient(三)连接池获取连接

源码:httpcomponents-core-4.4.13,org.apache.http.pool.AbstractConnPool

一、源码+注释

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
public abstract class AbstractConnPool<T, C, E extends PoolEntry<T, C>>
implements ConnPool<T, E>, ConnPoolControl<T> {
private E getPoolEntryBlocking(
final T route, final Object state,
final long timeout, final TimeUnit timeUnit,
final Future<E> future) throws IOException, InterruptedException, ExecutionException, TimeoutException {
// 1. 根据connectionRequestTimeout计算等待终止时间
Date deadline = null;
if (timeout > 0) {
deadline = new Date (System.currentTimeMillis() + timeUnit.toMillis(timeout));
}
this.lock.lock();
try {
// 2. 获取该路由的连接池,如果没有则新建
final RouteSpecificPool<T, C, E> pool = getPool(route);
E entry;
// 3. 循环用来处理等待逻辑
for (;;) {
Asserts.check(!this.isShutDown, "Connection pool shut down");
if (future.isCancelled()) {
throw new ExecutionException(operationAborted());
}
// 4. 尝试从该路由的连接池获取连接
for (;;) {
// 若pool.available有可用连接,则移动至pool.leased并返回
entry = pool.getFree(state);
if (entry == null) {
break;
}
// entry若过期,设置为close
if (entry.isExpired(System.currentTimeMillis())) {
entry.close();
}
// entry若已close,则从全局可用队列this.available移除
if (entry.isClosed()) {
// 全局可用available移除
this.available.remove(entry);
// 从pool.leased移动至pool.available
pool.free(entry, false);
} else {
break;
}
}
// 5.1 若从该路由的连接池获取到连接,则从全局可用队列移动到待释放队列,然后返回
// this.available ---> this.leased
if (entry != null) {
this.available.remove(entry);
this.leased.add(entry);
onReuse(entry);
return entry;
}

// 5.2 若从该路由的连接池没获取到连接,则开始新建连接逻辑
// 1)获取该路由允许使用连接的最大值
final int maxPerRoute = getMax(route);
// 2)计算该路由超出的连接数(由于最少需要释放一个,所以总数+1)
final int excess = Math.max(0, pool.getAllocatedCount() + 1 - maxPerRoute);
// 3)按LRU淘汰从该路由的pool.available移除空闲连接,同样在全局this.available里删除
if (excess > 0) {
for (int i = 0; i < excess; i++) {
final E lastUsed = pool.getLastUsed();
if (lastUsed == null) {
break;
}
lastUsed.close();
this.available.remove(lastUsed);
pool.remove(lastUsed);
}
}
// 4.1)如果该路由当前已分配的连接小于设置的该路由的最大连接数,并且全局连接数未达到上限,为该路由创建新连接。然后返回。
// 当前全局连接数大于上限时,则根据LRU原则从全局可用连接里淘汰连接(会从其路由里也删除)
if (pool.getAllocatedCount() < maxPerRoute) {
final int totalUsed = this.leased.size();
final int freeCapacity = Math.max(this.maxTotal - totalUsed, 0);
if (freeCapacity > 0) {
final int totalAvailable = this.available.size();
if (totalAvailable > freeCapacity - 1) {
if (!this.available.isEmpty()) {
final E lastUsed = this.available.removeLast();
lastUsed.close();
final RouteSpecificPool<T, C, E> otherpool = getPool(lastUsed.getRoute());
otherpool.remove(lastUsed);
}
}
final C conn = this.connFactory.create(route);
entry = pool.add(conn);
this.leased.add(entry);
return entry;
}
}
// 4.2)加入连接等待队列,当有连接释放时,若等待队列不为空则会唤醒等待的线程,进入下次循环重新尝试获取连接。
// 此处会根据设置的connectionRequestTimeout设置等待超时时间
boolean success = false;
try {
pool.queue(future);
this.pending.add(future);
if (deadline != null) {
success = this.condition.awaitUntil(deadline);
} else {
this.condition.await();
success = true;
}
if (future.isCancelled()) {
throw new ExecutionException(operationAborted());
}
} finally {
pool.unqueue(future);
this.pending.remove(future);
}
if (!success && (deadline != null && deadline.getTime() <= System.currentTimeMillis())) {
break;
}
}
throw new TimeoutException("Timeout waiting for connection");
} finally {
this.lock.unlock();
}
}
}
阅读更多

Maven3.x兼容笔记

一、依赖解析

The core of Maven 3 uses Aether for dependency resolution. Aether employs a different approach to calculate the transitive dependencies and is meant to fix some of the trickier (conflict) resolution issues within Maven 2.x.

In practice, this change can result in different class paths especially for projects with big/complex dependency graphs. For instance, projects that erroneously have conflicting dependencies on their classpath might encounter build issues depending on the classpath order. See JETTY-1257 for a concrete example of this issue.

阅读更多

Maven如何处理循环依赖

一、Reactor对Project中model循环依赖检测

  • 发生阶段:开始构建时,解析project的model解析顺序

  • 检测到循环依赖时动作:抛出异常,终止解析

阅读更多

Maven源码-依赖解析

一、依赖解析流程说明

1)通过DefaultModelBuilder#build生成当前pom的Model,也就是effective pom

2)递归解析(深度遍历)effective pom的child dependency,生成effective pom

阅读更多

Maven源码-主流程

一、MavenCli#main

maven使用Plexus容器(一种IOC容器)进行MOJO等管理,apache-maven/src/bin/m2.conf里声明了其主类org.apache.maven.cli.MavenCli:

1
2
3
4
5
6
7
8
main is org.apache.maven.cli.MavenCli from plexus.core

set maven.conf default ${maven.home}/conf

[plexus.core]
load ${maven.conf}/logging
optionally ${maven.home}/lib/ext/*.jar
load ${maven.home}/lib/*.jar
阅读更多

Maven依赖协调原则及依赖顺序的影响

一、Maven依赖协调原则

1)POM的直接依赖,后声明的依赖有效

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
<?xml version="1.0"?>
<project
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<groupId>person.kivihub</groupId>
<artifactId>maven-demo</artifactId>
<version>1.0.0-SNAPSHOT</version>
<url>http://maven.apache.org</url>
<dependencies>
<!-- 被覆盖 -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.17</version>
</dependency>
<!-- 有效 -->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.2</version>
</dependency>
</dependencies>
</project>
阅读更多

echo无法清空日志

一、问题描述

前段时间webmethod应用所在物理机磁盘满了,于是清理其日志。但是有个通过Log4j2记录的日志文件在执行cat /dev/null | tee *.log后大小又瞬间恢复到原来尺寸。当时感觉应该应该是使用Appender导致的,但时苦于没有找到log4j2.xml配置也就搁置了。

二、问题探究

阅读更多

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
2021-12-16 14:05:27[ http-nio-8082-exec-10:519172801 ] - [WARN ] com.xxx.xxx.XxxFilter-doFilter:107 - 解析Tenant出错, 错误信息:Request processing failed; nested exception is java.lang.NullPointerException
org.springframework.web.util.NestedServletException: Request processing failed; nested exception is java.lang.NullPointerException
        at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:982)
        at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:861)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:687)
        at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:846)
        at javax.servlet.http.HttpServlet.service(HttpServlet.java:790)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:108)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:81)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
        at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:197)
        at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
Caused by: java.lang.NullPointerException
阅读更多