初探LinkedList线程安全问题(一)
2009-10-28 19:08
417 查看
Java中LinkedList是线程不安全的,那么如果在多线程程序中有多个线程访问LinkedList的话会出现什么问题呢?
抛出ConcurrentModificationException
JDK代码里,ListItr的add(), next(), previous(), remove(), set()方法都会跑出ConcurrentModificationException。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
代码中,modCount记录了LinkedList结构被修改的次数。Iterator初始化时,expectedModCount=modCount。任何通过Iterator修改LinkedList结构的行为都会同时更新expectedModCount和modCount,使这两个值相等。通过LinkedList对象修改其结构的方法只更新modCount。所以假设有两个线程A和B。A通过Iterator遍历并修改LinkedList,而B,与此同时,通过对象修改其结构,那么Iterator的相关方法就会抛出异常。这是相对容易发现的由线程竞争造成的错误。
通过LinkedList对象修改其结构
如果两个线程都是通过LinkedList对象修改其结构,会发生什么呢?我们先看一下JDK中LinkedList的数据结构。
![](http://p.blog.csdn.net/images/p_blog_csdn_net/Scholfield/EntryImages/20091028/绘图1.jpg)
这是一个双向循环链表。header的作用就是能快速定位链表的头和尾。图中,“n”表示next,“p”表示previous。header的n指向first element;p指向last element。当一个线程A调用LinkedList的addFirst方法时(假设添加节点“4”),它首先更新“4”的n和p,n->3, p->header。此为第一步。第二步,更新节点“3”和herder的n和p,3n不变, 3p->4, headern->4, headerp不变。假设两个线程A,B同时调用addFirst(4), addFirst(5),会发生什么呢?很可能4,5的n指向3,p都指向header。(这里不是很确定,没有写代码测试,貌似无法从LinkedList获得entry)。也可能addFirst后,紧接着发现getFirst已经不是刚刚加入的元素。
package linkedlist;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.Executors;
public class Test {
private final LinkedList<Integer> list = new LinkedList<Integer>();
public Test() {
Thread add1 = new Thread(new Runnable() {
@Override
public void run() {
int number = 0;
while (true) {
if (number == 10)
number = 0;
// System.out.println("Writing " + number);
list.addFirst(number);
int first = list.getFirst();
if (first != number) {
System.err.println("ERROR!!! " + number + " " + first);
System.exit(-1);
}
number++;
System.out.flush();
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread add2 = new Thread(new Runnable() {
@Override
public void run() {
int number = 100;
while (true) {
if (number > 119) {
number = 100;
}
// System.out.println("Writing " + letter);
// System.out.flush();
list.addFirst(number);
int first = list.getFirst();
if (first != number) {
System.err.println("ERROR!!! " + number + " " + first);
System.exit(-1);
}
number++;
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Executors.newCachedThreadPool().execute(add1);
Executors.newCachedThreadPool().execute(add2);
// Executors.newCachedThreadPool().execute(read2);
}
/**
* @param args
*/
public static void main(String[] args) {
Test test = new Test();
}
}
程序输出:
ERROR!!! 0 100
总结一下,线程安全问题是由多个线程同时写或同时读写同一个资源造成的。这里只是通过分析LinkedList的相关源码浅显的分析,下一步就要考虑如何解决或避免线程竞争了。
抛出ConcurrentModificationException
JDK代码里,ListItr的add(), next(), previous(), remove(), set()方法都会跑出ConcurrentModificationException。
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
代码中,modCount记录了LinkedList结构被修改的次数。Iterator初始化时,expectedModCount=modCount。任何通过Iterator修改LinkedList结构的行为都会同时更新expectedModCount和modCount,使这两个值相等。通过LinkedList对象修改其结构的方法只更新modCount。所以假设有两个线程A和B。A通过Iterator遍历并修改LinkedList,而B,与此同时,通过对象修改其结构,那么Iterator的相关方法就会抛出异常。这是相对容易发现的由线程竞争造成的错误。
通过LinkedList对象修改其结构
如果两个线程都是通过LinkedList对象修改其结构,会发生什么呢?我们先看一下JDK中LinkedList的数据结构。
![](http://p.blog.csdn.net/images/p_blog_csdn_net/Scholfield/EntryImages/20091028/绘图1.jpg)
这是一个双向循环链表。header的作用就是能快速定位链表的头和尾。图中,“n”表示next,“p”表示previous。header的n指向first element;p指向last element。当一个线程A调用LinkedList的addFirst方法时(假设添加节点“4”),它首先更新“4”的n和p,n->3, p->header。此为第一步。第二步,更新节点“3”和herder的n和p,3n不变, 3p->4, headern->4, headerp不变。假设两个线程A,B同时调用addFirst(4), addFirst(5),会发生什么呢?很可能4,5的n指向3,p都指向header。(这里不是很确定,没有写代码测试,貌似无法从LinkedList获得entry)。也可能addFirst后,紧接着发现getFirst已经不是刚刚加入的元素。
package linkedlist;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.concurrent.Executors;
public class Test {
private final LinkedList<Integer> list = new LinkedList<Integer>();
public Test() {
Thread add1 = new Thread(new Runnable() {
@Override
public void run() {
int number = 0;
while (true) {
if (number == 10)
number = 0;
// System.out.println("Writing " + number);
list.addFirst(number);
int first = list.getFirst();
if (first != number) {
System.err.println("ERROR!!! " + number + " " + first);
System.exit(-1);
}
number++;
System.out.flush();
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Thread add2 = new Thread(new Runnable() {
@Override
public void run() {
int number = 100;
while (true) {
if (number > 119) {
number = 100;
}
// System.out.println("Writing " + letter);
// System.out.flush();
list.addFirst(number);
int first = list.getFirst();
if (first != number) {
System.err.println("ERROR!!! " + number + " " + first);
System.exit(-1);
}
number++;
try {
Thread.sleep(2);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
Executors.newCachedThreadPool().execute(add1);
Executors.newCachedThreadPool().execute(add2);
// Executors.newCachedThreadPool().execute(read2);
}
/**
* @param args
*/
public static void main(String[] args) {
Test test = new Test();
}
}
程序输出:
ERROR!!! 0 100
总结一下,线程安全问题是由多个线程同时写或同时读写同一个资源造成的。这里只是通过分析LinkedList的相关源码浅显的分析,下一步就要考虑如何解决或避免线程竞争了。
相关文章推荐
- 初探LinkedList线程安全问题
- 初探LinkedList线程安全问题
- 关于ArrayList和LinkedList的索引问题
- Odd Even Linked List问题及解法
- 使用单链表求解约瑟夫环问题 (利用java中的LinkedList)
- Collection容器初探之LinkedList
- Flatten Binary Tree to Linked List问题及解法
- ReverseLinkedList,ReverseLinkedList2,链表反转问题
- [LeetCode] Reverse Linked List I II - 链表翻转问题
- LinkedList基本用法,真得很详细,借鉴了,以后再遇到类似的问题啊,不仅要收集起来博文,更要自己争取写出来!
- Java 集合 LinkedList 需要知道的几个问题
- java LinkedList中的泛型问题
- LinkedList和ArrayList经常被忽略的问题
- Palindrome Linked List问题及解法
- Leetcode之Palindrome Linked List 问题
- 链表的反转问题(Reverse Linked List)
- PAT 1097. Deduplication on a Linked List (25)(链表问题)(链表分段)
- 关于LinkedList和ArrayList的执行效率的问题的区别(测试用例)
- 206. Reverse Linked List(递归法问题)
- Leetcode之Linked List Cycle 问题