Java 同步:多个锁-创建锁对象?

Java 同步:多个锁-创建锁对象?,java,multithreading,locking,Java,Multithreading,Locking,一个快速(我想)并发问题:我正在Udemy.com上学习一门多线程课程,老师讲解了下面的代码。虽然他解释过,但我仍然不知道为什么要创建lock1和lock2对象,而不是锁定list1和list2 App.java: public class App { public static void main(String[] args) { Worker worker = new Worker(); worker.main(); } } Worker.

一个快速(我想)并发问题:我正在Udemy.com上学习一门多线程课程,老师讲解了下面的代码。虽然他解释过,但我仍然不知道为什么要创建
lock1
lock2
对象,而不是锁定
list1
list2

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());
    }
}
import java.util.ArrayList;
导入java.util.List;
导入java.util.Random;
公社工人{
私有随机=新随机();
私有对象lock1=新对象();
私有对象锁2=新对象();
private List list1=new ArrayList();
private List list2=新的ArrayList();
公共空间stageOne(){
已同步(锁1){
试一试{
睡眠(1);
}捕捉(中断异常e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
列表1.add(random.nextInt(100));
}
}
公共空间stagewo(){
已同步(锁2){
试一试{
睡眠(1);
}捕捉(中断异常e){
//TODO自动生成的捕捉块
e、 printStackTrace();
}
列表2.add(random.nextInt(100));
}
}
公共程序(){

对于(int i=0;i我不认为您给出的代码表达了这样做的动机,但这通常是一种最佳实践。但是,同样的最佳实践要求锁对象也是
final

如果所讨论的列表是从外部接受的,或者是通过一种方法向外部公开的,那么单独的锁对象的好处就变得更加明显:向外来代码公开您的锁从来都不是一个好主意,因为外来代码可以自己使用它们进行锁定,从而打破您自己的使用模式


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

我不认为您编写的代码中表达了这样做的动机ave,但这通常是一种最佳实践。然而,同样的最佳实践要求锁对象也是
final

如果所讨论的列表是从外部接受的,或者是通过一种方法向外部公开的,那么单独的锁对象的好处就变得更加明显:向外来代码公开您的锁从来都不是一个好主意,因为外来代码可以自己使用它们进行锁定,从而打破您自己的使用模式


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

没有理由。您应该只在
final变量。如果你可以将你的
列表
标记为
final
,那么你可以简单地对其进行同步。使用一个单独的对象可以分离关注点,并可以考虑使代码更清晰。同意Boris的观点。我想补充一点,你永远不想锁定getter返回的字段。因此,通过将锁与任何fi隔离eld用于不同的目的,您消除了这种可能性。感谢@Boristespider和@John B。为什么只锁定
final
变量?为什么不使用getter?JVM不会让多个线程同时在同一对象上同步。两个线程可以输入
同步(foo)吗
同时阻塞?当然!因为一个线程可以进入该块,然后将foo更改为引用不同的对象,然后另一个线程出现并在另一个对象上同步。当这种情况发生时,通常是一个错误。使foo
成为final
有助于避免该错误。没有理由。你真的应该仅对
final
变量进行同步。如果您可以将
列表标记为
final
,那么您可以简单地在其上进行同步。使用单独的对象可以分离关注点,并可以考虑使代码更清晰。同意Boris的观点。我要补充一点,您永远不希望锁定get返回的字段因此,通过将锁与用于不同目的的任何字段隔离,可以消除这种可能性。感谢@Boristespider和@John B。为什么只锁定
final
变量?为什么不使用getter?JVM不会让多个线程同时在同一对象上同步。两个线程可以输入
同步(foo)吗
同时阻塞?当然!因为一个线程可以进入该块,然后将foo更改为引用不同的对象,然后另一个线程出现并在另一个对象上进行同步。当这种情况发生时,通常是一个错误。使foo
成为final
有助于避免该错误。太棒了-我觉得这是一个错误锁定多线程访问的任何对象都是有意义的,但我理解你的观点。每次创建一个新的
对象
都有效吗?在我还在学习的时候,新对象似乎与
列表
没有关系。在任何情况下,对象与其监视器之间都没有关系——它是只是另一个显示器。两者之间唯一的联系点是有点尴尬的“便利性”语法允许将一个方法标记为
synchronized
。该功能实际上造成的混乱远远超过了方便性。太棒了-在我看来,锁定多线程访问的任何对象都是有意义的,但我理解你的观点。只需每次创建一个新的
对象
就行了吗