avatar

正进一步

只有喜欢,才会全情投入,才会成功!!!

不关注架构设计细节的程序员不是一个好架构师

  • Home
  • Tags
  • Categories
  • Archives
  • 2018寄语
当多线程并发遇到Actor
引自于:https://mp.weixin.qq.com/s/mzZatZ10Rh19IEgQvbhGUg

“我发现了一个漏洞,你这个转账虽然看起来很美,没有加锁,但是和原来的是有区别的,原来多线程思路是会把旺财和小强的账户同时锁住,然后转账,在这个过程中,别人是不能操作这两个账号的! 而你的Actor方案中,当转账管家给旺财发消息扣款的时候,小强其实是自由的,如果这时候小强的账户被冻结,那你的转账管家还得回滚旺财的扣款,这多麻烦啊。”

Bill:“哈哈,你小子还挺机灵的嘛,看出了这个问题,Actor模型非常适用于多个组件独立工作,相互之间仅仅依靠消息传递的情况。如果想在多个组件之间维持一致的状态(比如咱们例子中的转账),那就不爽了。”

“那怎么解决这个问题?”

“那必须得用一些特殊手段了,有些实现Actor的框架,例如Akka,专门提供了像Coordinated /Transactor这样的机制来处理这个问题。有空的话给你仔细讲讲。”

第6条_消除过期的对象引用
  • 将数组中的不用的元素设置为null,尽早垃圾回收

  • 缓存可以使用WeakHashMap.及时将key进行回收。

  • 使用定时器将缓存中不用的项目删除或者LinkedHashMap.removeEldestEntry()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import com.alibaba.druid.support.json.JSONUtils;

import java.util.LinkedHashMap;
import java.util.Map;

public class LinkedHashMapTest {

private static final int MAX_ENTRIES =3;

public static void main(String[] args) {
LinkedHashMap<String,String> linkedHashMap =new LinkedHashMap(){
@Override
protected boolean removeEldestEntry(Map.Entry eldest) {
return size() > MAX_ENTRIES;
}
};
linkedHashMap.put("beijing","haid");
linkedHashMap.put("beijing2","haid2");
linkedHashMap.put("beijing3","haid3");
System.out.println(JSONUtils.toJSONString(linkedHashMap));
linkedHashMap.put("beijing4","haid4");
System.out.println(JSONUtils.toJSONString(linkedHashMap));
}
}

输出:

1
2
{"beijing":"haid","beijing2":"haid2","beijing3":"haid3"}
{"beijing2":"haid2","beijing3":"haid3","beijing4":"haid4"}

  • 将监听器回调及时的清除,可以使用WeakHashMap.

  • 使用内存Heap Profiler进行对象分析。

通过Builder模式降低参数数量,增加可读性

参考:http://www.importnew.com/6605.html

  • Builder会增加个类代码,这也意味着开发者在给类增加属性时有时会忘记给该属性添加支持的builder。为了克服这个问题,通常我会将builder嵌套到类中,这样可以很容易地发现哪个相关的builder需要更新

  • 构建对象时,如果碰到类有很多参数——其中很多参数类型相同而且很多参数可以为空时,我更喜欢Builder模式来完成。当参数数量不多、类型不同而且都是必须出现时,通过增加代码实现Builder往往无法体现它的优势。在这种情况下,理想的方法是调用传统的构造函数。再者,如果不需要保持不变,那么就使用无参构造函数调用相应的set方法吧。

  • 必要参数写到构造器里

  • 如果要实现builder的重用性,可以讲初始化工作放到目标类中。不需要重用的话,可以讲初始化工作在builde创建出来的时候,就将实例创建出来。

第4条_通过私有构造器强化不可实例化的能力

将构造器 设置为Private并且要抛出异常,就可以解决这个问题.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public  class UtilsClass {

//子类不能调用父类的构造方式,也就不能有子类了.
//抛出异常是为了 本类中 声明实例对象
private UtilsClass(){
throw new RuntimeException("不可以被初始化");
}

public static void main(String[] args) {

UtilsClass u =new UtilsClass();
System.out.println(u);

}
}
漫画之什么是CAS机制

转载自: https://mp.weixin.qq.com/s/f23I1kyJQoNPu6Ntrx0S6g

读后感:

Java1.6为Synchronized做了优化,增加了从偏向锁到轻量级锁再到重量级锁的过度,但是在最终转变为重量级锁之后,性能仍然较低. 这其中用到了CAS机制。

CAS优点

  • Synchronized属于悲观锁,悲观地认为程序中的并发情况严重,所以严防死守。CAS属于乐观锁,乐观地认为程序中的并发情况不那么严重,所以让线程不断去尝试更新。
  • 避免了context switch

CAS缺点

  • CPU消耗较大,不适合高并发场景(因为冲突太多,失败率高)。
  • ABA问题
  • 不能保证代码块的原子性。例如不能保证多个变量同时更新.

乐观锁转悲观锁的经典代码见:ConcurrentHashMap的size方法

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
public int size() {
// Try a few times to get accurate count. On failure due to
// continuous async changes in table, resort to locking.
final Segment<K,V>[] segments = this.segments;
int size;
boolean overflow; // true if size overflows 32 bits
long sum; // sum of modCounts
long last = 0L; // previous sum
int retries = -1; // first iteration isn't retry
try {
for (;;) {
if (retries++ == RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
ensureSegment(j).lock(); // force creation
}
sum = 0L;
size = 0;
overflow = false;
for (int j = 0; j < segments.length; ++j) {
Segment<K,V> seg = segmentAt(segments, j);
if (seg != null) {
sum += seg.modCount;
int c = seg.count;
if (c < 0 || (size += c) < 0)
overflow = true;
}
}
if (sum == last)
break;
last = sum;
}
} finally {
if (retries > RETRIES_BEFORE_LOCK) {
for (int j = 0; j < segments.length; ++j)
segmentAt(segments, j).unlock();
}
}
return overflow ? Integer.MAX_VALUE : size;
}