Vector线程安全,ArrayList线程不安全问题。

javaweb_coder 2010-06-17 02:59:31
在网上看到的一篇文章,如下:
vector线程安全 ArrayList非线程安全,但却解约系统性能
比如一个 ArrayList 类,在添加一个元素的时候,它可能会有两步来完成:
1. 在 Items[Size] 的位置存放此元素;
2. 增大 Size 的值。
在单线程运行的情况下,如果 Size = 0,添加一个元素后,此元素在位置 0,而且 Size=1;
而如果是在多线程情况下,比如有两个线程,线程 A 先将元素存放在位置 0。但是此时 CPU 调度线程A暂停,线程 B 得到运行的机会。线程B也向此 ArrayList 添加元素,因为此时 Size 仍然等于 0 (注意哦,我们假设的是添加一个元素是要两个步骤哦,而线程A仅仅完成了步骤1),所以线程B也将元素存放在位置0。然后线程A和线程B都继续运行,都增加 Size 的值。
那好,现在我们来看看 ArrayList 的情况,元素实际上只有一个,存放在位置 0,而 Size 却等于 2。这就是“线程不安全”了。



然后我写了测试的例子。
发现都没有抛出异常啊。是不是我写的例子有问题?谁给我个演示ArrayList线程不安全的列子呢?谢谢了。

import java.util.Vector;

//实现Runnable接口的线程
public class HelloThread implements Runnable {
String name;
Vector v;

HelloThread(String name,Vector v) {
this.name = name;
this.v = v;
}

public void run() {
/*
* int sleeptime=(int)(Math.random()*3000); ///A try{ //B
* Thread.sleep(sleeptime); ///C }catch(InterruptedException e){} ///D
*/
System.out.println(name+"start");
v.add(name+".add");
}

public static void main(String args[]) throws InterruptedException {

Vector v = new Vector();

HelloThread hello1 = new HelloThread("hello1",v);
HelloThread hello2 = new HelloThread("hello2",v);
HelloThread hello3 = new HelloThread("hello3",v);

Thread h1 = new Thread(hello1);
Thread h2 = new Thread(hello2);
Thread h3 = new Thread(hello3);
h1.start();
h2.start();
h3.start();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}

for(int i=0;i<v.size();i++){
System.out.println(v.get(i));
}

}
}



import java.util.ArrayList;
import java.util.List;
import java.util.Vector;

//实现Runnable接口的线程
public class HelloThread implements Runnable {
String name;
List v;

HelloThread(String name,List v) {
this.name = name;
this.v = v;
}

public void run() {
System.out.println(name+"start");
v.add(name+".add");
}

public static void main(String args[]) throws InterruptedException {

List v = new ArrayList();

HelloThread hello1 = new HelloThread("hello1",v);
HelloThread hello2 = new HelloThread("hello2",v);
HelloThread hello3 = new HelloThread("hello3",v);

Thread h1 = new Thread(hello1);
Thread h2 = new Thread(hello2);
Thread h3 = new Thread(hello3);
h1.start();
h2.start();
h3.start();

try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e.getMessage());
}

System.out.println("v.size:"+v.size());
for(int i=0;i<v.size();i++){
System.out.println(v.get(i));
}

}
}
...全文
2213 31 打赏 收藏 转发到动态 举报
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
haihuichen 2012-10-31
  • 打赏
  • 举报
回复
import java.util.ArrayList;
import java.util.Vector;

