1. element()、contains()、indexOf()、lastIndexOf()、set()、push()
E element() :获取第一个元素,调用 getFirst(); boolean contains(Object o):是否包含某个元素,底层调用 indexOf(o) 来判断 int indexOf(Object o):前序循环遍历查找元素,找到返回下标,找不到返回 -1 int lastIndexOf(Object o):后序循环遍历查找元素,找到返回下标,找不到返回 -1 E set(int index, E element):替换指定位置的值,返回旧值,底层调用node(int index) 找到对应的节点,设置即可 void push(E e):在最前面添加一个元素,底层调用 addFirst(E e)
import java. util. LinkedList;
public class LinkedListTest01 {
public static void main ( String[ ] args) {
LinkedList< Integer> linkedList = new LinkedList < > ( ) ;
linkedList. add ( 10 ) ;
linkedList. add ( 20 ) ;
linkedList. add ( 30 ) ;
linkedList. add ( 30 ) ;
linkedList. add ( 20 ) ;
linkedList. add ( 10 ) ;
System. out. println ( "now LinkedList: " + linkedList) ;
System. out. println ( "linkedList.element() 相当于 getFirst(): " + linkedList. element ( ) ) ;
System. out. println ( "inkedList.contains(20): " + linkedList. contains ( 20 ) ) ;
System. out. println ( "linkedList.indexOf(20)" + linkedList. indexOf ( 20 ) ) ;
System. out. println ( "linkedList.lastIndexOf(20)" + linkedList. lastIndexOf ( 20 ) ) ;
System. out. println ( "linkedList.set(1, 200): " + linkedList. set ( 1 , 200 ) ) ;
System. out. println ( "now LinkedList: " + linkedList) ;
linkedList. push ( 50 ) ;
System. out. println ( "push(50), now LinkedList: " + linkedList) ;
}
}
2. LinkedList 的线程安全问题
2.1 先抛问题,LinkedList 是否线程安全?
package cn. cerish. container. collection. linkedList;
import java. util. LinkedList;
import java. util. concurrent. CountDownLatch;
import java. util. concurrent. LinkedBlockingDeque;
import java. util. concurrent. ThreadPoolExecutor;
import java. util. concurrent. TimeUnit;
public class ThreadSafeTest {
public static void main ( String[ ] args) throws InterruptedException {
LinkedList< Integer> linkedList = new LinkedList < > ( ) ;
CountDownLatch countDownLatch = new CountDownLatch ( 10 ) ;
System. out. println ( "now linkedList: " + linkedList) ;
System. out. println ( "now linkedList.size() : " + linkedList. size ( ) ) ;
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor ( 5 , 10 , 0 L, TimeUnit. SECONDS,
new LinkedBlockingDeque < > ( 100 ) ) ;
for ( int i = 1 ; i <= 10 ; i++ ) {
threadPoolExecutor. execute ( ( ) - > {
for ( int j = 1 ; j <= 10 ; j++ ) {
linkedList. add ( j) ;
} ;
countDownLatch. countDown ( ) ;
} ) ;
}
countDownLatch. await ( ) ;
System. out. println ( "now linkedList.size(): " + linkedList. size ( ) ) ;
System. out. println ( "now linkedList: " + linkedList) ;
}
}
问题出来,添加个数变少,还报 null 异常,逐个分析 1、size 为什么会变少?
linkLast() 可以看到,若线程AB同时执行到size++这一步,虽然size 有 transient修饰符,但size++并不是原子操作,加上线程之间的变量并不透明(类似volatile),AB可能拿到的是同一个值,比如10,此时A线程10++为11,B线程10++也为11,所以造成了 size < 100 的情况
public boolean add ( E e) {
linkLast ( e) ;
return true ;
}
void linkLast ( E e) {
final Node< E> l = last;
final Node< E> newNode = new Node < > ( l, e, null) ;
last = newNode;
if ( l == null)
first = newNode;
else
l. next = newNode;
size++ ;
modCount++ ;
}
2、为什么出现 null 异常?
要知道的是,若不调用显示 linkedList,是不会显示 null 异常的(可自行测试)。 那么就可以说明,是在 LinkedList 显示的时候报错,那就应该去看 toString()方法,最可能报错的地方就是 遍历for(;;),看报错信息也是定位到 LinkedList 的 next 方法,那是不是因为某一个元素的值为 null 呢?
Exception in thread "main" java. lang. NullPointerException
at java. util. LinkedList$ListItr. next ( LinkedList. java: 893 )
at java. util. AbstractCollection. toString ( AbstractCollection. java: 461 )
at java. lang. String. valueOf ( String. java: 2994 )
at java. lang. StringBuilder. append ( StringBuilder. java: 131 )
at cn. cerish. container. collection. linkedList. ThreadSafeTest. main ( ThreadSafeTest. java: 25 )
public String toString ( ) {
Iterator< E> it = iterator ( ) ;
if ( ! it. hasNext ( ) )
return "[]" ;
StringBuilder sb = new StringBuilder ( ) ;
sb. append ( '[' ) ;
for ( ; ; ) {
E e = it. next ( ) ;
sb. append ( e == this ? "(this Collection)" : e) ;
if ( ! it. hasNext ( ) )
return sb. append ( ']' ) . toString ( ) ;
sb. append ( ',' ) . append ( ' ' ) ;
}
}
回看 add(),假设现在的链表有两个元素1,2,若此时A线程进来,知道到 last = 2,跳转到B线程,检测到 last = 2,执行完整个整个add(),此时B线程的链条为1,2,4,此时跳转到A线程,执行完add(),此时链条会覆盖元素4,变为1,2,3。 而这里并不会包任何错误,只是size为4,链条长度为3。等到执行显示LinkedList时,调用hasNext(),nextIndex = 3,size = 4,返回true,再调用next()返回null,在执行null.item()则报 NullPointerException,这也刚好解释为何不调用不报错。
2.2 线程安全问题解决
使用 Collections.synchronizedList(List list) 解决
import java. util. Collections;
import java. util. LinkedList;
import java. util. List;
import java. util. concurrent. CountDownLatch;
import java. util. concurrent. LinkedBlockingDeque;
import java. util. concurrent. ThreadPoolExecutor;
import java. util. concurrent. TimeUnit;
import java. util. concurrent. atomic. AtomicInteger;
public class ThreadSafeResolveTest {
public static void main ( String[ ] args) throws InterruptedException {
ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor ( 5 , 10 , 0 L, TimeUnit. SECONDS,
new LinkedBlockingDeque < > ( 100 ) ) ;
CountDownLatch countDownLatch = new CountDownLatch ( 10 ) ;
List< Integer> linkedList = Collections. synchronizedList ( new LinkedList < Integer> ( ) ) ;
System. out. println ( "now linkedList:" + linkedList) ;
System. out. println ( "now linkedList.size():" + linkedList. size ( ) ) ;
for ( int i = 1 ; i <= 10 ; i++ ) {
threadPoolExecutor. execute ( ( ) - > {
for ( int j = 1 ; j <= 10 ; j++ ) {
linkedList. add ( j) ;
} ;
countDownLatch. countDown ( ) ;
} ) ;
}
countDownLatch. await ( ) ;
System. out. println ( "now linkedList.size():" + linkedList. size ( ) ) ;
System. out. println ( "now linkedList:" ) ;
AtomicInteger num = new AtomicInteger ( ) ;
linkedList. forEach ( e - > {
System. out. print ( e + "\t" ) ;
if ( num. incrementAndGet ( ) >= 10 ) {
num. set ( 0 ) ;
System. out. println ( ) ;
}
} ) ;
}
}
Collections.synchronizedList(List list):如名所示,创建了一个 加锁的列表synchronizedList,调用super(list) 初始化SynchronizedCollection,执行 mutex = this,在需要同步的代码里,加锁synchronized (mutex),实现了锁方法公用。