求教,为什么这么做是线程不安全的? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
hjchjc1993
V2EX    问与答

求教,为什么这么做是线程不安全的?

  •  
  •   hjchjc1993 2019-03-06 14:12:32 +08:00 3347 次点击
    这是一个创建于 2481 天前的主题,其中的信息可能已经有所发展或是发生改变。
    public class DeadLock { private Integer a = 0; private Integer b = 0; private void createDeadLock() { Runnable first = () -> { for (int i = 0; i < 100000; i++) { synchronized(this.a) { System.out.println("锁住 a"); this.a++; synchronized(this.b) { this.b++; } System.out.println("对 a、b 的处理完成:" + a + " " + b); } } }; Runnable secOnd= () -> { for (int i = 0; i < 100000; i++) { synchronized(this.b) { System.out.println("锁住 b"); this.b++; synchronized(this.a) { this.a++; } System.out.println("对 a、b 的处理完成:" + a + " " + b); } } }; Thread firstThread = new Thread(first); Thread secOndThread= new Thread(second); firstThread.start(); secondThread.start(); } public static void main(String[] args) { DeadLock deadLock = new DeadLock(); deadLock.createDeadLock(); } } 
    19 条回复    2019-03-06 18:40:51 +08:00
    hjchjc1993
        1
    hjchjc1993  
    OP
       2019-03-06 14:27:24 +08:00
    本来想搞一个死锁出来,运行了多次,结果是死锁没有出现,反而 a 和 b 的值表示这么做是线程不安全的,求教啊,别沉
    hjchjc1993
        2
    hjchjc1993  
    OP
       2019-03-06 14:29:33 +08:00
    再顶下~
    gaius
        3
    gaius  
       2019-03-06 14:41:42 +08:00
    输出语句没同步
    geelaw
        4
    geelaw  
       2019-03-06 14:48:04 +08:00   1
    Integer 对象是不可变对象,假设 x 是一个 Integer,则 x++ 等同于 x = new Integer(x.intValue() + 1),但是你锁住的对象是之前的对象,是 x 引用了谁改变了,而不是 x 引用的那个谁改变了。
    syncnano
        5
    syncnano  
       2019-03-06 14:53:42 +08:00
    ++不是原子操作外加对象引用其实变了,所以没有锁住吧?
    ipwx
        6
    ipwx  
       2019-03-06 1:55:06 +08:00
    你把两条 System.out.println 语句输出的内容做一下细微变化再看看。
    hjchjc1993
        7
    hjchjc1993  
    OP
       2019-03-06 15:32:02 +08:00
    @geelaw synchronized 貌似只能锁对象,那这种情况该怎么修改代码才能线程安全呢?
    gaius
        8
    gaius  
       2019-03-06 15:37:02 +08:00
    首先 Integer a =0; Integer b=0;是同一个对象,一把锁。synchronized 里的对象改变之后,原对象的锁就释放了。
    其实你锁的是数字。
    hjchjc1993
        9
    hjchjc1993  
    OP
       2019-03-06 15:54:54 +08:00
    @gaius 确实应该是没锁住,对 JVM 的内存模型还是不太清楚啊。
    ihavecat
        10
    ihavecat  
       2019-03-06 16:00:06 +08:00
    Integer -128 到 127 是放在缓存里的,你这种写法和 a 和 b 是指向同一地址的
    hjchjc1993
        11
    hjchjc1993  
    OP
       2019-03-06 16:13:50 +08:00
    @ihavecat 改为了
    private Integer a = new Integer(200);
    private Integer b = new Integer(200);
    最后的结果显示仍然不是线程安全的
    Malthael
        12
    Malthael  
       2019-03-06 16:35:11 +08:00
    把 b 改个对象,比如说 Double,锁对象时用 synchronized (Integer.class)和 synchronized (Double.class)
    Yuicon
        13
    Yuicon  
       2019-03-06 16:39:01 +08:00
    @hjchjc1993 写个包装类
    hjchjc1993
        14
    hjchjc1993  
    OP
       2019-03-06 16:47:03 +08:00
    @Malthael 其实用其它的写法是很容易做到线程安全的,我只是对这种写法上锁失败的原因表示好奇。。
    ihavecat
        15
    ihavecat  
       2019-03-06 16:48:07 +08:00
    synchronized 中锁住的对象不能被改变,在循环体内进行++操作后,对象变了,各自没锁住,就没法死锁了,建议用字符串或者 AtomicInteger 试试
    @hjchjc1993
    brainfxxk
        16
    brainfxxk  
       2019-03-06 16:58:13 +08:00   1
    Integer 类的问题 你把锁换成 private final Object 累加的数值分别加到一个 int/Integer 字段上 再试试
    hjchjc1993
        17
    hjchjc1993  
    OP
       2019-03-06 17:11:41 +08:00
    这样写就没问题了。原来的问题可能确实出现在没有锁 final 对象,自增改变了对象,所以没锁住。
    下面这么样写就死锁了。。。

    public class DeadLockProblem {

    private final Counter counter1 = new Counter();
    private final Counter counter2 = new Counter();

    private void createDeadLock() {
    Runnable first = () -> {
    for (int i = 0; i < 100000; i++) {
    synchronized(counter1) {
    System.out.println("锁住 counter1");
    counter1.addOne();
    synchronized(counter2) {
    counter2.addOne();
    }
    System.out.println("对 counter1、counter2 的处理完成:" + counter1.getCount() + " " + counter2.getCount());
    }
    }
    };

    Runnable secOnd= () -> {
    for (int i = 0; i < 100000; i++) {
    synchronized(counter2) {
    System.out.println("锁住 counter2");
    counter2.addOne();
    synchronized(counter1) {
    counter1.addOne();
    }
    System.out.println("对 counter1、counter2 的处理完成:" + counter1.getCount() + " " + counter2.getCount());
    }
    }
    };

    Thread firstThread = new Thread(first);
    Thread secOndThread= new Thread(second);
    firstThread.start();
    secondThread.start();
    }

    public static void main(String[] args) {
    DeadLockProblem deadLock = new DeadLockProblem();
    deadLock.createDeadLock();
    }
    }

    class Counter {
    private int count = 0;

    void addOne() {
    count++;
    }

    int getCount() {
    return count;
    }
    }
    hjchjc1993
        18
    hjchjc1993  
    OP
       2019-03-06 17:56:09 +08:00
    @brainfxxk 谢谢 正解
    MachineSpirit
        19
    MachineSpirit  
       2019-03-06 18:40:51 +08:00 via Android
    我一直以为 synchronized 锁的是对象的引用。想了想只锁引用也确实不安全。应该是对象和引用都被锁了吧?
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     2519 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 27ms UTC 15:05 PVG 23:05 LAX 07:05 JFK 10:05
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86