《从Java面试题看源码》-Java11中的toString与Java8的区别
在前面【《从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:我可以输出了
我想这应该是很好理解的。
开发者涨薪指南 48位大咖的思考法则、工作方式、逻辑体系