【问题标题】:Multithreading and Synchronization多线程和同步
【发布时间】:2013-06-10 13:02:27
【问题描述】:

我一直试图了解在多线程代码中使用同步的真正原因。

我们知道如果多个线程同时访问一个公共的共享资源,会导致很多问题,比如死锁、竞速条件等。但是如果我们同步一个由多个线程调用的代码,它只会允许一个线程访问资源,其他线程将在队列中等待。如果是这种情况,那么这与没有同步的单线程应用程序一样好。如果我们同步多线程代码,我们将获得什么性能提升?

只是一个比较两个场景的例子 1.我们要在单线程模型中处理1000条记录,假设处理一条记录需要1秒,那么总共需要1000秒才能完成。 2. 我们必须在多线程模型中处理 1000 条记录,处理方法是同步的,假设处理一条记录需要 1 秒,假设产生了 10 个线程,所以这里也是,只要一个线程可以访问同步方法,其余线程将在队列中,总共需要 1000 秒才能完成。

如果有人能让我理解这个基本原理,我会非常满意和放心。谢谢,

编辑:

我没有提到编程语言:它的 Java

以下代码(Spring Batch示例)只是为了了解同步和不同步的影响:

package com.dbas.core;
import java.util.List;
import org.springframework.batch.item.ItemReader;

public class NextReader implements ItemReader<String> {       
    private List<String> itemList;
    public NextReader(ListBean listBean) {
        itemList = listBean.getItemList();
    }
    public synchronized String read()
    {           
        if (!itemList.isEmpty()) {
                  return itemList.remove(0);
        }
        return null;
    }   
} 

我们必须同步上面的代码吗?如果没有,实例变量“itemList”将在多个线程之间共享,如果共享,上述项目检索是否正常工作? 在处理项目的 read() 之后将调用一个处理器。是否建议为多个线程同步上述代码,或者在没有同步的情况下它是否可以正常工作?

谢谢。

【问题讨论】:

  • 虽然他们是像@Claudio 这样的通用答案......答案也高度依赖于什么编程平台。听起来像你在使用 JVM?

标签: synchronized


【解决方案1】:

当然,如果与其他代码相比,您保留互斥锁的时间非常长,那么您将失去多线程的好处。作为一个极端,考虑一个线程永远保持互斥锁的应用程序:你得到一个单线程应用程序!

出于这个原因,软件开发人员通常会设计代码,使您保持锁定的时间尽可能短。例如,查看双重检查锁定模式:http://en.wikipedia.org/wiki/Double-checked_locking

对于更复杂的情况,即使有多个线程读取和写入数据,也存在允许具有良好性能的数据结构。例如,参见 RCU 数据结构,它也在 Linux 内核中实现:http://en.wikipedia.org/wiki/Read-copy-update

【讨论】:

    【解决方案2】:

    你是对的,如果整个进程是同步的,那么无论有多少线程都需要相同的时间。(实际上,由于上​​下文切换和开销的开销,线程越多需要的时间越长其他东西)。

    那么线索就是在这种情况下不同步整个过程方法。 理想情况下,处理一条消息将完全独立于处理另一条消息,在这种情况下,理论上您可以同时处理 1000 条消息,并且如果您有 1000 个处理器可供使用,则所需时间将减少 1000 倍。

    在实践中,你最终会介于两者之间。您可能会使用许多小锁,每个小锁都覆盖彼此独立的代码和数据。或者您可以使每条消息的所有数据彼此独立,不需要对该部分进行同步,但是当主要处理完成时,您需要将结果插入到共享数组中 - 您需要锁定对该共享的访问权限数组。

    【讨论】:

      【解决方案3】:

      多线程代码中的同步用于启用不同线程之间资源的安全共享状态访问。根据语言和硬件实现细节,由许多不同线程访问共享状态存在以下危险:

      1. 数据损坏,当两个线程同时尝试读取/写入相同的内存位置时。
      2. 可见性副作用,当一个线程更改共享资源的状态时,如果没有适当的同步,其他线程可能不会立即(甚至根本不会)看到更改。这也可能由于编译器优化(指令重新排序)而发生

      虽然您的问题含糊不清,但您提到的“同步”这个词不是任何特定的编程语言。在 Java 中,单词 syncrhonized 在各种上下文中意味着隐式监视器/锁,过多的锁可能对性能不利,在某些用例中,细粒度锁定或非阻塞算法/CAS 策略提供更好的性能。主题非常广泛,您必须更具体。

      编辑:在您描述的场景中,如果所有工作都是完全串行的并且所有状态都是共享的,那么多线程实现可能没有好处或没有好处。然而,这种极端情况很少见,通常一部分任务可以并行运行,然后您可以获得可衡量的性能改进。 Amdahl's law 可用于在尝试并行化任务时找到理论上的最大性能优势。

      编辑:

      关于您的编辑,因为我碰巧使用了 Spring Batch,所以我可以肯定地告诉您,如果您使用线程池从列表中读取项目,您必须使用同步,并且您可以通过多种方式做到这一点,如下所示你可以看到其中两个:

      public class NextReader implements ItemReader<String> {
          private List<String> itemList;
          private AtomicInteger current = new AtomicInteger(0);
      
          public NextReader(ListBean listBean) {
              itemList = listBean.getItemList();
          }
      
          public syncronized String read() {
              int index = this.current.getAndIncrement();
              if (index < itemList.size()) {
                  return itemList.get(index);
              } else
                  return null;
          }   
      }
      

      public class NextReader implements ItemReader<String> {
          private List<String> itemList;
          private AtomicInteger current = new AtomicInteger(0);
      
          public NextReader(ListBean listBean) {
              itemList = listBean.getItemList();
          }
      
          public syncronized String read() {
              int index = this.current.getAndIncrement();
              if (index < itemList.size()) {
                  return itemList.get(index);
              } else
                  return null;
          }   
      }
      

      它们都具有相同的效果,即它们允许您以线程安全的方式使用多个从 itemList 中读取。

      【讨论】:

      • 感谢大家的回复!
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-10-25
      • 2015-05-22
      相关资源
      最近更新 更多