参考慕课网并发课程做的笔记
不可变对象
-
不可变的对象需要满足的条件
- 对象创建以后期状态就不能改变。
- 对象所有域都是final类型
- 对象是正确创建的(对象创建期间,this引用没有逸出)
-
创建不可变对象的方式(参考String类型)
- 将类声明成final类型,使其不可以被继承
- 将所有的成员设置成私有的,使其他的类和对象不能直接访问这些成员 对变量不提供set方法
- 将所有可变的成员声明为final,这样只能对他们赋值一次
- 通过构造器初始化所有成员,进行深度拷贝
- 在get方法中,不直接返回对象本身,而是克隆对象,返回对象的拷贝
-
final关键字:类、方法、变量
- 修饰类:不能被继承(final类中的所有方法都会被隐式的声明为final方法)。
- 修饰方法:1、锁定方法不被继承类修改;2、提升效率(private方法被隐式修饰为final方法)。
- 修饰变量:基本数据类型变量(初始化之后不能修改)、引用类型变量(初始化之后不能再修改其引用)。
-
除了final 还有其他可以定义不可变的方法
-
Collections.unmodifiabnleXXX:Collection,List,Set,Map…
-
Guava:ImmutableXXX: Collection,List,Set,Map…
-
线程封闭
把对象封装到一个线程里,只有这个线程能看到这个对象
- 实现线程封闭
- Ad-hoc 线程封闭,程序控制实现 最糟糕
- 堆栈封闭:局部变量,无并发问题
- ThreadLocal线程封闭:特别好的封闭方法(可以看下源码)
线程不安全类的写法
如果一个类的对象可以被多个线程访问,不做特殊的同步和并发处理,很容表现出异常
- StringBuilder 线程不安全,StringBuffer线程安全 原因:StringBuffer几乎所有的方法都加了synchronized关键字
- SimpleDateFormat 在多线程共享使用的时候回抛出转换异常,应该在用堆栈封闭在每次调用方法的时候在方法里创建一个SimpleDateFormat,joda-time的DateTimeFormatter是线程安全的
- ArrayList,HashMap,HashSet等Collections线程不安全
- 先检查再执行这种方式线程不安全
1
2
3
4
// 因为可能不是非原子性(如果是在多线程下 要不然加锁 要不然操作是原子性)
if(condition(a)){
handle(a);
}
线程安全-同步容器
ArrayList -> Vector,Stack
HasMap -> HashTable(key,value不能为null)
Collections.synchronizedXXX(List,Set,Map)
但是使用同步容器的情况下 也不一定能做到线程安全(见代码)同步容器性能可能会稍差 而且不一定会保证性能安全,所以一般会使用并发容器来代替同步容器。
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
/**
* 并发测试
* 同步容器不一定线程安全
* @author gaowenfeng
*/
@Slf4j
@NotThreadSafe
public class VectorExample2 {
/** 请求总数 */
public static int clientTotal = 5000;
/** 同时并发执行的线程数 */
public static int threadTotal = 50;
public static List<Integer> list = new Vector<>();
public static void main(String[] args) throws InterruptedException {
for (int i = 0; i < 10; i++) {
list.add(i);
}
Thread thread1 = new Thread(() -> {
for (int i = 0; i < 10; i++) {
list.remove(i);
}
});
Thread thread2 = new Thread(() -> {
// thread2想获取i=9的元素的时候,thread1将i=9的元素移除了,导致数组越界
for (int i = 0; i < 10; i++) {
list.get(i);
}
});
thread1.start();
thread2.start();
}
}
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
@Slf4j
@NotThreadSafe
public class VectorExample3 {
//java.util.ConcurrentModificationException
//对集合遍历的时候 有对集合进行增删的操作 会报异常
//如果要做remove操作 可以遍历的过程中先标记下 再遍历完后再操作
private static void test1(Vector<Integer> v1) { //foreach
for (Integer i : v1) {
if (i.equals(3)) {
v1.remove(i);
}
}
}
//java.util.ConcurrentModificationException
private static void test2(Vector<Integer> v1) { //iterator
Iterator<Integer> iterator = v1.iterator();
while (iterator.hasNext()) {
Integer i = iterator.next();
if (i.equals(3)) {
v1.remove(i);
}
}
}
//success
private static void test3(Vector<Integer> v1) {
for (int i = 0; i < v1.size(); i++) {
if (v1.get(i).equals(3)) {
v1.remove(i);
}
}
log.info("size--{}",v1.size());
}
public static void main(String[] args) {
Vector<Integer> vector = new Vector<>();
vector.add(1);
vector.add(2);
vector.add(3);
test3(vector);
}
}