【问题标题】:What will happen when we create lock (Synchronized block) object inside a method?当我们在方法中创建锁(同步块)对象时会发生什么?
【发布时间】:2018-10-09 05:07:05
【问题描述】:
public class SampleExecutorService {
    private static int count = 0;

    private void increment() {
        Object lock = new Object();
        synchronized (lock) {
            count++;
        }   
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        SampleExecutorService obj = new SampleExecutorService();
        Runnable task = obj::increment;

        for (int i = 0; i < 1000; i++) {
            executorService.submit(task);

        }
        executorService.shutdown();

        try {
            executorService.awaitTermination(2, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("count : " + count);
    }
}

上述程序的预期结果是1000,但由于我遵循同步机制,它没有给出这个结果。但是如果我们在类级实例变量中创建一个锁对象,它就可以正常工作。正确的代码sn-p在下面

public class SampleExecutorService {
    private static int count = 0;
    Object lock = new Object();
    private void increment() {
        //Object lock = new Object();
        synchronized (lock) {
            count++;
        }   
    }

    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(10);
        SampleExecutorService obj = new SampleExecutorService();
        Runnable task = obj::increment;

        for (int i = 0; i < 1000; i++) {
            executorService.submit(task);

        }
        executorService.shutdown();

        try {
            executorService.awaitTermination(2, TimeUnit.MINUTES);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println("count : " + count);
    }
}

我想知道,当我们在方法中创建锁对象时会发生什么?在方法内创建锁定对象和作为实例变量有什么区别?

【问题讨论】:

  • 您正在为每个调用创建一个新的、单独的锁定对象,因此实际上根本没有锁定,因为每个调用都锁定一个 不同的 对象。实际上,您将有 10 个线程在没有任何同步的情况下同时修改您的计数器,从而为竞争条件和可见性问题留出空间。
  • 每个方法调用都会创建自己的调用堆栈,因此在第一个示例中锁是没有意义的。
  • 由于您只创建了 1 个 SampleExecutorService 实例,因此差异是 1000 个锁定对象(在方法内部创建)vs 1 个锁定对象(对于SampleExecutorService 实例)。
  • 谢谢!我能够理解你的答案。它消除了我对这个问题的所有疑虑

标签: java multithreading concurrency synchronization executorservice


【解决方案1】:

局部变量存储在线程堆栈中,并为每个线程单独创建。如果局部变量不是原始变量,则实例本身存储在堆中,但对对象的引用 存储在线程堆栈中。这就是为什么局部变量是线程安全的。

由于全局变量存储在堆上并且由多个线程共享/可见,因此它们需要同步。

因此,在您的第一个示例中,您正在为每个线程创建新锁,因此多个线程仍然能够访问它。

Here'sJava 内存模型的优秀文章

【讨论】:

  • 谢谢@xagaffar。但我有一个疑问,方法内的对象存储在堆中(所有对象都存储在堆中)对吗?,我的观点正确吗?
  • @Rajeshkumar 从技术上讲是的,对象本身存储在堆中,但是对对象的本地引用(这很重要)存储在线程堆栈中。我会更新我的答案以使其更清楚。
  • 谢谢 :-) @xagaffar。您的回答非常清楚,帮助我更好地理解了这些概念。您分享的文章看起来解释得很好。
猜你喜欢
  • 1970-01-01
  • 2014-03-02
  • 1970-01-01
  • 2018-12-28
  • 2013-06-21
  • 1970-01-01
  • 2015-09-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多