public class 多线程测试 {
public static void main(String[] args) throws Exception {
class B {
public int i = 0;
public void fn() {
System.out.println(i);
i++;
}
}
class TestArrayList extends Thread {
ArrayList al;
TestArrayList(ArrayList al) {
this.al = al;
}
public void run() {
for (int i = 1; i <= 10; i++) {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
al.add("a");
System.out.println(al.size());
}
}
}
class TestObject extends Thread {
B b;
TestObject(B b) {
this.b = b;
}
public void run() {
try {
Thread.sleep(1);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
b.fn();
}
}
B b = new B();
ArrayList al = new ArrayList();
// 不对B类的fn方法添加同步设置的话,下面的输出会乱序
// for (int i = 1; i <= 10; i++)
// new TestObject(b).start();
// 用ArrayList下面语句多执行几次会有可能报错,用Vector就不会
for (int i = 1; i <= 3; i++)
new TestArrayList(al).start();
}
}
javaweb_coder 2010-06-24
  • 打赏
  • 举报
回复
先结贴吧。不喜欢分浪费了。
javaweb_coder 2010-06-20
  • 打赏
  • 举报
回复
有没有人用程序模拟下ArrayList是线程不安全的啊?
mongo01 2010-06-20
  • 打赏
  • 举报
回复
回帖有分
dracularking 2010-06-20
  • 打赏
  • 举报
回复
[Quote=引用 25 楼 javaweb_coder 的回复:]

有没有人用程序模拟下ArrayList是线程不安全的啊?
[/Quote]
1楼的程序,睡眠间隔调少一点,多跑几遍就出来了
sqskg 2010-06-20
  • 打赏
  • 举报
回复
我只是知道vector是线程安全的和Arraylist是不安全的,但不知细节是怎样
ChDw 2010-06-18
  • 打赏
  • 举报
回复
用多线程其实模拟也不太方便,因为不容易重现的。但是你单步跟踪是最容易出现错误的。
final List l = new ArrayList(1);
Thread t = new Thread() {
public void run() {
l.add("X");
}
};
t.start();
Thread t2 = new Thread() {
public void run() {
l.add("X");
}
};
t2.start();
t.join();
t2.join();
System.out.println(l.size());


你在两个线程的add方法中增加断点,并开始调试。你看Debug窗口应该两个线程都会停止了。你单步其中一个线程进入public void ensureCapacity(int minCapacity)方法中,在这个方法返回前就不要再继续单步了。然后换到另外一个线程中继续运行,再回到第一个线程中继续运行
结果是会报ArrayIndexOutOfBoundsException异常的
dracularking 2010-06-18
  • 打赏
  • 举报
回复
以上一样的程序多试几次就出现了,另外关于Vector的thread-safety

所有的Vector的方法对它们自己而言都是synchronized的,所以如果只要同步单个方法,自己额外添加的同步措施就失去必要了。如果有几个方法需要调用,且它们互相存在依赖,比如vec.get(vec.size()-2),是要得到倒数第二个元素,那么就必须加上自己的同步措施,因为否则的话,vector有可能在 vec.size() 和 vec.get() 之间发生改变
W8746988 2010-06-18
  • 打赏
  • 举报
回复
http://hi.csdn.net/invite.php?u=3370434&c=9cb528505d2f42a0 加好友
dracularking 2010-06-18
  • 打赏
  • 举报
回复
ArrayList线程不安全的表现是这样的:
hello3start
hello3 list size is 1
hello1start
hello1 list size is 2
hello2start
hello2 list size is 3
hello3 list size is 4
hello1 list size is 5
hello2 list size is 4
hello3 list size is 6
hello1 list size is 8
hello2 list size is 7
hello1 list size is 9
hello3 list size is 10
hello2 list size is 9

加了12次,但size却只有10
dracularking 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 3 楼 javaweb_coder 的回复:]
hello3 list size is 15
hello2 list size is 13
hello2 list size is 17
hello3 list size is 18
hello1 list size is 17

Vector 不是说线程安全的吗?怎么还会出现上面的结果??
[/Quote]
1楼的不算线程不安全,ArrayList会线程不安全,但不是这样
你这个和1楼一样也不算线程不安全,加了18次,size是18,恰恰是线程安全的表现,只不过是待两个线程都add完了之后才调的size(),所以都是17,跳过了16
zhushoujun 2010-06-18
  • 打赏
  • 举报
