• 多线程


    易错点1:锁一个可变对象

    package cn.enjoyedu.ch1.syn;
    
    /**
     * 类说明:错误的加锁和原因分析
     */
    public class TestIntegerSyn {
    
        public static void main(String[] args) throws InterruptedException {
            Worker worker=new Worker(1);
            for(int i=0;i<5;i++) {
                //注意:这里传入的是同1个worker,因此Worker类里面的Integer i 和 Object obj,5个线程是共享的
                //不要误认为是5个worker,而是5个线程用的是同1个worker对象
                new Thread(worker).start();
            }
        }
    
        private static class Worker implements Runnable{
    
            private Integer i;//5个线程共享
            private Object obj = new Object();//5个线程共享
    
            public Worker(Integer i) {
                this.i=i;
            }
    
            @Override
            public void run() {
                
                 // 如果锁的是i:
                 //    >>>假设线程-0先拿到锁,进入同步代码块。其他4个线程被block在对象i=Integer(1)的锁上。由于i++装箱操作会return一个新的integer对象赋给i,此时被锁的i变为i=Integer(2)
                 //    原本4个线程被block在对象i=Integer(1)的锁上(因为线程-0已经拿到锁),但是此时锁的对象变成了i=Integer(2),因此这4个线程可以重新竞争进入代码块。
                 //    >>>当线程-0 i++后,不等它执行完同步代码块释放掉锁,线程-1就会直接进来操作。此时线程-1 拿的锁是线程-0加一之后的对象。。以此类推,等线程-1加一后,线程-2拿的锁是线程-1加一之后的对象
                 //    >>>又因为5个线程共享i,因此,线程-0 /其他线程 sleep之前和之后输出的i会发生变化。因为在sleep的时候,别的线程进来操作了。
                 //
                 // 如果锁的是obj:
                 //    >>>obj同样是5个线程共享的,但由于没有线程对其操作。因此5个线程始终在竞争同一把锁,可以互斥运行。
                 
    
                synchronized (i) { //obj
                    Thread thread=Thread.currentThread();
                    System.out.println(thread.getName()+"-------@"+System.identityHashCode(i));
                    i++;
                    System.out.println(thread.getName()+"-------"+i+"-@"+System.identityHashCode(i));
                    try {
                        Thread.sleep(3000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(thread.getName()+"-------"+i+"--@"+System.identityHashCode(i));
                }
    
            }
    
        }
    
    }
    

      

      

    如果锁的是可变对象,i 。结果:5个线程会无法互斥。

    Thread-0-------@129460851
    Thread-0-------2-@158369013
    Thread-2-------@158369013
    Thread-2-------3-@1898749074
    Thread-3-------@1898749074
    Thread-3-------4-@1763156594
    Thread-4-------@1763156594
    Thread-4-------5-@825455695
    Thread-4-------5--@825455695
    Thread-2-------5--@825455695
    Thread-0-------5--@825455695
    Thread-3-------5--@825455695
    Thread-1-------@825455695
    Thread-1-------6-@1779415244
    Thread-1-------6--@1779415244
    

      

    修改成不可变对象, obj。结果:5个线程必须互斥执行,一个执行完了再执行下一个。

    Thread-0-------@1898749074
    Thread-0-------2-@2146135239
    Thread-0-------2--@2146135239
    Thread-4-------@2146135239
    Thread-4-------3-@299052996
    Thread-4-------3--@299052996
    Thread-3-------@299052996
    Thread-3-------4-@976006504
    Thread-3-------4--@976006504
    Thread-2-------@976006504
    Thread-2-------5-@658235253
    Thread-2-------5--@658235253
    Thread-1-------@658235253
    Thread-1-------6-@825455695
    Thread-1-------6--@825455695
    

      

    原因:虽然我们对 i 进行了加锁,但是

    但是当我们反编译这个类的 class 文件后,可以看到 i++实际是,

    本质上是返回了一个新的 Integer 对象。也就是每个线程实际加锁的是不同 的 Integer 对象。

  • 相关阅读:
    作为【开发人员】如何持续提升自己的开发技能
    永远不要放弃做梦的权利---与所有程序员们共勉
    十种更好的表达“你的代码写的很烂”的方法---总有些人的代码让人难以忍受
    程序员技术练级攻略--练成这样,成神仙了!
    创业其实是个逻辑问题![想不想创业都来看看]
    多图震撼!数字的未来,2013报告
    记最难忘的一件事 等笑话一箩筐
    HDU4666 Hyperspace(曼哈顿)
    POJ3436 ACM Computer Factory(最大流)
    再思考
  • 原文地址:https://www.cnblogs.com/frankcui/p/12490093.html
Copyright © 2020-2023  润新知