synchronized块使用string作为锁遇到的问题以及代码原理

要处理多个订单,并对相同订单号的订单进行数量统计,思路是加锁不同的订单号不进行加锁,相同的订单号串行执行。代码如下:

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可以领取面试资料,感谢各位小伙伴的关注

 

 

 

jdk不同版本对String拼接的优化分析

String类和常量池内存分析例子以及8种基本类型

记一次synchronized锁字符串引发的坑兼再谈Java字符串

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术王老五

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值