回复
内部机制,难!不过,vector确实是比ArrayList安全
fly2749 2010-06-18
  • 打赏
  • 举报
回复
[Quote=引用 1 楼 dr_lou 的回复:]
hello2 list size is 12
hello1 list size is 12
[/Quote]

借用楼主的帖子问下dr_lou,这样的输出是不是代表两个线程会同时对一个集合进行重复赋值,即同时操作,这样也就意味着不安全了吧,也就是线程和线程之间的不安全。
Kanepan 2010-06-18
  • 打赏
  • 举报
回复

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadSafeTest implements Callable<Boolean> {

private List<String> list;

public ThreadSafeTest(List<String> list) {
this.list = list;
}

@Override
public Boolean call() {
list.add(Thread.currentThread().getName());
return true;
}

public static void main(String[] args) {
List<String> list = new ArrayList<String>(); // not safe
// List<String> list = new Vector<String>(); // safe
ExecutorService e = Executors.newFixedThreadPool(5);
Set<ThreadSafeTest> tasks = new HashSet<ThreadSafeTest>();

long timeStart = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
tasks.add(new ThreadSafeTest(list));
}

try {
e.invokeAll(tasks);
} catch (InterruptedException e2) {
e2.printStackTrace();
}

System.out.println("cost time:"
+ (System.currentTimeMillis() - timeStart) +"ms");
System.out.println("list size:" + list.size());
e.shutdown();
}
}

用arrayList
执行结果
cost time:859ms
list size:99965
用Vector
执行结果
cost time:891ms
list size:100000

有点疑惑为什么执行时间差不多.. 估计时间都耗在线程切换上了。
Kanepan 2010-06-18
  • 打赏
  • 举报
回复
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.Vector;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

public class ThreadSafeTest implements Callable<Boolean> {

private List<String> list;

public ThreadSafeTest(List<String> list) {
this.list = list;
}

@Override
public Boolean call() {
list.add(Thread.currentThread().getName());
return true;
}

public static void main(String[] args) {
List<String> list = new ArrayList<String>(); // not safe
// List<String> list = new Vector<String>(); // safe
ExecutorService e = Executors.newFixedThreadPool(5);
Set<ThreadSafeTest> tasks = new HashSet<ThreadSafeTest>();

long timeStart = System.currentTimeMillis();
for (int i = 0; i < 100000; i++) {
tasks.add(new ThreadSafeTest(list));
}

try {
e.invokeAll(tasks);
} catch (InterruptedException e2) {
e2.printStackTrace();
}

System.out.println("cost time:"
+ (System.currentTimeMillis() - timeStart) +"ms");
System.out.println("list size:" + list.size());
e.shutdown();
}
}


执行结果
cost time:859ms
list size:99965
surelei 2010-06-18
  • 打赏
  • 举报
回复
楼上的,干嘛学我!
ronniegxq 2010-06-18
  • 打赏
  • 举报
回复
线程安全不是这么测试的,只有在高并发的时候才有可能展现线程不安全的因素,那个恐怕要借助loadrunner等工具了,LZ你这样的代码是无法检测线程安全与否的。
surelei 2010-06-18
  • 打赏
  • 举报
回复
线程安全不是这么测试的,只有在高并发的时候才有可能展现线程不安全的因素,那个恐怕要借助loadrunner等工具了,LZ你这样的代码是无法检测线程安全与否的。
宁静-夏天 2010-06-18
  • 打赏
  • 举报
回复
ChDw 的方法在理解问题场景后重现问题真是不错的方法。

只是不知道这样debug方便不。
luolei8624 2010-06-18
  • 打赏
  • 举报
回复
回帖有分`
加载更多回复(10)

62,615

社区成员

发帖
与我相关
我的任务
社区描述
Java 2 Standard Edition
社区管理员
  • Java SE
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