讨喜的隔离可变性(十)使用Transactor

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

Akka transactor或事务角色为我们提供了一种将多个角色的执行过程合并到一个事务中的方法。顾名思义,transactor可以将多个角色对于托管STM Ref对象的更改变成原子操作,即仅当外围事务提交成功之后,对于那些托管对象的变更才能生效,否则所有的变更都会被丢弃。

Transactor提供了三种处理消息的方法:

  • 默认情况下,Transactor会在其自己的事务中处理消息。
  • 实现normally()函数。该函数不属于任何事物,其主要功能是独立地处理我们所选择的消息。
  • 申请让消息被协调处理,即使其作为总控事务的一部分来执行。

总体而言,Transactor为我们提供了将其他角色链接到我们的协调事务里的弹性。此外,transactor还提供了前置和后置于事务的可选函数,以便于我们可以提前为事务做好准备或执行某些后置提交操作。
阅读全文

讨喜的隔离可变性(九)混合使用角色和STM

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

角色可以帮助我们对可变状态进行很好地隔离。尤其是当问题能够被拆分成可以独立运行的多个并发任务、并且并发任务彼此之间都是通过消息进行异步通信时,角色的表现更佳。但是,角色并未提供对跨任务的一致性进行管理的方法。所以如果我们希望两个或多个角色的动作要么全部成功、要么全部失败,则角色就无法独立实现,而此时我们就需要通过引入STM来与角色配合完成此类功能。在本节中,我假定你已经阅读过第6章、以及本章中有关角色和类型化角色的相关内容。 阅读全文

讨喜的隔离可变性(八)类型化角色和Murmurs

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

使用了类型化角色的EnergySource使我们能够以调用函数的形式来掩盖后台顺序处理异步消息的过程,在实现了线程安全的同时又可以免去显式同步的困扰。虽然创建类型化角色并不困难,但此时我们的EnergySource却还是一个丢失了关键特性的半成品——即还没有可以周期性自动补充电量的能力。 阅读全文

讨喜的隔离可变性(七)使用类型化角色

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

到目前为止我们所接触过的角色都是可以接收消息的,而消息的类型也是五花八门,如String、元组、case类/自定义消息等。然而发送消息的行为在感觉上与我们日常编程工作中所使用的常规函数调用还是有很大区别的,为了弥合二者之间的鸿沟,类型化角色(Typed Actor)就应运而生了。这种类型的角色可以将发送消息的动作在形式上伪装成常规的函数调用,而将消息传输动作隐藏在后台执行。我们可以将类型化角色想像成为一个活动的对象,该对象运行在一个属于自己的轻量消息驱动的线程里面,并且还带有一个用于将正常的函数调用转换成异步非阻塞消息的拦截代理。 阅读全文

讨喜的隔离可变性(六)多角色协作

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

在使用基于角色的编程模型时,只有当多个角色互相协作、同心协力解决问题时,我们才能真正从中获益并感受到其中的乐趣。为了更好地利用并发的威力,我们通常需要把问题拆分成若干个子问题。不同的角色可以负责不同的子问题,而我们则需要对角色之间的通信进行协调。下面我们将通过重写计算目录大小的例子来学习如何在进行多角色协作。
阅读全文

讨喜的隔离可变性(五)同时使用多个角色

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

通过前面的学习,我们已经了解了如何创建角色以及如何给角色发送消息,下面让我们来一起学习如何让多个角色协同工作。在第2章中,我们创建了一个统计给定区间内所有素数的程序。在该程序中,我们使用了ExecutorService、Callable、Future以及其他差不多超过一页纸那么多代码。本节我们将会学习如何用Akka角色对该示例进行重构,并且根据之前的惯例我们的介绍顺序还是先Java后Scala。
阅读全文

讨喜的隔离可变性(四)收发消息

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

