同步块在获取所需对象的锁定时未按预期工作,但在应用于 'this' 时工作正常

Syncronization block not working as expected, when aquired lock on required object, but works fine when applied on 'this'

public class SyncBlockIssue {
        
        public static void main(String[] args) {    
            Thread r1= new Multi();
            Thread t1= new Thread(r1);          
            Thread t2= new Thread(r1);
            t1.start();
            t2.start();
        }
    }

    class Multi extends Thread
    {
        Integer count =new Integer(0);      
        @Override
        public void run() {
            for(int i=0;i<100000;i++)
            {               
                    synchronized (count) {      
                    //synchronized (this) {   //working fine with this
                    count++;
                }               
            }
            System.out.println(Thread.currentThread().getName() + "  "+count);          
        }       
    }

上面的代码是从第 2 个线程打印 200 万,无论我 运行 在 'this' 上获取锁时多少次,但是当获取锁 'count' 时打印任意计数] 目的。 谁能解释一下区别。

这是我的猜测,因为我不能使用注释来输入这么多单词。

Integer 对象是不可变的:

    /**
     * The value of the {@code Integer}.
     *
     * @serial
     */
    private final int value;

    /**
     * Constructs a newly allocated {@code Integer} object that
     * represents the specified {@code int} value.
     *
     * @param   value   the value to be represented by the
     *                  {@code Integer} object.
     */
    public Integer(int value) {
        this.value = value;
    }

因此,当您执行 count++ 时,将创建一个新的 Integer 对象。 count 引用然后指向新对象。

假设流程如下:

  1. Thread r1获取count锁(注意count object被锁),此时thread r2被阻塞。

  2. r1 调用 count++,创建一个新的 Integer 对象。 count 引用然后指向新对象。并且r1释放锁。(这个锁是指前面的Integer对象)。

  3. 被阻塞的
  4. r2获取锁

  5. r1也获得了锁,因为count已经指向了一个新的对象。

  6. 两个线程都执行count++:出现问题。

以下代码证明两个线程同时持有“锁”

for(int i=0; i<2; i++)
        {
            synchronized (count) {
                System.out.println(Thread.currentThread().getName() + "get lock");
                count++;
                if (i == 1) {
                    try {
                        Thread.sleep(100000);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
                System.out.println(Thread.currentThread().getName() + "release lock");
            }
        }