Devoxx Fr 2012第1天

尽管您的谦虚作家坚信底层机制的抽象性,但我选择下一次会议是因为在某些用例中,开发人员的意图与其实现之间的不匹配会导致性能问题。 匹配它们需要更好地了解实际情况。

Jose Paumard从Runnable synchronizeparallel()atomically()

演讲全部涉及并发编程及其相关的副作用,性能和错误。 标题解释来自于过去与未来: Runnable是JDK 1.1(1995年)和parallel从JDK 8(2014?)。 特别是,我们将看到硬件如何影响软件以及软件开发人员的工作方式。

中心问题是如何利用处理器功能? 因此,并行应用程序有什么问题(考虑并发性,同步性和不变性)。 最后,现在和不久的将来,Java开发人员可以使用哪些工具来帮助我们开发并行应用程序?

多线程

在1995年,处理器只有一个核心。 多线程是关于在执行线程之间共享处理器的时间。 当前线程完成时,或者在等待慢速线程或锁定资源时,处理器将执行线程。 此时,并行化是所有处理器在同一时间执行同一条指令,但对不同的数据执行。 这是非常快的,因为线程不需要相互交互(不需要相互传递数据)。

从API的角度来看,Java提供了RunnableThread 。 可以使用关键字synchronizedvolatile 。 在2004年之前,没有必要超越这一范围。

早在2005年,多核处理器就可以普遍使用。 而且,从这一点开始,功率不是来自核心频率的增加(摩尔定律),而是核心数量加倍。 为了解决这个问题,Java 5将java.util.concurrent引入到表中。 附带说明一下,有一个EDU.oswego向Java 4的反向移植。此软件包中提供了许多新概念。

如今,每台简单的计算机都有2到8个内核。 诸如SPARC T3之类的专业计算机具有128个内核。 明天,规模将达到一万个内核,甚至更多。

多线程问题

  • 原始数据的增加使得有必要进行处理
  • 比赛条件。 这是Singleton模式和double-check锁定模式(不起作用)中竞争条件的演示。

    Java语言规范定义了一个原则:对变量的读取应返回该变量的最后写入值。 为了具有确定性,在访问同一变量之间应该存在一个“之前发生”的链接。 如果没有链接,则无法确定控制流程:糟糕,这里有一个很好的错误定义。 解决方案是在变量上使用关键字volatile以创建链接“ happens before”。 不幸的是,它极大地将性能降低到了单个内核的水平。

    至于具有Singleton实现的唯一方法是:

    public enum Singleton {
    	instance ;
    }

现在,当您深入到硬件级别时,它变得更加复杂。 每个内核都有两个级别的缓存(L1和L2),并且所有内核(L3)之间共享一个缓存。 下表显示了将数据从核心传递到不同的缓存所需的时间:

目的地 时间(纳秒)

L1

1个

L2

3

L3

15

内存

80

总之,无需同步,就可以以最佳速度执行程序。 使用它,您应该考虑处理器管理连接到处理器体系结构的内存(包括缓存)的方式! 这正是我们试图用Java阻止的内容(编写一次,在任何地方运行)。 也许Java尚未准备好走这条路。

java.util.concurrent

Java 5 java.util.concurrent是启动线程的新方法。 之前,我们实例化了一个新的Runnable ,创建了一个新的Thread wrapper并start后者。 Thread的run()方法没有返回值,也不会引发异常。

在Java 5中,我们实例化了Callable :其call方法既有返回值,又会引发异常。 我们将其传递给线程执行程序服务,该服务管理其onw线程池(并可能重用已经存在的线程)。 我们将线程管理委托给可以抽象(并适应)底层硬件的服务。 还有其他概念。

  • 锁定:将块锁定1个线程。 它提供了在一个方法中获取锁并在另一个方法中释放锁的方法。 此外,还有两种获取锁的方法: lock()tryLock() 最后,有一种方法可以拥有一个ReadWriteLock ,就像拥有所需的多个读取器,以及一个阻止读取的写入器一样。 读取不同步,写入同步。
  • 信号量:将块锁定为n个线程
  • 屏障:同步线程
  • 闩锁:打开/关闭所有线程的代码块

此外,还提供了新的集合: BlockingQueue (和DeQueue ),它们是具有超时方法的固定大小的集合。 它们是线程安全的,用于生产者/消费者实现。

最后,一个CopyOnWriteArrayList (和CopyOnWriteLinkedList )提供了并发友好性,使每个线程仅在写入时才能读取和锁定。 不幸的是,在读取次数不多时(对数组进行了复制,并且在写入时更改了内存指针),therey得到了优化。 哈希和集合结构可以实现为以相同的方式操作(复制和更改指针)。

替代synchronize

内存交易

Akka提供了软件事务存储 API。 使用API​​,我们不应该直接操作对象,而应该引用对象。 此外,我们需要能够为我们管理STM的Atomic对象。 内存是通过乐观锁定进行管理的:不做任何更改,并且在这种情况下可以提交。 最佳用例是访问并发性较低时,因为事务失败时会重播事务。 在高并发访问的情况下,将有很多重播。

一些实现使用锁,而其他一些则不使用。 这些与JLS中的锁语义不同。 另外,某些实现非常消耗内存,并且会随着并发事务的数量而扩展。 英特尔最近宣布,Haswell体系结构将支持事务同步扩展:迟早会对Java语言产生影响。

演员们