我们可以向角色发送任何类型的消息——String、Integer、Long、Double、List、Map、元组(tuples)、Scala的case类…但其中有一点需要注意的是,上述所有类型的消息都必须是不可变的。在上述这些类型中,我对于元组有着特殊的偏好,这并非因为我听到别人把元组误读成“two-ples”时感到很有趣,而是由于元组是轻量的、不可变的并且是最容易创建的实例之一。例如,在Scala中,我们可以简单地用(number1,number2)来创建一个含有两个数字的元组。除了元组之外,Scala的case类也是用来定义消息的理想类型——因为case类是不可变的、可以进行模式匹配并且还很容易进行复制。在Java中,我们可以通过将消息定义为一个不可修改(unmodifiable)的Collection的方式来将多个对象塞到一个消息中。当我们向角色传递消息时,如果发送者和接收者都在同一个JVM里[1],则默认情况下我们传递的是消息的引用。需要注意的是,保证所传递消息的不可变性是程序员自己的责任,尤其是当所发送的消息是我们自定义的类时则更需要加倍小心。为了解决这个问题,我们可以让Akka替我们先将消息序列化,然后将序列化出来的拷贝而不是原对象的引用发送出去,这样就可以避免由于类定义不严谨所造成的问题。 阅读全文

讨喜的隔离可变性(三)创建角色

声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。

正如前面曾经提到过的那样,虽然我们有很多支持角色的类库可供选择,但是在本书中我们将使用Akka。这是一个基于Scala的类库,该类库拥有非常好的性能和可扩展性、并同时支持角色和STM。此外,该类库还可以被用于多种JVM上的语言中。在本章中,我们将注意力集中在Java和Scala身上。而在下一章,我们将会学习如何在其他语言中使用Akka的角色。

 某个角色的生命周期

图 8‑2 某个角色的生存周期

阅读全文

线程执行者(五)运行多个任务并处理第一个结果

声明:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González     译者:许巧辉     校对:方腾飞,叶磊

运行多个任务并处理第一个结果

在并发编程中的一个常见的问题就是,当有多种并发任务解决一个问题时,你只对这些任务的第一个结果感兴趣。比如,你想要排序一个数组。你有多种排序算法。 你可以全部启用它们,并且获取第一个结果(对于给定数组排序最快的算法的结果)。

在这个指南中,你将学习如何使用ThreadPoolExecutor类的场景。你将继续实现一个示例,一个用户可以被两种机制验证。如果使用其中一个机制验证通过,用户将被确认验证通过。
阅读全文

非阻塞同步算法实战(二)-BoundlessCyclicBarrier

感谢网友trytocatch的投稿

前言

相比上一 篇而言,本文不需要太多的准备知识,但技巧性更强一些。因为分析、设计的过程比较复杂繁琐,也限于篇幅,所以,主要展示如何解决这些需求,和讲解代码。另外,所讲的内容也是后一篇实战中需要用到的一个工具类。

需求介绍

我需要编写一个同步工具,它需要提供这样几个方法:await、pass、cancel。某个线程调用await时,会被阻塞;当调用pass方法时,之前因为await而阻塞的线程将全部被解除阻塞,之后调用await的线程继续被阻塞,直到下一次调用pass。

该工具同时还维护一个版本号,await方法可以带一个目标版本号,如果当前的版本号比目标版本号新或相同,则直接通过,否则,阻塞本线程,直到到达或超过目标版本。调用pass的时候,更新版本号。

如果停止了版本更新,可使用cancel方法来解除所有因await而阻塞的线程,包括指定版本号的。此方法用于避免无谓地等待。若await发生在cancel之后,则仍将被阻塞。

因为CountDownLatch不允许重复使用,CyclicBarrier只支持固定个数的线程,并且都没有维护一个版本号,所以没有已有的类能实现上面的需求,需要自己实现。 阅读全文

线程管理(十)线程组

声明:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 译者:郑玉婷 校对:方腾飞

线程组

Java并发 API里有个有趣的方法是把线程分组。这个方法允许我们按线程组作为一个单位来处理。例如,你有一些线程做着同样的任务,你想控制他们,无论多少线程还在运行,他们的状态会被一个call 中断。

