好的,我有两个班,Lock和Worker。
所有Lock都是创建工作者对象。现在在Worker中两个线程将运行。我创建了对象来充当锁,这样两个线程就可以同时处理两个方法中的一个,而不必等待使用共享的内部锁。所有方法都是添加到两个列表中的一个,从1到1000,所以list1和list基本上都有(或者应该)列表中有2000个数字。
我想要理解的是,如果我不使用锁,为什么在此过程中这会给我一个ArrayIndexOutOfBoundsException
。我所做的只是增加一个arraylist,为什么它超出界限?如果两个线程同时读取相同大小的arraylist,arraylist是否会打开一个位置,而两个线程都试图同时放入两个数字?注意:如果我取消注释同步锁定,这很好,我只想知道幕后发生了什么。
public class Lock {
public static void main(String[] args) {
Worker x = new Worker();
try {
x.main();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class Worker {
private Random random = new Random();
private Object lock1 = new Object();
private Object lock2 = new Object();
private List<Integer> list1 = new ArrayList<Integer>();
private List<Integer> list2 = new ArrayList<Integer>();
public void main() throws InterruptedException {
System.out.println("Starting...");
long start = System.currentTimeMillis();
Thread t1 = new Thread(new Runnable() {
public void run() {
process();
}
});
Thread t2 = new Thread(new Runnable() {
public void run() {
process();
}
});
t1.start();
t2.start();
try {
t1.join();
t2.join();
} catch(InterruptedException e) {
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("Time take: " +(end - start) + " ms");
System.out.println("List1: " + list1.size() +"; List2: " + list2.size());
}
//Without synchronized it will not fill the entire list
public void stageOne() {
//synchronized (lock1)
{
try {
Thread.sleep(1);
} catch(InterruptedException e) {
e.printStackTrace();
}
list1.add(random.nextInt(100));
}
}
public void stageTwo() {
//synchronized (lock2)
{
try {
Thread.sleep(1);
} catch(InterruptedException e) {
e.printStackTrace();
}
list2.add(random.nextInt(100));
}
}
public void process(){
for (int i = 0; i < 1000; i++) {
stageOne();
stageTwo();
}
}
}
答案 0 :(得分:1)
ArrayList
不是线程安全的,没有synchronized
块,两个线程都在list1
和list2
中添加元素。
可能会发生ArrayList
之一的支持数组达到其全部容量,并且在调整大小时,其中一个线程正在尝试向已经完整的数组添加元素,从而抛出异常。
答案 1 :(得分:0)
我想了解的是,如果我不使用锁,为什么在此过程中会给我一个
ArrayIndexOutOfBoundsException
。
我们无法准确回答这个问题,我们只能推测。 ArrayList
不是线程安全的类。这就是您需要使用同步的原因。
请注意,此实现未同步。如果多个线程同时访问ArrayList实例,并且至少有一个线程在结构上修改了列表,则必须在外部进行同步。
如果你不使用同步,事情就会爆发,在这种情况下,你发现它们已经发生了。
为这个特定情况推测导致此异常的原因是没有用的,因为它本质上是未定义的。 重要的是要理解,如果没有同步,每个线程相互之间的动作都没有指定的顺序。因此,无法以逻辑方式推断导致异常的事件序列。
例如,假设t1
采取两项措施:
actionA();
actionB();
如果没有同步,t2
完全有权查看actionB
而非actionA
。
在某些情况下,线程交错会导致一个人尝试访问超出范围的索引。这就是为什么需要同步的原因。