`
mondayw
  • 浏览: 144735 次
  • 性别: Icon_minigender_2
  • 来自: 广州
社区版块
存档分类
最新评论

[译文]一些Java并发技巧

    博客分类:
  • Java
阅读更多

原文:Some Java Concurrency Tips

作者:Carol McDonald

出处:http://weblogs.java.net/blog/caroljmcdonald/archive/2009/09/17/some-java-concurrency-tips

 

这是来自Joshua BlochBrian Goetz和其他人的一个关于一些并发技巧的汇总。

 

首先选择不可变的对象/数据

 

不可变对象(immutable object)在构建之后就不会改变了,不可变对象更简单、安全,不需要锁,并且是线程安全的。为了使得不可变对象无需提供setter/mutator方法,把域修饰成private final的,并可防止其子类别化。如果不可选择不变性,那么限制可变的状态,越少的可变状态意味着越少的协调配合。在任何实际可行的情况下把域声明成final的,final域比可变域要简单得多。

在线程共享可变数据的时候,每个读线程或者写线程都必须要协调对数据的访问,未能同步共享的可变数据可能会导致原子性的失败、竞争条件、不一致的状态,以及其他形式的非确定性。这些不确定的问题都是最难以调试的。

使并发交互只在明确定义的地方发生,减少共享数据,考虑复制而不是共享。

 

Web应用的线程风险

 

一个Servletgetpostservice方法能够同时被多个客户端调用,多线程Servlet实例和静态变量是共享的,因此如果是可变的话,那么访问必须加以协调。Servlet是典型的生命期长且被线程高频度加载的对象,如果过分同步的话性能会受影响,或者尽量共享不可变(final)数据,或者尝试完全不共享,请求参数和局部变量会更安全。

 

保持锁定的时间尽可能的短

 

在同步区内完成尽可能少的工作,把不需要加锁的代码放到同步代码块之外,特别是如果这些代码非常耗时的话!

 



  

Lock接口比使用同步代码块提供了更详尽的锁定操作,其有一个好处是如果锁是不可用的话,则不会导致阻塞。你应该只有在需要的时候才获取锁来读取或者写入共享的数据,并在一个finally子句内部解锁,以确保锁被释放。下面是一个使用ReentrantReadWriteLock的例子:

 



  

一种减少保持锁定的时间的做法是锁分拆或者锁分离,该方法是为各个状态变量使用不同的锁而不是使用单个锁,这减小了锁的粒度,提供了更好的可伸缩性,但必须要按照规则顺序来取用锁,否则会有死锁的危险。

 

首先选择executortask而不是thread

 

使用Java并发工具Executor框架(Java Concurrency Utilities Executor Framework)而不是直接使用thread来工作。Executor服务把任务的提交从执行策略中分离出来,从可运行任务的角度来考虑,由executor服务来执行这些任务。

 



  

executor可以被直接创建或者是通过使用Executors类的工厂方法来创建:

 



  

 



  

现在给出一个使用ExecutorExecutorsExecutorService类的例子:

 



  

这个例子是一个web service类,该类使用一个固定的线程池来处理多路同时传入的连接(connection),固定线程池使用返回一个ExecutorService对象的Executors类的newFixedThreadPool方法来初始化,收到的连接通过调用ExecutorService类型的pool对象的execute方法来处理,给该方法传入一个Runnable对象,Runnable对象的run方法处理连接,当run方法执行完成时,线程会被自动返还给线程池。如果有连接进来但所有的线程都已正被使用的话,那么主循环将会阻塞直到有一个线程被释放。

 

首先选择并发实用工具而不是waitnotify操作

 

每次只要你打算使用waitnotify方法时,查看一下java.util.concurrent中是否有你所需要的类,并发集合总是会提供诸如ListQueueMap一类的标准集合接口的高性能并发实现。

 



  

BlockingQueue是带有扩展的阻塞方法的并发对象,这些方法会一直等待(或者阻塞),直到检索的元素变为可用的,或者是存储空间变为可用的时才继续执行。

 



  

生产者消费者模式

 

阻塞队列对生产者消费者模式(Producer Consumer Pattern)来说很有用,在该模式中,生产者线程把工作项目加入队列而消费者线程则把工作项目从队列中取出并进行处理。以下是一个被多个线程使用的记录器的消费者模式(Consumer Pattern)例子:

 



  

下面是使用logger的生产者(Producer)的例子,一个新的ArrayBlockingQueue被实例化以传递给logger的构造方法,在run方法中,信息被放入队列中以进行记录,如果队列满了的话,则放入操作会被阻塞直到logger已把队列中的消息取走才继续。

 



  

同步器

 

同步器是一些用来帮助线程间的协调访问的对象,最常用的同步器是倒计数锁存器(CountDownLatch)和信号量(Semaphore),同步器的使用可以消除大多数wait或者notify方法的使用。

 



  

下面是一个使用信号量来控制资源池访问的例子,多个线程可以请求使用一个资源,并在完成使用后把它交还。

在构造方法中,我们创建了一个新的信号量,信号量的大小与我们正在创建的资源池的大小相同。

getResource()方法中,信号量的aquire方法被调用,尝试取得使用资源的许可,如果有资源可用的话,则该方法会有返回,然后会从池中返回一个资源。如果所有的资源都正在使用当中的话,则aquire方法的调用会阻塞,直到另一个线程调用该信号量的release方法时才继续执行。当某个线程完成资源的使用时,该资源会被返还给池,然后release方法会被调用。aquirerelease方法都可以看作是原子操作。

 



  

多线程的延迟初始化需要一些技巧

 

当多个线程共享一个延迟初始化的域时,对该域的访问必须是同步的,否则可能会导致一些非确定性的错误出现。

 



  

首先选择正常的初始化

 

不要使用延迟初始化,除非对象或者域的初始化代价昂贵且不经常使用。通常情况下,正常的初始化是最好的;) 下面是一个线程安全的单例(singleton)预先初始化的例子,私有的final实例域和私有的构造方法使得该单例是不可变的。

 



  

如果基于静态域的性能问题而需要使用延迟加载的话,则使用按需初始化持有者模式(initialize-on-demand holder pattern),该模式利用了类直到被使用时才会初始化这一担保

 



  

参考资料和更多信息:

 

Effective Java,第二版,Joshua Bloch

Java Concurrency in PracticeBrian Goetz

Robust and Scalable Concurrent Programming: Lessons from the Trenches

Concurrency: Past and Present

  • 大小: 49.9 KB
  • 大小: 63.1 KB
  • 大小: 43.4 KB
  • 大小: 49.2 KB
  • 大小: 44.4 KB
  • 大小: 34.3 KB
  • 大小: 32.8 KB
  • 大小: 46.6 KB
  • 大小: 29.4 KB
  • 大小: 52.9 KB
  • 大小: 45.9 KB
  • 大小: 48.3 KB
  • 大小: 44 KB
  • 大小: 39.3 KB
  • 大小: 47 KB
分享到:
评论

相关推荐

    Java 并发核心编程原文+译文

    Java并发核心编程是Java开发中的重要领域,它涉及到多线程和高效率程序设计的关键技术。在Java 5和Java 6中,JVM引入了大量的并发工具和改进,以支持多处理器和多核系统上的高性能应用程序。以下是这些知识点的详细...

    Java-for-Anylogic-user译文

    本资源原文为java-for-Anylogic-user英文版,即面向Anylogic用户的Java开发,本人纯手工翻译,讲述以拖放方式以外的编程思路,对于建立复杂系统有很大帮助。这是一个信息论,可以在模型中进行数据操作以及智能体的...

    java及web中英对照译文

    JavaServer Pages(JSP)是Java技术领域中用于构建动态网页的一种标准技术,由Sun Microsystems公司发起并由多家公司共同参与开发。JSP类似于微软的ASP技术,它允许开发者在HTML或XML文档中嵌入Java代码片段...

    探讨计算机软件开发的JAVA 编程语言应用.doc,原文+译文。

    Java编程语言在计算机软件开发中的应用 在计算机软件开发领域,Java编程语言因其独特的优势而被广泛应用。项目团队最初考虑使用C++进行程序编写,主要是因为C++的强大功能和灵活性。然而,在硬件资源极其有限的单片...

    JAVA外文文献+翻译.pdf

    CGI程序有一些限制,例如维护困难和响应时间的问题。这些限制使得CGI程序不能满足互联网上编程的需求,因此JAVA语言的出现变得非常重要。 8. JAVA语言的发展前景 JAVA语言的出现标志着计算机编程的发展方向已经...

    译文:Fork and Join: Java Can Excel at Painless Parallel Programming Too!

    Java平台上的并发编程一直是一个重要的话题,特别是在多核处理器普及的今天。Fork/Join框架是Java SE 7引入的一项重要技术,它使得编写高效、并行的程序变得更加容易。本文将简要回顾Java中的并发编程基础知识,介绍...

    java2007【搜狗文档翻译_译文_英译中】1

    Twitter在2006年10月推出后迅速发展,其背后的技术架构可能涉及到Java,因为Java是开发大规模分布式系统时常用的语言,尤其是对于处理实时、高并发的数据流,如Twitter用户发布的微博客(microblogs)。 【微博客...

    java外文文献

    Java外文文献 Java是一种广泛应用于软件开发的编程语言,它的历史可以追溯到20世纪70年代末期。当时,计算机革命引发了对高级计算机软件的需求,C语言成为了连接计算机和程序员之间的桥梁。但是,随着计算机技术的...

    有关java,jsp类论文可用的英文论文及中文译文

    Java和JSP(JavaServer Pages)是Web开发领域中的核心技术,广泛应用于构建动态网页和企业级应用程序。这篇论文集包含了关于这两个主题的英文原版论文和对应的中文译文,为研究者、学生或开发者提供了深入理解这两项...

    Trados 使用技巧- 译文批量导入记忆库

    其中,“Trados使用技巧-译文批量导入记忆库”这一主题,深入探讨了如何利用WinAlign工具将大量已翻译的文本批量导入到Trados的记忆库中,从而在未来的翻译项目中重复使用这些翻译,节省时间并保持术语的统一。...

    JAVA/JSP的英文参考文献及翻译

    JAVA/JSP 英文参考文献及翻译 JAVA/JSP 方面通用的英文参考文献及翻译是 IT 行业中一个非常重要的知识点。下面我们将从技术发展历史、JSP 技术特点、JSP 和数据库相关知识、JavaBean 相关内容等几个方面来详细介绍 ...

    Java虚拟机规范 JavaSE7

    Java虚拟机(JVM)是Java程序运行的基础,它负责执行Java字节码,提供了一个与平台无关的执行环境。JVM规范定义了JVM的结构、指令集和运行时数据区,以及如何执行指令和处理异常。自1999年以来,JVM规范经历了多次...

    Java和因特网.docx,原文+译文。

    Java 和互联网:编程的新革命 Java 作为一种计算机编程语言,其重要性和革命性的地位可能让初学者感到疑惑。从传统的编程视角来看,它的优势并不显而易见。然而,Java 不仅能解决传统独立应用程序的问题,它在...

    基于JAVA的蓝牙无线技术研究.doc,原文+译文。

    这篇毕业设计的外文原文与译文提供了深入理解这一主题的基础。 1.1 引言 无线通信是指信号通过电磁波而非有线方式在通信路径的部分或全部中传输的技术。网络是由用于生成、处理和接收电信流量的开关、传输链接和...

    外文翻译—Java(译文-英文).doc

    Java的I/O系统随着时间不断演进,JDK 1.4引入的NIO(New I/O)提供了通道(Channels)和选择器(Selectors)的概念,允许非阻塞I/O操作,适用于高并发的服务器端应用。NIO的`FileChannel`能够高效地进行大文件传输,...

    外文翻译(含原文、译文及出处) 适用于用Java做毕业设计的同学

    还有一些语言是基于约束编程或者完全通过操作图形符号来编程的(后者证明过于局限)。这些方法各自在特定问题领域内表现出色,但一旦超出其设计范畴,就会变得笨拙。 面向对象的方法更进一步,为程序员提供了表示...

Global site tag (gtag.js) - Google Analytics