在前面【《从Java面试题来看源码》-LinkedBlockingQueue 源码分析】的文章中,我们看到有一个toString
方法是这样的:
public String toString() {
//
return Helpers.collectionToString(this);
}
为什么要这样呢?
分析
`使用Helpers类,来输出字符串,与Java8不同。
Helpers类用于并发包输出字符串,该类只在输出数组的时候获取锁,而不是在toString中获取锁`
Java11中用到了Helpers.collectionToString(this)的方式输出字符串,并且与Java8是不同的。
先看看Java11
中Helpers类的写法:
/**
* Collection.toString() 的一种实现,适用于有锁的类。
* 代替了以前在整个toString()过程中加锁,或者在每次调用Iterator.next()的时候加锁
* 该方法只在调用toArray()期间加锁,以减少其他线程对访问集合时产生的影响
* 并且遵循在加锁期间,不调用任何外部代码
*/
static String collectionToString(Collection<?> c) {
//这里toArray会加锁
final Object[] a = c.toArray();
final int size = a.length;
if (size == 0)
return "[]";
int charLength = 0;
// Replace every array element with its string representation
for (int i = 0; i < size; i++) {
Object e = a[i];
// Extreme compatibility with AbstractCollection.toString()
String s = (e == c) ? "(this Collection)" : objectToString(e);
a[i] = s;
charLength += s.length();
}
return toString(a, size, charLength);
}
/**
* 与 Arrays.toString() 类似,但调用者保证 size > 0,索引为 0 <= i < size 的每
* 个元素都是非空 String,charLength 是输入 String 的长度之和。
*/
static String toString(Object[] a, int size, int charLength) {
// assert a != null;
// assert size > 0;
// Copy each string into a perfectly sized char[]
// Length of [ , , , ] == 2 * size
final char[] chars = new char[charLength + 2 * size];
chars[0] = '[';
int j = 1;
for (int i = 0; i < size; i++) {
if (i > 0) {
chars[j++] = ',';
chars[j++] = ' ';
}
String s = (String) a[i];
int len = s.length();
s.getChars(0, len, chars, j);
j += len;
}
chars[j] = ']';
// assert j == chars.length - 1;
return new String(chars);
}
整个过程中就是将当前状态队列的元素
进行拼接输出,而不会影响到其他线程操作队列,只是在通过toArray()
获取队列元素的时候进行加锁。
看看Java8是怎么写的:
public String toString() {
fullyLock();
try {
Node<E> p = head.next;
if (p == null)
return "[]";
StringBuilder sb = new StringBuilder();
sb.append('[');
for (;;) {
E e = p.item;
sb.append(e == this ? "(this Collection)" : e);
p = p.next;
if (p == null)
return sb.append(']').toString();
sb.append(',').append(' ');
}
} finally {
fullyUnlock();
}
}
Java8中,toString输出字符串在前面加了一个锁fullyLock()
,fullyLock()
使用ReentrantLock
对put和take、poll分别加锁。
Java8会在整个toString的拼接过程中,对队列进行加锁,会影响性能。
private final ReentrantLock takeLock = new ReentrantLock();
private final ReentrantLock putLock = new ReentrantLock();
void fullyLock() {
putLock.lock();
takeLock.lock();
}
总结
简单点说
Java8中的toString,就如同一个人干活,一群人歇下来看着他干完完,比较粗暴。
Java11中是:
toString():我要输出了,队列你先把当前值给我
队列:放下原来的事,把toString()要的数据准备好,给了toString()后,继续做原来的事
toString:我可以输出了
我想这应该是很好理解的。