Java 在多线程编程中,同步是否剥夺了并发执行的好处

Java 在多线程编程中,同步是否剥夺了并发执行的好处,java,multithreading,concurrency,Java,Multithreading,Concurrency,关于在我正在处理的应用程序中使用多线程,我有一个两难的选择。我有一个工作流程,其中对象的状态发生变化,这对单线程操作没有任何问题。但是,为了提高性能,我计划使用多线程 我的理解是,由于状态将在线程之间共享,因此每个线程都必须在执行之前获得状态锁,所以这不违背多线程的目的吗?看起来多线程不会产生任何实际的并发性,所以它不会比单线程更好 我的分析正确吗?如果我有误解,请有人澄清一下这个概念好吗?您对多线程和并发限制的解释是正确的。由于状态必须由线程获取和控制,以便它们执行工作(并在不工作时等待),因

关于在我正在处理的应用程序中使用多线程,我有一个两难的选择。我有一个工作流程,其中对象的状态发生变化,这对单线程操作没有任何问题。但是,为了提高性能,我计划使用多线程

我的理解是,由于状态将在线程之间共享,因此每个线程都必须在执行之前获得状态锁,所以这不违背多线程的目的吗?看起来多线程不会产生任何实际的并发性,所以它不会比单线程更好


我的分析正确吗?如果我有误解,请有人澄清一下这个概念好吗?

您对多线程和并发限制的解释是正确的。由于状态必须由线程获取和控制,以便它们执行工作(并在不工作时等待),因此实际上是将单个线程的工作拆分为多个线程

解决此问题的最佳方法是调整程序设计,以限制
关键部分的大小。正如我们在操作系统课程中学习的进程同步

在任何给定时间只能执行一个关键部分

特定术语临界部分可能不会直接应用于Java并发,但它仍然说明了这个概念

限制此关键部分的含义是什么?例如,假设您有一个管理单个银行帐户的程序(不现实,但说明了我的观点)。如果要更新余额,线程必须获取帐户锁,基本选项是让单个线程始终更新余额(无需并发)。关键部分是整个计划。然而,假设还需要执行其他逻辑,比如提醒其他银行余额更新。您可以仅在更新余额时要求锁定银行帐户状态,而不是在通知其他银行时,减少关键部分的大小,并允许其他线程通过在一个线程更新余额时通知其他银行来执行工作


如果不清楚,请发表评论。您似乎已经理解了并发的限制,但希望这将揭示实现并发的可能步骤。

您的需求并不完全清楚,但您可以很好地猜测多线程可能具有的限制
如果某些“相对自治”的任务可以由不同的线程或线程组并发执行,则运行并行线程有一定意义。

如果您的场景是这样的:启动5个线程,最后只有一个线程处于活动状态,而其他线程正在等待锁定资源,那么使用多线程毫无意义,甚至可能由于cpu上下文切换而引入开销。

我认为在您的用例中,多线程可以用于:

  • 不改变状态的任务
  • 执行一项任务,如果该任务可能被划分为多个处理,并且只有一组最小的指令可用于多线程,则该任务会改变状态

    • 简单的回答是:并发很难。对于多个并发编写器,真正的并发是非常困难的

      您需要确定的是您的实际一致性保证需要是什么。每个读者都需要能够看到每一篇文章吗?然后,您将以某种方式被迫进入所有线程(例如,使用锁)——您的下一个努力应该是确保在锁之外做尽可能多的工作,以使锁保持在尽可能短的时间内

      使锁保持最短时间的一种方法是使用锁。大多数无锁算法基于原子比较和设置原语,如
      java.util.concurrent.atomic
      包提供的原语。这些可以是非常高的性能,但设计一个成功的无锁算法可能是微妙的。一种简单的无锁算法是只构建一个新的(不可变的)状态对象,然后将其原子化为“活动”状态,如果另一个写入程序在此期间激活了另一个状态,则在循环中重试。(这种方法对于许多应用程序来说已经足够好了,但如果编写器太多,它很容易受到livelock的攻击。)

      如果您可以通过更宽松的一致性保证,那么许多其他优化都是可能的。例如,您可以使用线程本地缓存,这样每个线程都可以看到自己的数据视图,并且可以并行写入。然后,您需要处理数据过时或不一致的后果。这方面的大多数技术都力求做到:书写可能不会立即对所有读者可见,但最终保证对所有读者可见

      这是一个活跃的研究领域,一个完整的答案可以填满一本书(真的,好几本书!)。如果您刚刚开始这方面的工作,我建议您阅读Goetz等人的文章,因为它对该主题提供了很好的介绍,并提供了许多关于如何成功构建并发系统的实用建议。

      想象一下:

    • 同步是阻塞
    • 并发就是并行化
    • 您不必使用同步。您可以使用原子引用对象作为共享可变状态的包装器

      您还可以使用戳锁,它通过允许乐观读取来提高并发性。您还可以使用累加器来编写并发代码。这些特性是Java8的一部分

      另一种防止同步的方法是使用不可变的对象,这些对象可以自由共享和发布,不需要同步。我应该补充一点,您应该使用不可变对象
       private AtomicReference<State> sharedState;
       ...
       // inside a thread processing loop
       // do the processing job
       while (true) {
         State existingState = sharedState.get();
          // create a new state object from the existing and our processing
          State newState = updateState(state);
          // if the application state hasn't changed, then update it
          if (sharedState.compareAndSet(existingState, newState)) {
              break;
          }
          // otherwise we need to get the new existing state and try again
       }