编写并发代码,非阻塞代码

时间:2012-04-28 16:54:57

标签: java concurrency

无论SO和其他网站上的其他问题在哪里问“如何编写并发代码?”,答案总是涉及非常模糊的解释,例如“检查数据依赖性 “或”相互依赖“在代码中。我想知道这些神秘的依赖关系实际上是什么样的Java代码!?!

  • 可以轻松并行化的一段代码的具体示例是什么,因为它的一部分不依赖于另一部分?
  • 由于存在这些依赖关系,必须必须是一段代码的具体示例是什么?
  • 这些依赖关系的存在如何影响是否使用线程池?

我想我不是在这里看到“穿过树林的森林”。提前谢谢!

4 个答案:

答案 0 :(得分:3)

以下是一些简单的例子:

1.没有依赖关系,易于并行化:

 int[] array = new int[size];
 for(int i = 0; i < size; i++) {
     array[i] = array[i] * array[i];
 }

每个项目都是独立计算的。

2.对前一次迭代的依赖:

 int[] array = new int[size];
 for(int i = 1; i < size; i++) {
     array[i] = array[i - 1] * 2 + array[i];
 }

array[3]之前,您无法计算array[2](在某些情况下,您可以推断出允许重组的公式,但您明白了这一点。)

线程池的使用并不依赖于依赖关系的存在与否。这些概念是正交的。线程池只是回收线程的有效方式,这通常是系统中昂贵的资源。

答案 1 :(得分:1)

假设你想要写一个银行账户对象而你想维持一个不变量 - 由于银行账户上的操作,钱从未被神奇地创造或销毁。

你可以写

int balance;

void deposit(int amount) {
  if (amount < 0) { throw ... }
  long after = balance + amount;
  if (after > Integer.MAX_VALUE) { throw ... }
  // DANGER 1
  balance = (int) after;
}

void withdraw(int amount) {
  if (amount < 0 || amount > balance) { throw ... }
  // DANGER 2
  balance -= amount;
}

考虑当一个线程暂停任何一个危险评论并同时发生另一次存款或取款时会发生什么。

如果一笔存款开始并且在危险1处暂停,然后又发生另一笔存款,则存款可能会丢失,然后第一笔存款完成,破坏价值。

如果一次取款开始,退出可能会推动余额为负,在危险2暂停,然后发生存款,然后退出结束。

如果存款开始,可以创建资金,在危险1处暂停,然后提取,然后存款完成。


解决此问题的标准方法是,计算after值并分配balance = (int) after的存款中的代码是一个关键部分,必须在不对其任何依赖项进行任何干预更改的情况下发生: balance

同样,withdrawal-=操作中的安全检查构成了一个关键部分 - 如果检查不正确,则不应发生-=操作。

为了获得额外乐趣,请考虑可能会发生什么,因为a -= b实际上是几个陈述:

int x = b;
int y = a;
a = y - x;

这些都必须一起发生才有意义,因此a -= b具有标准含义取决于其依赖性ab在操作过程中不会改变

答案 2 :(得分:1)

  

一段代码的具体例子是什么,可以轻松实现   并行化,因为它的一部分不依赖于另一部分   部分?

并发应用程序建立在tasks的执行之上。您可以将task标识为任何离散工作单元。如果您可以识别代码的一部分,这些部分是一个独立的工作单元,并且具有明确的任务边界,您可以为每个任务创建Runnable并在单独的{Thread中运行它1}}实现并行性。实际上Runnable表示Task抽象 一个示例是需要加载多个文件以进行处理的过程。加载文件是一个独立的工作单元,可以在后台通过线程完成而不会阻塞代码流

  

必须是串行代码段的具体示例是什么   因为存在这些依赖关系?

如果Task 2取决于Task 1的计算/处理,则Task 2必须等待才能完成Task 1,从而序列化序列。
一个例子是在后台加载文件,然后然后搜索文件中的密钥

  

这些依赖关系的存在如何与决策相称   至于是否使用线程池?

他们没有。创建Thread是一项昂贵的操作,Thread Pools是重用线程的构造设计,以避免创建新线程的开销。在Thread Pool中,您只需将Tasks作为Runnable传递,Thread Pool负责将Task分配给某个主题,或者根据需要创建新主题。您还可以通过线程池等定义策略。但它们是并发程序的工具。您应该已经确定了要提交给线程池执行的任务。

答案 3 :(得分:0)

没有依赖关系的简单示例是通过集合的任何循环,对每个项目执行相同的工作(在FP-speak中执行 map 操作)。存在依赖关系的典型示例是具有大量数据包提取的业务逻辑,基于它们的决策,然后进一步提取,全部采用嵌套ifs - 大量数据处理,但每个步骤取决于前一步骤的结果。