Hey Lock
All things come to those who wait.–苍天不负有心人.
最近做项目时,有个需求就是实现线程安全的List集合,那么什么是线程安全的呢? 无非就是 保证其在增删改的操作中保持原子性罢了.
举个案例实现并发执行List的 add方法
public class TestList {
// 实现多线程并发执行 添加操作
public static void main(String[] args) {
// 创建 Mylist 实例对象 并声明为 Final jdk1.7中必须声明 jdk1.8之后,当成员内部类 调用成员属性时,默认也是 final
final MyList ml = new MyList();
ExecutorService es = Executors.newFixedThreadPool(2);
es.submit(new Runnable() {
@Override
public void run() {
ml.add("D");
}
});
es.submit(new Runnable() {
@Override
public void run() {
ml.add("E");
}
});
for (int i = 0; i < ml.size(); i++) {
System.out.println(ml.get(i));
}
}
}
class MyList {
// 数组容器
private Object elements[] = {
"A", "B", "C", "", ""
};
// 当前数组内的有效 长度
int size;
// Lock 锁
private Lock locker;
MyList() {
size = 3;
locker = new ReentrantLock();
}
public void add(Object obj) {
locker.lock();
try {
elements[size] = obj;
size++;
} finally {
locker.unlock();
}
}
public Object get(int i) {
return elements[i];
}
public int size() {
return size;
}
}
本案例中的 成员变量 ml 为什么在 jdk 1.7中要加上 final 呢? 再举个例子
/**
* 为什么 要加上 final 呢?jdk 1.7 之前 当成员内部类需要访问成员变量时 需要加上final 目的是 使 内部类对象的生命周期大于 目标方法的局部变量的生命周期.
*/
public class TestFinal {
public static void main(String[] args) throws Exception {
Outer outer = new Outer();
// 先执行 outMethod 方法
outer.outerMethod();
// 再执行 p.print() 如果 jdk 1.7不加 final 就会出现 打印时 没有 变量 a ,所以出错.声明周期不一样了.
outer.p.print();
}
}
class Outer {
PrintTest p;
/*
/ jdk 1.7 之前 当成员内部类需要访问成员变量时 需要加上final 目的是 使 内部类对象的生命周期大于 目标方法的局部变量的生命周期.
*
* 但 是 jdk 1.8 之后就不用加 final 了
*
* final修饰符可省略
内部类在访问 它所在的方法中的变量时 该变量必须是常量(被final修饰的)
jdk1.8之后 省略了final修饰符 但是本质上该变量还是final的
*/
public void outerMethod() throws Exception {
final int a = 10;
// 成员内部类
class InnerTest implements PrintTest{
@Override
public void print() throws Exception {
System.out.println("a is " + a);
}
}
p = new InnerTest();
}
// 接口
interface PrintTest {
void print() throws Exception;
}
}
由此大家也许知道了为什么要加上 final了吧,再拉回到我们的MyList案例中,大家都知道ArrayList中的add方法实际上是没有加锁的,所以当并发访问时 也许会出现错误,当一个线程执行add方法时,另一个线程也抢到了临界资源,那么也会执行add操作,所以会造成size错误,所以在本案例中加了锁,目的是演示临界资源的问题,虽然解决了锁的问题,但是这还不是最优方法,因为对性能会有一定的影响,下篇文章中会详细介绍如何得到高效率的线程安全集合List Set Map,并且会对其源码进行解析.