class Person extends Thread{ List<String> list; //本方法中,run将被并发调用 public void run(){ Thread t=Thread.currentThread(); //假设集合有2个元素 while(!list.isEmpty()){// a , b 进来
try { Thread.sleep(100);// a, 停下了 , b 随后也停下 } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); }
String one=list.remove(0);//a 执行完了出去了..... (假设b还没醒)然后 a 又进 while 条件了, b 没醒就没执行 remove , b 执行了删除了集合中最后一个,然后 a 又继续删除,然后出问题了
try {
Thread.sleep(100);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}这几行代码完全可以删除。程序的意思是:两个人同时操作一个list(先判断不为空,然后删除第一个元素)。多线程操作可能出现以下特殊情况:当list只剩最后一个时,Person1先判断list不为空,然后线程跳转,Person2判断list也不为空,于是删除第一个元素,此时,线程跳转回去,Person1之前判断过不为空了,于是直接执行remove操作,但list已经没有数据了,所以报错
其实这样子的操作是模拟一个卖火车票的那种情况。
List<String> list;
//本方法中,run将被并发调用
public void run(){
Thread t=Thread.currentThread();
//假设集合有2个元素
while(!list.isEmpty()){// a , b 进来
try {
Thread.sleep(100);// a, 停下了 , b 随后也停下
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
String one=list.remove(0);//a 执行完了出去了..... (假设b还没醒)然后 a 又进 while 条件了, b 没醒就没执行 remove , b 执行了删除了集合中最后一个,然后 a 又继续删除,然后出问题了
System.out.println(t.getName()+one);
}
if(list.isEmpty()){
System.out.println("卖完了!");
}
}
List<String> list;
//本方法中,run将被并发调用
public void run(){
Thread t=Thread.currentThread();
//假设集合有2个元素 ,
while(!list.isEmpty()){// a,b 线程进来了 , b 在这里停下了,
String one=list.remove(0); //a 线程执行了 ,
try {
Thread.sleep(100); //a 停下了, a 执行完又出去了,接着a 进 while,然后因为b没醒,a 执行了,b 突然醒了,然后 a , b 都要 remove 集合中元素,但是集合中只有一个元素,故也会出现异常;
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println(t.getName()+one);
}
if(list.isEmpty()){
System.out.println("卖完了!");
}
}
同一时段一段代码只能有一个线程在跑; 这个也是晕多线程的原因; 你的第二个一进来就 remove (0) 不会出现问题,那是因为可能 执行太快了,不容易看到错误,最后做一个总结: 两个以上的线程操作同一共享资源避免安全隐患必须使用锁(同步代码块 | jdk1.5 锁的新特性);
所以,出现数组下标越界,也是很正常的事情。
这时,可能产生一种情况,并发情况下,有可能一个线程先执行了后续的代码,删除了集合内的元素,
另一个线程后执行后续的代码,仍然要删除集合内的元素,由于集合对象不是线程安全的,并且当前环境中,也米有对象可删,
就会抛出异常了。
明白了?
单个线程不会出这个问题,因为,在一个线程内,调用isEmpty方法后,如果集合里面有对象,那么,后续的代码执行时,它就是有对象的,并且,除了这个线程以外,是没有其他线程会对这个集合进行内容的更改的。
2.除了【1】,楼上说的是正解
import java.util.Collections;
import java.util.List;
import java.util.Vector;public class Yesterday {
public static void main(String[] args) {
Vector<String> list = new Vector<String>(); //
list.add("肉包子");
list.add("菜包子");
list.add("狗不理");
list.add("豆沙包");
list.add("汉堡包");
list.add("汉堡包2");
list.add("汉堡包3");
list.add("汉堡包4");
list.add("汉堡包5");
list.add("汉堡包6");
list.add("汉堡包7");
list.add("汉堡包8");
list.add("汉堡包9");
Object lock = new Object(); Person p1 = new Person(list, lock);
Person p2 = new Person(list, lock); // 两个对象共享同个资源
p1.start();
p2.start();
}
}class Person extends Thread {
private Vector<String> list;
private Object lock; Person(Vector<String> list, Object lock) {
this.list = list;
this.lock = lock;
} // 本方法中,run将被并发调用
public void run() {
while (true) {
synchronized (lock) { if (!list.isEmpty()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
} String one = list.remove(0);
System.out.println(Thread.currentThread().getName() + ":"
+ one);
}//if
}
}//while
}//run
}
//实际上不使用Vector ,线程同步安全的 list ,应该使用集合工具类来转换一个线程安全的list; vector 不能真正解决线程安全,没有效率并且需要同步代码块
List<String> list;
public void run() {
Thread t = Thread.currentThread();
while (true) {
synchronized (list) {
if (!list.isEmpty()) {
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
String one = list.remove(0);
System.out.println(t.getName() + one);
} else {
System.out.println("卖完了");
break;
}
}
}
}
}