要处理多个订单,并对相同订单号的订单进行数量统计,思路是加锁不同的订单号不进行加锁,相同的订单号串行执行。代码如下:
public class LockDemo {
private static ConcurrentHashMap<String,Integer> total = new ConcurrentHashMap<>();
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
while (true){
String riskNO = "RSK000";
int count = 0;
List<Risk> list = new ArrayList<>();
for (int i = 0; i < 1000; i++) {
Risk risk = new Risk();
risk.setName("课程名称"+i);
if(i % 100 == 0){
++count;
}
risk.setRiskNO(riskNO + count);
list.add(risk);
}
for (Risk risk : list) {
service.submit(()->{
show(risk.getRiskNO());
});
}
TimeUnit.SECONDS.sleep(2);
System.out.println("总数统计:"+total);
total.clear();
}
}
public static void show(String riskNO){
System.out.println("riskNO number is "+riskNO+" is hashCode is "+System.identityHashCode(riskNO));
synchronized (riskNO){
Integer integer = total.putIfAbsent(riskNO, 1);
if(integer != null){
integer++;
total.put(riskNO,integer);
}
}
}
}
@Data
class Risk{
private String riskNO;
private String name;
}
结果应该是所有的订单数量应该都是100才正确,结果如下:
总数统计:{RSK0001=85, RSK0002=85, RSK0003=94, RSK0004=85, RSK0005=90, RSK0006=93, RSK0007=88, RSK00010=90, RSK0008=89, RSK0009=86}
总数统计:{RSK0001=99, RSK0002=87, RSK0003=91, RSK0004=91, RSK0005=87, RSK0006=77, RSK0007=89, RSK00010=85, RSK0008=86, RSK0009=93}
总数统计:{RSK0001=99, RSK0002=92, RSK0003=99, RSK0004=99, RSK0005=92, RSK0006=94, RSK0007=95, RSK00010=78, RSK0008=88, RSK0009=91}
添加代码:System.out.println("riskNO number is "+riskNO+" is hashCode is "+System.identityHashCode(riskNO)); 打印String的hashcode
riskNO number is RSK0001 is hashCode is 951484302
riskNO number is RSK0001 is hashCode is 474912117
riskNO number is RSK0001 is hashCode is 869734007
riskNO number is RSK0001 is hashCode is 1853399740
riskNO number is RSK0001 is hashCode is 1080609824
riskNO number is RSK0001 is hashCode is 772801250
riskNO number is RSK0001 is hashCode is 687386865
riskNO number is RSK0001 is hashCode is 1468964324
打印的hashCode都不相同,说明传递的参数都不是同一个对象
解决方法:
方法1和方法2这两种都可以。
String.intern()
在jdk1.6中,将此String对象添加到常量池中,然后返回这个String对象的引用(此时引用的串在常量池)。
在jdk1.7中,放入一个引用,指向堆中的String对象的地址,返回这个引用地址(此时引用的串在堆)。
在上面的代码中intern(),将所有的相同的字符串常量指向了同一个地址。
原因解析
代码中参数拼接之后为什么会变成不同的对象呢?
Java jdk对String字符串拼接做了优化,变量直接的拼接归根结底是Stringbuilder的append方法,最后调用StringBuilder的toString方法转换成String串,传递到方法中的参数是不同的堆中的对象
public class ForTest {
public static void main(String[] args) {
String a = "0";
for (int i = 1; i < 10; i++) {
a = a + "i";
}
System.out.println(a);
}
}
字节码文件以及注释
0 ldc #2 <0>
2 astore_1
3 iconst_1
4 istore_2
5 iload_2
6 bipush 10
8 if_icmpge 37 (+29)
11 new #3 <java/lang/StringBuilder> //创建StringBuilder
14 dup
15 invokespecial #4 <java/lang/StringBuilder.<init>> //初始化
18 aload_1
19 invokevirtual #5 <java/lang/StringBuilder.append> //字符串拼接
22 ldc #6 <i>
24 invokevirtual #5 <java/lang/StringBuilder.append>
27 invokevirtual #7 <java/lang/StringBuilder.toString>//toString方法
30 astore_1
31 iinc 2 by 1
34 goto 5 (-29)
37 getstatic #8 <java/lang/System.out>
40 aload_1
41 invokevirtual #9 <java/io/PrintStream.println>
44 return
StringBuilder的toString方法:每次创建新的String实体
@Override
public String toString() {
// Create a copy, don't share the array
return new String(value, 0, count);
}
这才是synchronized锁定String字符串不生效的根本原因
回复 8888可以领取面试资料,感谢各位小伙伴的关注