引言
有没有常被人问到以下几个问题:
ArrayList和Vector有什么区别?
HashMap和HashTable有什么区别?
StringBuilder和StringBuffer有什么区别? 每当听到这样的问题时,答案则无非是来自‘XX面试宝典’的那几句:
ArrayList是非线程安全的,Vector是线程安全的
HashMap是非线程安全的,HashTable是线程安全的
StringBuilder是非线程安全的,StringBuffer是线程安全的 突然发现我们平时用的基本都是非线程安全的,甚至线程安全的有人从来没用过。那么我们是否写的程序都存在安全问题,下面我们将就安全进行一个解释。
安全问题的暴露
在高并发的场景下,我们会利用java的多线程特性提高效率。当多个线程共享一个资源的时候,则会出现是否安全的问题。例如以下代码关于ArrayList的使用场景,我们假设有这么一个线程
package cn.buglife.learn.thread;
import java.util.List;
import java.util.UUID;
import java.util.concurrent.CountDownLatch;
/**
* 定义一个线程(往给定的列表添加100个数据)
*
* Created by zhangjun on 16/6/23.
*/
public class LearnThread implements Runnable {
private List resource;
private CountDownLatch countDownLatch;
public LearnThread(List resource, CountDownLatch countDownLatch) {
this.resource = resource;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
for (int i = 0; i < 100; i++) {
resource.add(UUID.randomUUID().toString());
}
countDownLatch.countDown();
}
}
接下来我们启动100个线程去帮助我们完成资源的录入,理论期望应该资源数组大小为10000
package cn.buglife.learn.thread;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.CountDownLatch;
/**
* Created by zhangjun on 16/6/23.
*/
public class ThreadSafe {
public static void main(String[] args) {
List unSafeList = new ArrayList();
//启动100个线程
int threadCount = 100;
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i=0; i
Thread thread = new Thread(new LearnThread(unSafeList,countDownLatch));
thread.start();
}
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.print(unSafeList.size());
}
}
执行上述程序5次得到的结果如下
资源大小
耗时
9999
119
10000
115
9999
119
9999
114
9999
117
若使用线程安全的Vector呢?
package cn.buglife.learn.thread;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.CountDownLatch;
/**
* Created by zhangjun on 16/6/23.
*/
public class ThreadSafe {
public static void main(String[] args) {
Long time = new Date().getTime();
List safeList = new Vector();
//启动100个线程
int threadCount = 100;
CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i=0; i
Thread thread = new Thread(new LearnThread(safeList,countDownLatch));
thread.start();
}
//主线程等待其他线程全部执行完再执行
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("资源大小:"+safeList.size());
System.out.print("耗时:"+(new Date().getTime()-time));
}
} 上述代码执行结果:
资源大小
耗时
10000
112
10000
118
10000
105
10000
123
10000
125
如何选择
根据以上现象可以看出,非线程安全的ArrayList结构会在多线程使用的过程中随机性得使计算错乱,那么是不是非线程安全得数据结构就不要用了呢?
非线程安全是指多线程操作同一个对象可能会出现一些误差问题。而
线程安全则是指多线程操作同一个对象不会出现问题。 那么又有一个新得问题,是不是非线程安全得是不是就不能在多线程环境下使用了?答案是No,我们需要明确得是:
线程安全问题是针对多线程环境下,多个线程操作同一个对象
鱼和熊掌不可兼得,线程安全得代价就是性能相对得有所下降
非线程安全得对象在独立线程内是不存在问题得
结论 线程安全与非线程安全取决于应用得环境,是取是舍需要根据所处环境决定,切勿盲目下结论。