线程安全,是一个多线程编程概念。当多线程程序并行执行时,通过同步加锁保护机制保证每个线程都能完全正确的执行,避免数据出现污染等意外情况。反之,则是非线程安全,当多线程程序执行时,由于没有采取同步机制,很容易互相影响导致数据出现异常形成脏数据。
相爱相杀的例子:
线程安全 | 非线程安全 |
Vector | ArrayList |
HashMap | HashTable |
StringBuilder | StringBuffer |
通过以下代码体会下线程的安全与非安全。示例是通过创建多线程,并在线程中向主线程中定义的list对象添加值,最后输出list大小,为了保证演示效果,定义1000个线程,每个子线程向list集合添加100个对象,那么理论上最终list的大小就是1000*100=100000,实际结果运行以下代码查看。
public class SafeThread {
public static void main(String[] args) {
long start = System.currentTimeMillis();
//多次执行主线程
for (int i = 0; i < 10; i++) {
//定义一个非安全线程的对象
List<Object> list = new ArrayList<Object>();
//定义一个安全线程的对象
// List<Object> list = new Vector<Object>();
int thread_count = 1000;
CountDownLatch countDownLatch = new CountDownLatch(thread_count);
for (int t = 0; t < thread_count; t++) {
//启动一个arraylist子线程
Thread thread_arrayList = new Thread(new MyThreadImpl(list, countDownLatch));
thread_arrayList.start();
}
//CountDownLatch知识点:通过子线程的计数监视是否全部子线程已经完成,全部子线程完成后主线程才继续向下执行。
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
//输出对象长度
System.out.println("[" + (i + 1) + "]对象大小 : " + list.size());
}
long end = System.currentTimeMillis();
System.out.println("程序执行时间:" + (end - start) + "ms");
}
//内部类:线程处理程序
static class MyThreadImpl implements Runnable {
List<Object> list;
CountDownLatch countDownLatch;
//主线通过构造函数传递参数至子线程
public MyThreadImpl(List<Object> list, CountDownLatch countDownLatch) {
this.list = list;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
//子线程业务处理
for (int k = 0; k < 100; k++) {
list.add(new Object());
}
//CountDownLatch知识点:通知主线程子线程处理完成,没执行一次CountDownLatch-1。
countDownLatch.countDown();
}
}
}
ArrayList运行结果:
[1]对象大小 : 97744
[2]对象大小 : 99855
[3]对象大小 : 99996
[4]对象大小 : 100000
[5]对象大小 : 99573
[6]对象大小 : 99613
[7]对象大小 : 99429
[8]对象大小 : 99302
[9]对象大小 : 99488
[10]对象大小 : 99524
程序执行时间:2016ms
非线程安全的运行结果,只对了一个,时不时还抛出java.lang.ArrayIndexOutOfBoundsException异常,如果降低一位线程数量正确率就会高很多。
将ArrayList换成安全的Vector,运行结果:
[1]对象大小 : 100000
[2]对象大小 : 100000
[3]对象大小 : 100000
[4]对象大小 : 100000
[5]对象大小 : 100000
[6]对象大小 : 100000
[7]对象大小 : 100000
[8]对象大小 : 100000
[9]对象大小 : 100000
[10]对象大小 : 100000
程序执行时间:1359ms
通过以上示例可以直观感受到线程安全与非安全的区别,但是这不代表着以后都不使用非线程安全的对象了,那么什么时候使用什么对象?这就需要判断我们对线程安全的要求和操作对象是同一个还是各自线程专属的,示例中我们始终操作的都是主线程中创建的同一个list集合。另外,非线程安全的性能好于安全的。