Actor是以只读消息形式接收数据的组件。 他们计算结果并将其作为消息发送给其他参与者(最终是其调用者)。 该策略全部基于不变性, 在这种情况下无需并发

Akka还提供了Actor API:编写Akka actor与编写Java 5 Callable非常相似。 此外,CPU使用率与执行时间大致相同(准确地说,无显着差异)。 那么,为什么要使用actoris而不是执行者服务呢? 通过交易参与者引入STM的力量。 提供的示例是银行帐户转帐。

并行运算

在Java 7中,有Fork / Join模式。 另外,也有并行数组,Java 8提供了parallel()方法。 目标是使阵列和集合上的并行计算自动化。

叉连接

它基于任务。 如果一个任务决定它太大,它将分散其他较小的子任务:这被称为fork。 然后,系统托管的线程池可用,每个线程都有一个任务队列。 当队列中没有更多任务时,线程能够从其他线程队列中获取任务并将其放在自己的队列中。 任务完成后,称为联接的阻塞可以聚合子任务的结果(以前是分叉的)。

结果,任务实现不仅必须描述其业务代码,而且还必须知道如何确定它是否太大以及如何分叉自身。 此外,可以以递归方式或迭代方式使用Fork Join。 与前一种方法相比,后一种方法的性能提高了30%。 结论是仅在不事先知道系统边界的情况下才使用递归。

平行阵列

并行阵列在JSR 166y中进行了描述。 [因此结束我在谈话中的笔记以及电池寿命]#失败

结论

说话者不仅知道他的主题,而且知道如何让听众陷入困境。 除了谈话中描述的要点外,我认为整件事中最重要的一点是他对最后一个问题的回答:

“考虑到Java隔离了开发人员的抽象级别,使用替代语言解决新的多核体系结构是否合乎逻辑?

- 大概是”

[更新]

减轻Olivier Lamy和Benoit Perroud对内存分配的压力

Apache Direct Memory的目标是减少垃圾收集器引起的延迟。 目前在Apache Incubator中。

缓存堆

提醒一下,Java的内存被划分为不同的空间(年轻,使用期限和烫发)。 有些部分分配给JVM,其余部分是本地的,并由网络使用。 堆的问题是它由JVM管理,并且为了释放未使用的对象,必须清理垃圾收集器。 这是一个冻结程序执行并使程序不确定的过程(您不知道GC何时启动和停止)。

由于当今内存非常便宜,因此许多应用程序使用本地缓存来提高性能。 可能有两个缓存:

  • 堆上:想想一个大的哈希图。 这会增加GC处理时间。
  • 堆外:通过序列化将其存储在本机内存中(这有一定的代价),但会减少堆内存的使用,从而减少垃圾收集器的时间

Apache直接内存

ADM基于ByteBuffer类:批量分配,然后按需分离。 该体系结构是分层的,顶部是一个Cache API。

分配内存的策略必须解决与磁盘空间碎片完全相同的问题:熔丝字节缓冲区或固定大小的字节缓冲区(内存“丢失”但没有碎片)。

在现实生活中,应该只有一小部分的缓存对象在堆上,其余部分在堆外。 这与Terracotta的BigMemory所做的非常相似。 这意味着人们可以使用EhCache的API并委托给ADM的实现。

下一步包括JSR 107的实现,基准测试,与其他组件(Cassandra,Lucene,Tomcat的集成),热配置,监视和管理功能等。有关更多信息,请参见ADM站点

请注意,ADM仅使用Java的公共API( ByteBuffer )作为掩体,并且没有com.sun黑客。

演讲非常有趣,是Terracotta产品的不错替代品。 我认为ADM尚未准备好投入生产,但肯定会引起关注。

尽管我不喜欢被JavaScript困扰的应用程序,但事实是,这里有。 最好有一些工具来管理它们,而不要遭受痛苦。 一个管理得当JavaScript应用程序可能等同于一个管理得当的Java应用程序( 邪恶的笑声 )。

小心Romain LinsolasJavaScript代码

从本质上讲,该演讲是关于如何通过Jenkins CI编写JavaScript测试,对其进行分析和管理的。 实际上,只有一小部分已经在JavaScript代码上做到了。

测试JavaScript

Jasmine是JavaScript的行为驱动测试库,而Underscore.js是实用程序库。 两者都将用于创建我们的测试工具。 Akquinet Maven原型可以帮助我们引导项目。

describe是对测试“类”的引用,而it是对测试函数的引用。 另外, expect让我们断言。 beforeEach扮演Java(JUnit或TestNG)中等效的Before注释的角色。 其他Jasmine函数模仿它们的Java等效项。

分析JavaScript

让我们在这里保留经过验证的工具。 如果您使用上述Maven原型,那么它已经与Sonar兼容! 为了控制测试范围,可以使用一个不错的库js-test-driver 。 它看起来像一个JUnit。 无需放弃我们先前的Jasmine测试,我们只需要使用js-test-driver启动它们,因此某些先决条件暗示了一些POM更新:

  • 茉莉花适配器
  • JAR计算覆盖率
  • 和可用端口以启动Web服务器

指标可以在Sonar中轻松获得! 通过Maven CLI完成的操作可以在Jenkins中轻松复制。

尽管开发JavaScript很好,但是与我的应用程序方法形成了对立。 至少,当我遇到这些基于JavaScript的应用程序时,我将准备好解决它们。

翻译自: https://blog.frankel.ch/devoxx-fr-2012/1/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值