Java 提供 ThreadGroup 类来组织线程。 ThreadGroup 对象可以由 Thread 对象组成和由另外的 ThreadGroup 对象组成,生成线程树结构。

在这个指南中, 我们将开发一个简单的例子来学习 ThreadGroup 对象。我们有 10 个随机时间休眠的线程 (例如,模拟搜索),然后当其中一个完成,就中断其余的。 阅读全文

线程执行者(四)执行者执行返回结果的任务

声明:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González     译者:许巧辉     校对:方腾飞,叶磊

执行者执行返回结果的任务

Executor framework的一个优点是你可以并发执行返回结果的任务。Java并发API使用以下两种接口来实现:

  • Callable:此接口有一个call()方法。在这个方法中,你必须实现任务的(处理)逻辑。Callable接口是一个参数化的接口。意味着你必须表明call()方法返回的数据类型。
  • Future:此接口有一些方法来保证Callable对象结果的获取和管理它的状态。

在这个指南中,你将学习如何实现返回结果的任务,并在执行者中运行它们。
阅读全文

线程执行者(三)创建一个大小固定的线程执行者

声明:本文是《 Java 7 Concurrency Cookbook 》的第四章,作者: Javier Fernández González     译者:许巧辉     校对:方腾飞,叶磊

创建一个大小固定的线程执行者

当你使用由Executors类的 newCachedThreadPool()方法创建的基本ThreadPoolExecutor,你会有执行者运行在某一时刻的线程数的问题。这个执行者为每个接收到的任务创建一个线程(如果池中没有空闲的线程),所以,如果你提交大量的任务,并且它们有很长的(执行)时间,你会使系统过载和引发应用程序性能不佳的问题。

如果你想要避免这个问题,Executors类提供一个方法来创建大小固定的线程执行者。这个执行者有最大线程数。 如果你提交超过这个最大线程数的任务,这个执行者将不会创建额外的线程,并且剩下的任务将会阻塞,直到执行者有空闲线程。这种行为,保证执行者不会引发应用程序性能不佳的问题。

在这个指南中,你将继续学习怎样创建一个大小固定的线程执行者,然后修改本章第一个示例的实现。
阅读全文

线程管理(三)线程的中断

声明:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 译者:郑玉婷 校对:欧振聪

线程的中断

一个多个线程在执行的Java程序,只有当其全部的线程执行结束时(更具体的说,是所有非守护线程结束或者某个线程调用System.exit()方法的时候),它才会结束运行。有时,你需要为了终止程序而结束一个线程,或者当程序的用户想要取消某个Thread对象正在做的任务。

Java提供中断机制来通知线程表明我们想要结束它。中断机制的特性是线程需要检查是否被中断,而且还可以决定是否响应结束的请求。所以,线程可以忽略中断请求并且继续运行。

在这个指南中, 我们将开发一个程序,它创建线程,然后在5秒之后,它会使用中断机制来强制结束线程。

 
阅读全文

线程管理(二)获取和设置线程信息

声明:本文是《 Java 7 Concurrency Cookbook 》的第一章, 作者: Javier Fernández González 译者:郑玉婷 校对:欧振聪

获取和设置线程信息

Thread类的对象中保存了一些属性信息能够帮助我们来辨别每一个线程,知道它的状态,调整控制其优先级。 这些属性是:

  • ID: 每个线程的独特标识。
  • Name: 线程的名称。
  • Priority: 线程对象的优先级。优先级别在1-10之间,1是最低级,10是最高级。不建议改变它们的优先级,但是你想的话也是可以的。
  • Status: 线程的状态。在Java中,线程只能有这6种中的一种状态: new, runnable, blocked, waiting, time waiting, 或 terminated.

在这个指南里,我们将开发一个为10个线程设置名字和优先级的程序,然后展示它们的状态信息直到线程结束。这些线程会计算数字乘法表。
阅读全文

return top