【问题标题】:Can I synchronize an action of 2 threads with an empty lock object?我可以将 2 个线程的操作与空锁对象同步吗?
【发布时间】:2018-07-10 14:32:45
【问题描述】:

我在一个应用程序中有 2 个单独的线程,用于进行一些图论模拟(使用 Java2D),一个线程每秒执行约 60 次,其任务正在渲染。另一个每秒执行约 30 次并进行一些计算。

这两个线程共享资源。具体来说,他们共享他们迭代的集合。当这两个线程同时访问一个集合时,就会出现问题。 (一个是尝试在其中渲染对象,另一个是修改集合)。这会导致 ConcurrentModificationException。我已经尝试了一些解决方案,我发现其中一个可以正常工作(并且易于实现)的解决方案是使用一个空对象,线程切换并保持锁定。

像这样(示例代码):

public class Question {

static Object syncObject;

public static void main(String []Args) {

    syncObject = new Object();

    Runnable rendering = new Runnable() { //called 60 times a second
        public void run() {
            synchronized(syncObject) {
                //doRendering
            }

        }
    };
    Runnable calculations = new Runnable() { //called 30 times a second
        public void run() {
            synchronized(syncObject) {
                //doModifications
            }       
        }
    };

    Thread t1 = new Thread(rendering);
    Thread t2 = new Thread(calculations);
    t1.start();
    t2.start();
    }
}

现在,这似乎可以正常工作。这样我就不必同步我正在使用的每一个资源,这些资源可能在渲染时被修改以及“逻辑”计算更新。

但我觉得这不是一个很好的解决方案。所以我想问一下这是否可以接受和/或是否有更合适的解决方案。 (除非我做的是完全错误的)

另外,我不想重写我已经工作了一段时间的现有代码的一半。

【问题讨论】:

  • 这是完全可以接受的,虽然我更喜欢这样的东西:static final Object syncObject = new Object(); 以防止重新分配锁定对象。

标签: java multithreading synchronization


【解决方案1】:

当“粗”锁适合时,如您在上面实现的那样,全局锁是一个很好的解决方案。我觉得它很符合你的情况。

要改进它,您应该确保全局锁是不可变的。

private static final Object syncObject = new Object();

这种方法的局限性在于,您实际上是在序列化对图形的所有访问。这将并发的潜力限制为一次只有一个。因此,在多 cpu 机器上,cpu 处于空闲状态,而渲染和计算工作可能会花费太长时间。优点是实现简单,可以安全地从不同线程访问。

实施起来更复杂的其他选项有:1) 使用多个细粒度锁,2) 研究“无锁算法”或 3) 使用 mutate/publish 方法对数据结构进行“不可变”。

以下参考讨论了粗粒度锁和细粒度锁:http://fileadmin.cs.lth.se/cs/education/eda015f/2013/herlihy4-5-presentation.pdf

无锁算法可能会非常复杂地正确实施,并且不适用于所有情况。可以阅读引物here

选项 3,使用不可变数据结构有很多优点(和一些限制)。这意味着图表一旦创建就无法更改。突变会通过创建一个新图来发生;为了减少内存开销,将允许重用图表的某些部分。这意味着渲染可以获取图形的副本,并在完全隔离改变图形的计算的情况下进行渲染。突变完成后,可以使用 AtomicReference 使更新后的图可供渲染器使用。缺点是它需要更多的对象分配和更复杂的变异算法,但是它受益于允许“读取器”线程与单个变异线程并行工作。有关详细信息,请参阅persistent vs immutable data structurehttps://docs.oracle.com/javase/8/docs/api/java/util/concurrent/atomic/AtomicReference.htmlHow does concurrency using immutable/persistent types and data structures work?

【讨论】:

    【解决方案2】:

    您可以使用 any 对象进行同步,所以您所做的是正确的。但是,java 并发包没有使用空对象的内在锁,而是提供了特定的锁对象来执行此操作。见https://docs.oracle.com/javase/tutorial/essential/concurrency/newlocks.html

    【讨论】:

    • java.util.concurrent.locks.ReentrantLock 类比语言的内置同步功能更强大,但它能够完成复杂且可能有风险的事情(例如,将锁锁定在一个例程并在另一个中解锁)并不一定会让它更好做内置同步可以实现的简单事情。
    • @jameslarge: 没说更好,只是说OP做的是对的,不过还有一个选项。
    猜你喜欢
    • 2023-03-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-04-02
    • 2014-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多