本文章向大家介绍为什么(1),Java架构师技术进阶路线图,主要包括为什么(1),Java架构师技术进阶路线图使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 10; i++) {
new Thread(() -> {
for (int j = 0; j < 1000; j++) {
sb.append("a");
}
}).start();
}
// 睡眠确保所有线程都执行完
Thread.sleep(1000);
System.out.println(sb.length());
}
上述业务逻辑比较简单,就是构建一个StringBuilder,然后创建10个线程,每个线程中拼接字符串“a”1000次,理论上当线程执行完成之后,打印的结果应该是10000才对。
但多次执行上面的代码打印的结果是10000的概率反而非常小,大多数情况都要少于10000。同时,还有一定的概率出现下面的异常信息“
Exception in thread “Thread-0” java.lang.ArrayIndexOutOfBoundsException
at java.lang.System.arraycopy(Native Method)
at java.lang.String.getChars(String.java:826)
at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:449)
at java.lang.StringBuilder.append(StringBuilder.java:136)
at com.secbro2.strings.StringBuilderTest.lambda$test$0(StringBuilderTest.java:18)
at java.lang.Thread.run(Thread.java:748)
9007
StringBuilder中针对字符串的处理主要依赖两个成员变量char数组value和count。StringBuilder通过对value的不断扩容和count对应的增加来完成字符串的append操作。
// 存储的字符串(通常情况一部分为字符串内容,一部分为默认值)
char[] value;
// 数组已经使用数量
int count;
上面的这两个属性均位于它的抽象父类AbstractStringBuilder中。
如果查看构造方法我们会发现,在创建StringBuilder时会设置数组value的初始化长度。
public StringBuilder(String str) {
super(str.length() + 16);
append(str);
}
默认是传入字符串长度加16。这就是count存在的意义,因为数组中的一部分内容为默认值。
当调用append方法时会对count进行增加,增加值便是append的字符串的长度,具体实现也在抽象父类中。
public AbstractStringBuilder append(String str) {
if (str == null)
return appendNull();
int len = str.length();
ensureCapacityInternal(count + len);
str.getChars(0, len, value, count);
count += len;
return this;
}
我们所说的线程不安全的发生点便是在append方法中count的“+=”操作。我们知道该操作是线程不安全的,那么便会发生两个线程同时读取到count值为5,执行加1操作之后,都变成6,而不是预期的7。这种情况一旦发生便不会出现预期的结果。
回头看异常的堆栈信息,回发现有这么一行内容:
at java.lang.String.getChars(String.java:826)
对应的代码就是上面AbstractStringBuilder中append方法中的代码。对应方法的源代码如下:
public void getChars(int srcBegin, int srcEnd, char dst[], int dstBegin) {
if (srcBegin < 0) {
throw new StringIndexOutOfBoundsException(srcBegin);
}
if (srcEnd > value.length) {
throw new StringIndexOutOfBoundsException(srcEnd);
}
if (srcBegin > srcEnd) {
throw new StringIndexOutOfBoundsException(srcEnd - srcBegin);
}
System.arraycopy(value, srcBegin, dst, dstBegin, srcEnd - srcBegin);
}
其实异常是最后一行arraycopy时JVM底层发生的。arraycopy的核心操作就是将传入的String对象copy到value当中。
而异常发生的原因是明明value的下标只到6,程序却要访问和操作下标为7的位置,当然就跑异常了。
那么,为什么会超出这么一个位置呢?这与我们上面讲到到的count被少加有关。在执行str.getChars方法之前还需要根据count校验一下当前的value是否使用完毕,如果使用完了,那么就进行扩容。append中对应的方法如下:
ensureCapacityInternal(count + len);
ensureCapacityInternal的具体实现:
private void ensureCapacityInternal(int minimumCapacity) {
// overflow-conscious code
if (minimumCapacity - value.length > 0) {
value = Arrays.copyOf(value,
最后
面试是跳槽涨薪最直接有效的方式,马上金九银十来了,各位做好面试造飞机,工作拧螺丝的准备了吗?
掌握了这些知识点,面试时在候选人中又可以夺目不少,暴击9999点。机会都是留给有准备的人,只有充足的准备,才可能让自己可以在候选人中脱颖而出。
如果你需要这份完整版的面试笔记,只需你多多支持我这篇文章。
PS:本篇的内容有点多,请大家耐心看完,文章后半段有小惊喜哈~
计组原理深入、文件系统深入、网络IO模型、JVM模型深入、单机锁实现深入
网络通信原理、四层负载均衡、七层负载均衡、单机并发方案、集群并发方案
ZOOKEEPER、ETCD、KEEPALIVED、HAPROXY
容器化、容器与DEVOPS、容器编排KUBERNETES、监控与日志
NETTY原理、手写NETTY-RPC、DUBBO实现RPC、GRPC、THRIFT、RESTFUL
PS:这份学习路线图和最后的配套学习笔记都是完整版的,请大家一定要耐心看完,
MEMCACHED原理、REDIS缓存原理、可靠性实现、代理层实现、自主集群实现
ACTIVEMQ、RABBITMQ、ROCKETMQ、KAFKA、消息协议
LUCENE、SOLR、ELASTICSEARCH、LOGSTASH、KIBANA
FASTDFS、HDFS、HBASE、TIDB / NEO4J /INFLUXDB、MONGODB
手写分布式、REDIS分布式、ZK分布式、ETCD分布式、DB分布式、场景与方案选择
2/3PC方案、TCC方案、柔性事务、最终一致、可靠消息、最大努力通知、SAGA、SEATA落地
NACOS、APPOLO、SPRINGCLOUDCONFIG
CONSUL、EUREKA、NACOS
SPRINGCLOUD、ZUUL、NGINX、OPENRESTY、KONG、GATEWAY
WEBCLIENT、HTTPCLIENT、OKHTTP、RESTTEMPL ATE、FEIGN/OPENFEIGN(响应式)
负载均衡算法、服务端负载均衡、客户端负载均衡、RIBBON、SPINGCLOUDLOADBALANCER
SENTINEL、HYSTRIX、熔断、降级、限流
全民抢红包的设计、12306优化设计、微博好友互动设计、共享推送设计、配送优化设计、双十一流量洪峰优化设计、电商过亿日活接入设计
具体项目内容如何学习,不做过多赘述!
阿里面试面试题:链接:https://pan.baidu.com/s/1ryi6EMUEjZvlaRGEnipryw
提取码:tn3g