所谓的非线程安全就是指多个线程操作同一个对象,可能会出现问题(ArrayList),而线程安全指的是多个线程操作同一个对象不会出现问题(Vector),线程安全需要用到同步机制 ,这个也导致了性能的下降。非线程安全≠不安全:非线程安全指的的多个线程操作同一个对象可能会出现问题,并不是在多线程中不能使用ArrayList或者其他的非线程安全的类。只要保证在多线程中操作的不是同一个对象,就可以大胆的使用非线程安全的类。
ArrayList:如果我们创建10个线程,在每个线程中给同一个ArrayList对象添加10个元素,最后ArrayList中的总元素可能不是100个,这就是非线程安全。造成这种情况的原因是:ArrayList的add操作并没有实现同步。也就是没有保证add操作的原子性。一个 ArrayList ,在添加一个元素的时候,它可能会有两步来完成:
1. 在 Items[Size] 的位置存放此元素;
2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。这样就造成了元素的缺失。
Vector:由于vector类对成员方法都加了同步锁(synchronized标志),在多线程的访问过程中,对加锁的方法同时只允许一个线程访问,也就保证了add的原子性,必须在当前线程执行完一个完整的add操作,才允许另一个线程调用add方法。
但是vector类只是相对安全,并不是绝对安全
1.
Vector test = Vector();
.......
.......
for(i =0; i < test.size(); i++)
{
/////////////////在此处当前线程时间片到期
///线程重新获取时间片,但test内元素有可能已经被其他线程修改
System.out.println(test.get(i));
}
Vector所谓的线程安全是指调用Vector类的成员方法时,其他线程不能再访问该Vector对象。但是在调用两个Vector成员方法时,当前线程有可能再完成第一个方法后时间片到期,这时其他线程可以访问该Vector对象,造成调用第二个成员方法的结果可能与预想结果不同。这时为保证线程安全,需要加synchronized。
2.要保证线程绝对安全就必需自己用synchronized把整个代码片段包起来。
synchronized(test)
{
...
}
类似的我们经常用到的场景比如:
synchronized(map){
Object value = map.get(key);
if(value ==null)
{
value =newObject();
map.put(key,value);
}
return value;
}
上面代码中 map 即使是线程安全的也没有用,如果不在外面加synchronized,map.get和map.put之间的代码片段都有可能被其他线程重入,可能出现同一个key对应多个value。
StringBuffer和StringBuilder
当两个线程同时执行sb.append()操作时,StringBuffer可以保证2个操作都被执行,不会其中一个被另一个冲掉,因为append()方法是synchronized的,这样2个append是先后执行的。但是StringBuilder则有可能发生其中一个操作被另一个冲掉的情况,因为2个线程的append()方法不需要获得锁。我觉得,所谓的线程安全,大概也就只能做到这点了,保证StringBuffer里面的方法本身不会因为同步执行而发生错误的结果。但是由于我们的应用中,如果需要控制同步,肯定不止在这个层次,否则,一般都是不需要同步的(局部变量),因此才诞生了StringBuilder类。