【问题标题】:Synchronization: multiple locks - create lock objects?同步:多个锁 - 创建锁对象?
【发布时间】:2014-09-29 19:47:08
【问题描述】:

一个快速(我认为)并发问题:我正在 Udemy.com 上学习多线程课程,老师通过下面的代码进行了讨论。虽然他解释了,但我仍然不确定为什么要创建 lock1lock2 对象而不是锁定 list1list2

App.java:

public class App {

    public static void main(String[] args) {
        Worker worker = new Worker();
        worker.main();
    }
}

Worker.java:

import java.util.ArrayList;
import java.util.List;
import java.util.Random;


public class Worker {

    private Random random = new Random();

    private Object lock1 = new Object();
    private Object lock2 = new Object();

    private List<Integer> list1 = new ArrayList<Integer>();
    private List<Integer> list2 = new ArrayList<Integer>();

    public void stageOne() {

        synchronized (lock1) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            list1.add(random.nextInt(100));
        }

    }

    public void stageTwo() {

        synchronized (lock2) {
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }

            list2.add(random.nextInt(100));
        }

    }

    public void process() {
        for(int i=0; i<1000; i++) {
            stageOne();
            stageTwo();
        }
    }

    public void main() {
        System.out.println("Starting ...");

        long start = System.currentTimeMillis();

        Thread t1 = new Thread(new Runnable() {
            public void run() {
                process();
            }
        });

        Thread t2 = new Thread(new Runnable() {
            public void run() {
                process();
            }
        });

        t1.start();
        t2.start();

        try {
            t1.join();
            t2.join();
        } catch (InterruptedException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        long end = System.currentTimeMillis();

        System.out.println("Time taken: " + (end - start));
        System.out.println("List1: " + list1.size() + "; List2: " + list2.size());
    }
}

【问题讨论】:

  • 没有理由。你真的应该只同步final 变量。如果您可以将List 标记为final,那么您可以简单地对其进行同步。使用单独的对象将关注点分开,并且可以考虑使代码更清晰。
  • 同意鲍里斯。有一点我要补充一点,您永远不想锁定由 getter 返回的字段。因此,通过将锁与用于不同目的的任何字段隔离开来,您就可以消除这种可能性。
  • 感谢@BoristheSpider 和@John B。为什么只锁定final 变量?为什么不使用 getter?
  • JVM 不会让多个线程同时在同一个对象上同步。两个线程可以同时进入synchronized(foo) 块吗?绝对地!因为一个线程可以进入块,然后将 foo 更改为引用不同的对象,然后另一个线程出现并在另一个对象上同步。当这种情况发生时,它通常是一个错误。制作 foo final 可以帮助你避免这个错误。

标签: java multithreading locking


【解决方案1】:

我不认为您提供的代码中表达了这样做的动机,但它通常是最佳实践。但是,同样的最佳实践要求锁定对象也必须是 final

如果有问题的列表要么是从外部接受的,要么是通过某种方法暴露给外部的,那么单独的锁对象的好处就变得更加明显:将锁暴露给外来代码绝不是一个好主意,因为然后外星人代码可以单独使用它们进行锁定,从而打破您自己的使用模式。

如果列表是严格私有的,那么它们的监视器将可用于内部锁定;但是,以后对列表中的访问策略的更改可能会无意中影响锁定策略。因此,从私有锁开始也有助于避免将来出现任何错误。

【讨论】:

  • 太棒了 - 在我看来,锁定多个线程可以访问的任何对象是有意义的,但我理解你的观点。每次都只创建一个新的Object 工作吗?虽然我还在学习,但似乎新对象与List 无关。
  • 在任何情况下,对象和它的监视器之间没有任何关系——它只是另一个监视器。两者之间唯一的联系点是有点尴尬的“方便”语法,它允许将方法标记为synchronized。该功能实际上造成的混乱远大于便利。
猜你喜欢
  • 2014-03-11
  • 1970-01-01
  • 1970-01-01
  • 2013-04-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多