存档

‘JAVA’ 分类的存档

使用固定大小的ThreadPool

2009年12月27日 admin 没有评论

java.util.concurrent包提供了很多并发的工具类,下面这段代码是一个简单的线程池的样例代码。

public class FixedThreadPoolDemo {
	public static void main(String[] args) {

		// int nThreads=Runtime.getRuntime().availableProcessors();
		ExecutorService executorService = Executors.newFixedThreadPool(2);

		Runnable saveLogTask = new Runnable() {

			@Override
			public void run() {
				try {
					Thread.currentThread().sleep(2000L);
				 System.out.println("Log is saved by thread"
						+ Thread.currentThread().getId());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		};
		Runnable feedBack = new Runnable() {

			@Override
			public void run() {
				try {
					Thread.currentThread().sleep(2000L);
				   System.out.println("Feedback is called by thread"
						+ Thread.currentThread().getId());
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		};

		executorService.submit(saveLogTask);
		System.out.println("Do save log asynchronously.");
		executorService.submit(feedBack);
		System.out.println("Call feedback asynchronously.");

	}
}

其他相关

转载:使用Callable返回结果

分类: JAVA 标签:

玩转JVM 内存参数

2009年12月26日 admin 没有评论

-Xms 最小堆的大小, 也就是当你的虚拟机启动后, 就会分配这么大的堆内存给你

-Xmx 是最大堆的大小

-Xmn设置新生代的大小

整个JVM内存大小=新生代大小 + 老生代大小 + 永生代大小。老生代一般固定大小为64m,所以增大年轻代后,将会减小年老代大小。此值对系统性能影响较大,Sun官方推荐配置为整个堆的3/8。

样例配置

JAVA_OPTS=”${JAVA_OPTS} -Xms512m -Xmx1536m -XX:MaxPermSize=256m”

从程序中也可以获取部分的JVM 内存参数

public class GetHeapSize {
	public static void main(String[] args) {

		// Get the jvm heap size.
		Runtime runtime = Runtime.getRuntime();

		long heapSize = runtime.totalMemory()/1024/1024;

		// Print the jvm heap size.
		long maxMemory = runtime.maxMemory()/1024/1024;
		System.out.println("Heap Size = " + heapSize+", maxMemory="+maxMemory);
	}
}
 
java -Xms64m -Xmx256m -Xmn16m com.danieljourney.GetHeapSize
输出 Heap Size = 62, maxMemory=254
java -Xms256m -Xmx256m -Xmn256m com.danieljourney.GetHeapSize
输出 Heap Size = 254, maxMemory=254
 

-Xss设置每个线程的堆栈大小。JDK5.0以后每个线程堆栈大小为1M,以前每个线程堆栈大小为256K。更具应用的线程所需内存大小进行调整。在相同物理内存下,减小这个值能生成更多的线程。但是操作系统对一个进程内的线程数还是有限制的,不能无限生成,经验值在3000~5000左右。

-XX:NewRatio=n设置新生代和老生代的比值。如:为3,表示新生代与老生代比值为1:3,新生代占整个老生代老生代和的1/4。

-XX:SurvivorRatio=n:新生代中Eden区与两个Survivor区的比值,注意Survivor区有两个。如:3,表示Eden:Survivor=3:2,一个Survivor区占整个年轻代的1/5。

-XX:MaxPermSize=n:设置永生代大小。

JAVA_OPTS=”${JAVA_OPTS} -Xms512m -Xmx1536m -XX:MaxPermSize=256m”

参考资料

转载:Java 6 JVM参数选项大全(中文版)

Java内存管理系列文章

分类: JAVA 标签:

JMS 入门-1

2009年12月6日 admin 没有评论

There are two main concepts in JMS: message brokers and destinations.

When an application sends a message, it hands it off to a message broker. A Message broker is JMS’s answer to the post office. The message broker will ensure that the message is delivered to the specified destination, leaving the sender free to go about other business.

Point-to-point messaging model

In the point-to-point model, each message has exactly one sender and one receiver. When the message broker is given a message, it places the message in a queue. When a receiver comes along and asks for the next message in the queue, the message is pulled from the queue and delivered to the receiver. Because the message is removed from the queue as it is delivered, it is guaranteed that the message will be delivered to only one receiver.

image

Publish-subscribe messaging model

In the publish-subscribe messaging model, messages are sent to a topic. As with queues, many receivers may be listening to a topic. However, unlike queues where a message is delivered to exactly one receiver, all subscribers to a topic will receive a copy of the message.

 

image

分类: JAVA 标签:

转载:Java 6 JVM参数选项大全(中文版)

2009年12月6日 admin 没有评论

 

作者:Ken Wu

Email: ken.wug@gmail.com

原文链接 http://kenwublog.com/docs/java6-jvm-options-chinese-edition.htm

本文是基于最新的SUN官方文档Java SE 6 Hotspot VM Options 编写的译文。主要介绍JVM中的非稳态选项及其使用说明。

为了让读者明白每个选项的含义,作者在原文基础上补充了大量的资料。希望这份文档,对正在研究JVM参数的朋友有帮助!

另外,考虑到本文档是初稿,如有描述错误,敬请指正。

非稳态选项使用说明

-XX:+<option> 启用选项

-XX:-<option> 不启用选项

-XX:<option>=<number> 给选项设置一个数字类型值,可跟单位,例如 32k, 1024m, 2g
-XX:<option>=<string> 给选项设置一个字符串值,例如-XX:HeapDumpPath=./dump.core

行为选项

选项

默认值与限制

描述

-XX:-AllowUserSignalHandlers

限于Linux和Solaris,默认不启用

允许为java进程安装信号处理器。

Java信号处理相关知识,详见 http://kenwublog.com/java-asynchronous-notify-based-on-signal

-XX:-DisableExplicitGC

默认不启用

禁止在运行期显式地调用 System.gc()。

开启该选项后,GC的触发时机将由Garbage Collector全权掌控。
注意:你熟悉的代码里没调用System.gc(),不代表你依赖的框架工具没在使用。

例如RMI就在多数用户毫不知情的情况下,显示地调用GC来防止自身OOM。

请仔细权衡禁用带来的影响。

-XX:-RelaxAccessControlCheck

默认不启用

在Class校验器中,放松对访问控制的检查。

作用与reflection里的setAccessible类似。

-XX:-UseConcMarkSweepGC

默认不启用

启用CMS低停顿垃圾收集器。

资料详见:http://kenwublog.com/docs/CMS_GC.pdf

-XX:-UseParallelGC

-server时启用

其他情况下,默认不启用

策略为新生代使用并行清除,年老代使用单线程Mark-Sweep-Compact的垃圾收集器。

-XX:-UseParallelOldGC

默认不启用

策略为老年代和新生代都使用并行清除的垃圾收集器。

-XX:-UseSerialGC

-client时启用

其他情况下,默认不启用

使用串行垃圾收集器。

-XX:+UseSplitVerifier

java5默认不启用

java6默认启用

使用新的Class类型校验器 。

新Class类型校验器有什么特点?
新Class类型校验器,将老的校验步骤拆分成了两步:
1,类型推断。
2,类型校验。
新类型校验器通过在javac编译时嵌入类型信息到bytecode中,省略了类型推断这一步,从而提升了classloader的性能。

Classload顺序(供参考)
load -> verify -> prepare -> resove -> init

关联选项:
-XX:+FailOverToOldVerifier

-XX:+FailOverToOldVerifier

Java6新引入选项,默认启用

如果新的Class校验器检查失败,则使用老的校验器。

为什么会失败?

因为JDK6最高向下兼容到JDK1.2,而JDK1.2的class info 与JDK6的info存在较大的差异,所以新校验器可能会出现校验失败的情况。


关联选项:
-XX:+UseSplitVerifier

-XX:+HandlePromotionFailure

java5以前是默认不启用,java6默认启用

关闭新生代收集担保。

什么是新生代收集担保?
在一次理想化的minor gc中,Eden和First Survivor中的活跃对象会被复制到Second Survivor。
然而,Second Survivor不一定能容纳下所有从E和F区copy过来的活跃对象。

为了确保minor gc能够顺利完成,GC需要在年老代中额外保留一块足以容纳所有活跃对象的内存空间。
这个预留操作,就被称之为新生代收集担保(New Generation Guarantee)。如果预留操作无法完成时,仍会触发major gc(full gc)。
为什么要关闭新生代收集担保?
因为在年老代中预留的空间大小,是无法精确计算的。

为了确保极端情况的发生,GC参考了最坏情况下的新生代内存占用,即Eden+First Survivor。

这种策略无疑是在浪费年老代内存,从时序角度看,还会提前触发Full GC。

为了避免如上情况的发生,JVM允许开发者手动关闭新生代收集担保。

在开启本选项后,minor gc将不再提供新生代收集担保,而是在出现survior或年老代不够用时,抛出promotion failed异常。

-XX:+UseSpinning

java1.4.2和1.5需要手动启用, java6默认已启用

启用多线程自旋锁优化。

自旋锁优化原理

大家知道,Java的多线程安全是基于Lock机制实现的,而Lock的性能往往不如人意。
原因是,monitorenter与monitorexit这两个控制多线程同步的bytecode原语,是JVM依赖操作系统互斥(mutex)来实现的。
互斥是一种会导致线程挂起,并在较短的时间内又必须重新调度回原线程的,较为消耗资源的操作。

为了避免进入OS互斥,Java6的开发者们提出了自旋锁优化。

自旋锁优化的原理是在线程进入OS互斥前,通过CAS自旋一定的次数来检测锁的释放。

如果在自旋次数未达到预设值前锁已被释放,则当前线程会立即持有该锁。

CAS检测锁的原理详见: http://kenwublog.com/theory-of-lightweight-locking-upon-cas

关联选项:
-XX:PreBlockSpin=10

-XX:PreBlockSpin=10

-XX:+UseSpinning 必须先启用,对于java6来说已经默认启用了,这里默认自旋10次

控制多线程自旋锁优化的自旋次数。(什么是自旋锁优化?见 -XX:+UseSpinning 处的描述)

关联选项:
-XX:+UseSpinning

-XX:+ScavengeBeforeFullGC

默认启用

在Full GC前触发一次Minor GC。

-XX:+UseGCOverheadLimit

默认启用

限制GC的运行时间。如果GC耗时过长,就抛OOM。

-XX:+UseTLAB

1.4.2以前和使用-client选项时,默认不启用,其余版本默认启用

启用线程本地缓存区(Thread Local)。

-XX:+UseThreadPriorities

默认启用

使用本地线程的优先级。

-XX:+UseAltSigs

限于Solaris,默认启用

为了防止与其他发送信号的应用程序冲突,允许使用候补信号替代 SIGUSR1和SIGUSR2。

-XX:+UseBoundThreads

限于Solaris, 默认启用

绑定所有的用户线程到内核线程。
减少线程进入饥饿状态(得不到任何cpu time)的次数。

-XX:+UseLWPSynchronization

限于solaris,默认启用

使用轻量级进程(内核线程)替换线程同步。

-XX:+MaxFDLimit

限于Solaris,默认启用

设置java进程可用文件描述符为操作系统允许的最大值。

-XX:+UseVMInterruptibleIO

限于solaris,默认启用

在solaris中,允许运行时中断线程 。


性能选项

选项与默认值

默认值与限制

描述

-XX:+AggressiveOpts

JDK 5 update 6后引入,但需要手动启用。

JDK6默认启用。

启用JVM开发团队最新的调优成果。例如编译优化,偏向锁,并行年老代收集等。

-XX:CompileThreshold=10000

1000

通过JIT编译器,将方法编译成机器码的触发阀值,可以理解为调用方法的次数,例如调1000次,将方法编译为机器码。

-XX:LargePageSizeInBytes=4m

默认4m

amd64位:2m

设置堆内存的内存页大小。

调整内存页的方法和性能提升原理,详见 http://kenwublog.com/tune-large-page-for-jvm-optimization

-XX:MaxHeapFreeRatio=70

70

GC后,如果发现空闲堆内存占到整个预估堆内存的70%,则收缩堆内存预估最大值。

什么是预估堆内存?

预估堆内存是堆大小动态调控的重要选项之一。

堆内存预估最大值一定小于或等于固定最大值(-Xmx指定的数值)。

前者会根据使用情况动态增大或缩小,以提高GC回收的效率。

-XX:MaxNewSize=size

1.3.1 Sparc: 32m

1.3.1 x86: 2.5m

新生代占整个堆内存的最大值。

-XX:MaxPermSize=64m

5.0以后: 64 bit VMs会增大预设值的30%

1.4 amd64: 96m

1.3.1 -client: 32m

其他默认 64m

Perm(俗称方法区)占整个堆内存的最大值。

-XX:MinHeapFreeRatio=40

40

GC后,如果发现空闲堆内存占到整个预估堆内存的40%,则增大堆内存的预估最大值。此值不会超过固定最大值。

(什么是预估堆内存?见 -XX:MaxHeapFreeRatio 处的描述)

关联选项:

-XX:MaxHeapFreeRatio=70

-XX:NewRatio=2

Sparc -client: 8

x86 -server: 8

x86 -client: 12

-client: 4 (1.3)

8 (1.3.1+)

x86: 12

其他默认 2

新生代和年老代的堆内存占用比例。

例如2表示新生代占最大堆内存的1/2。即年老代和新生代平分堆的占用。

-XX:NewSize=2.125m

5.0以后: 64 bit Vms 会增大预设值的30%

x86: 1m

x86, 5.0以后: 640k

其他默认 2.125m

新生代预估堆内存占用的默认值。(什么是预估堆内存?见 -XX:MaxHeapFreeRatio 处的描述)

-XX:ReservedCodeCacheSize=32m

Solaris 64-bit, amd64, -server x86: 48m

1.5.0_06之前, Solaris 64-bit amd64: 1024m

其他默认 32m

设置代码缓存的最大值,编译时用。

-XX:SurvivorRatio=8

Solaris amd64: 6

Sparc in 1.3.1: 25

Solaris platforms 5.0以前: 32

其他默认 8

Eden与Survivor的占用比例。例如8表示,一个survivor区占用 1/8 的新生代内存,另外因为有2个survivor,

所以survivor总共是占用新生代内存的 2/8,Eden的占比则为 6/8。

-XX:TargetSurvivorRatio=50

50

实际使用的survivor空间大小占比。默认是50%,最高90%。

-XX:ThreadStackSize=512

Sparc: 512

Solaris x86: 320 (5.0以前 256)

Sparc 64 bit: 1024

Linux amd64: 1024 (5.0 以前 0)

其他默认 512.

线程堆栈大小

-XX:+UseBiasedLocking

JDK 5 update 6后引入,但需要手动启用。

JDK6默认启用。

启用偏向锁。

偏向锁原理详见 http://kenwublog.com/theory-of-java-biased-locking

-XX:+UseFastAccessorMethods

默认启用

优化原始类型的getter方法性能。

-XX:-UseISM

默认启用

启用solaris的ISM。

详见Intimate Shared Memory.

-XX:+UseLargePages

JDK 5 update 5后引入,但需要手动启用。

JDK6默认启用。

启用大内存分页。

调整内存页的方法和性能提升原理,详见http://kenwublog.com/tune-large-page-for-jvm-optimization

关联选项

-XX:LargePageSizeInBytes=4m

-XX:+UseMPSS

1.4.1 之前: 不启用

其余版本默认启用

启用solaris的MPSS,不能与ISM同时使用。

-XX:+StringCache

默认启用

启用字符串缓存。

-XX:AllocatePrefetchLines=1

1

与机器码指令预读相关的一个选项,资料比较少,本文档不做解释。有兴趣的朋友请自行阅读官方doc。

-XX:AllocatePrefetchStyle=1

1

与机器码指令预读相关的一个选项,资料比较少,本文档不做解释。有兴趣的朋友请自行阅读官方doc。


调试选项

选项与默认值

默认值与限制

描述

-XX:-CITime

1.4引入。

默认启用

打印JIT编译器编译耗时。

-XX:ErrorFile=./hs_err_pid<pid>.log

Java 6引入。

如果JVM crashed,将错误日志输出到指定文件路径。

-XX:-ExtendedDTraceProbes

Java6引入,限于solaris

默认不启用

启用dtrace诊断。

-XX:HeapDumpPath=./java_pid<pid>.hprof

默认是java进程启动位置,即user.dir

堆内存快照的存储文件路径。

什么是堆内存快照?

当java进程因OOM或crash被OS强制终止后,会生成一个hprof(Heap PROFling)格式的堆内存快照文件。该文件用于线下调试,诊断,查找问题。

文件名一般为

java_<pid>_<date>_<time>_heapDump.hprof

解析快照文件,可以使用 jhat, eclipse MAT,gdb等工具。

-XX:-HeapDumpOnOutOfMemoryError

1.4.2 update12 和 5.0 update 7 引入。

默认不启用

在OOM时,输出一个dump.core文件,记录当时的堆内存快照(什么是堆内存快照? 见 -XX:HeapDumpPath 处的描述)。

-XX:OnError="<cmd args>;<cmd args>"

1.4.2 update 9引入

当java每抛出一个ERROR时,运行指定命令行指令集。指令集是与OS环境相关的,在linux下多数是bash脚本,windows下是dos批处理。

-XX:OnOutOfMemoryError="<cmd args>;
<cmd args>"

1.4.2 update 12和java6时引入

当第一次发生OOM时,运行指定命令行指令集。指令集是与OS环境相关的,在linux下多数是bash脚本,windows下是dos批处理。

-XX:-PrintClassHistogram

默认不启用

在Windows下, 按ctrl-break或Linux下是执行kill -3(发送SIGQUIT信号)时,打印class柱状图。

Jmap –histo pid也实现了相同的功能。

详见 http://java.sun.com/javase/6/docs/technotes/tools/share/jmap.html

-XX:-PrintConcurrentLocks

默认不启用

在thread dump的同时,打印java.util.concurrent的锁状态。

Jstack –l pid 也同样实现了同样的功能。

详见 http://java.sun.com/javase/6/docs/technotes/tools/share/jstack.html

-XX:-PrintCommandLineFlags

5.0 引入,默认不启用

Java启动时,往stdout打印当前启用的非稳态jvm options。

例如:

-XX:+UseConcMarkSweepGC -XX:+HeapDumpOnOutOfMemoryError -XX:+DoEscapeAnalysis

-XX:-PrintCompilation

默认不启用

往stdout打印方法被JIT编译时的信息。

例如:

1 java.lang.String::charAt (33 bytes)

-XX:-PrintGC

默认不启用

开启GC日志打印。

打印格式例如:

[Full GC 131115K->7482K(1015808K), 0.1633180 secs]

该选项可通过 com.sun.management.HotSpotDiagnosticMXBean API 和 Jconsole 动态启用。

详见 http://java.sun.com/developer/technicalArticles/J2SE/monitoring/#Heap_Dump

-XX:-PrintGCDetails

1.4.0引入,默认不启用

打印GC回收的细节。

打印格式例如:

[Full GC (System) [Tenured: 0K->2394K(466048K), 0.0624140 secs] 30822K->2394K(518464K), [Perm : 10443K->10443K(16384K)], 0.0625410 secs] [Times: user=0.05 sys=0.01, real=0.06 secs]

该选项可通过 com.sun.management.HotSpotDiagnosticMXBean API 和 Jconsole 动态启用。

详见 http://java.sun.com/developer/technicalArticles/J2SE/monitoring/#Heap_Dump

-XX:-PrintGCTimeStamps

默认不启用

打印GC停顿耗时。

打印格式例如:

2.744: [Full GC (System) 2.744: [Tenured: 0K->2441K(466048K), 0.0598400 secs] 31754K->2441K(518464K), [Perm : 10717K->10717K(16384K)], 0.0599570 secs] [Times: user=0.06 sys=0.00, real=0.06

secs]

该选项可通过 com.sun.management.HotSpotDiagnosticMXBean API 和 Jconsole 动态启用。

详见 http://java.sun.com/developer/technicalArticles/J2SE/monitoring/#Heap_Dump

-XX:-PrintTenuringDistribution

默认不启用

打印对象的存活期限信息。

打印格式例如:

[GC
Desired survivor size 4653056 bytes, new threshold 32 (max 32)
- age 1: 2330640 bytes, 2330640 total
- age 2: 9520 bytes, 2340160 total

204009K->21850K(515200K), 0.1563482 secs]

Age1 2表示在第1和2次GC后存活的对象大小。

-XX:-TraceClassLoading

默认不启用

打印class装载信息到stdout。记Loaded状态。

例如:

[Loaded java.lang.Object from /opt/taobao/install/jdk1.6.0_07/jre/lib/rt.jar]

-XX:-TraceClassLoadingPreorder

1.4.2引入,默认不启用

按class的引用/依赖顺序打印类装载信息到stdout。不同于 TraceClassLoading,本选项只记 Loading状态。

例如:

[Loading java.lang.Object from /home/confsrv/jdk1.6.0_14/jre/lib/rt.jar]

-XX:-TraceClassResolution

1.4.2引入,默认不启用

打印所有静态类,常量的代码引用位置。用于debug。

例如:

RESOLVE java.util.HashMap java.util.HashMap$Entry HashMap.java:209

说明HashMap类的209行引用了静态类 java.util.HashMap$Entry

-XX:-TraceClassUnloading

默认不启用

打印class的卸载信息到stdout。记Unloaded状态。

-XX:-TraceLoaderConstraints

Java6 引入,默认不启用

打印class的装载策略变化信息到stdout。

例如:

[Adding new constraint for name: java/lang/String, loader[0]: sun/misc/Launcher$ExtClassLoader, loader[1]: <bootloader> ]

[Setting class object in existing constraint for name: [Ljava/lang/Object; and loader sun/misc/Launcher$ExtClassLoader ]

[Updating constraint for name org/xml/sax/InputSource, loader <bootloader>, by setting class object ]

[Extending constraint for name java/lang/Object by adding loader[15]: sun/reflect/DelegatingClassLoader ]

装载策略变化是实现classloader隔离/名称空间一致性的关键技术。

对此感兴趣的朋友,详见 http://kenwublog.com/docs/Dynamic+Class+Loading+in+the+Java+Virtual+Machine.pdf 中的 contraint rules一章。

-XX:+PerfSaveDataToFile

默认启用

当java进程因OOM或crashed被强制终止后,生成一个堆快照文件(什么是堆内存快照? 见 -XX:HeapDumpPath 处的描述)。

作者敬告

完善的单元测试,功能回归测试,和性能基准测试可以减少因调整非稳态JVM选项带来的风险。

参考资料

Java6性能调优白皮书

http://java.sun.com/performance/reference/whitepapers/6_performance.html

Java6 GC调优指南

http://java.sun.com/javase/technologies/hotspot/gc/gc_tuning_6.html

更为全面的options列表

http://blogs.sun.com/watt/resource/jvm-options-list.html

分类: JAVA 标签:

常用JAVA代码质量静态检查工具

2009年11月21日 admin 没有评论

周五给大家做的关于常用JAVA代码质量静态检查工具的分享,一并通过网站分享给大家。

分类: JAVA 标签: , ,

Double Brace Initialization in Java

2009年11月15日 admin 没有评论
通常我们初始化一个Collection类采用以下的样例代码
List normal = new ArrayList();
normal.add("string_1");
normal.add("string_2");
normal.add("string_3");
这种初始化方式也可以替换成下面的代码
List<String> countries = new ArrayList<String>() {{
	add("India");
	add("Switzerland");
	add("Italy");
	add("France");
	add("Germany");
}};
    这种编码风格让Java这种语言似乎多了动态语言的色彩:-),这就是Java的Double Brace Initialization,
这种实现方式的原理和可能存在的性能问题,大家可以学习原文Double Brace Initialization in Java
.
分类: JAVA 标签:

转载:使用Callable返回结果

2009年11月8日 admin 1 条评论

原文地址 http://java.chinaitlab.com/advance/748786.html

自从Java平台的最开始,Runnable接口就已存在了。它允许你定义一个可由线程完成的任务。如大多数人所已知的那样,它只提供了一个run方法,该方法既不接受任何参数,也不返回任何值。如果你需要从一个未完成的任务中返回一个值,你就必须在该接口之外使用一个方法去等待该任务完成时通报的某种消息。例如,下面的示例就是你在这种情景下可能做的事情:

Runnable runnable = …;
Thread t = new Thread(runnable);
t.start();
t.join();
String value = someMethodtoGetSavedValue()

严格来说,上述代码并无错误,但现在可用不同的方法去做,这要感谢J2SE 5.0引入的Callable接口。不同于Runnable接口拥有run方法,Callable接口提供的是call方法,该方法可以返回一个 Object对象,或可返回任何一个在泛型化格式中定义了的特定类型的对象。

    public interface Callable<V> {
       V call() throws Exception;
    }

因为你不可能把Callable对象传到Thread对象去执行,你可换用ExecutorService对象去执行Callable对象。该服务接受Callable对象,并经由submit方法去执行它。

    <T> Future<T> submit(Callable<T> task)

如该方法的定义所示,提交一个Callable对象给ExecutorService会返回一个Future对象。然后,Future的get方法将会阻塞,直到任务完成。
    为了证明这一点,下面的例子为命令行中的每个词都创建一个单独的Callable实例,然后把这些词的长度加起来。各个Callable对象将只是计算它自己的词的长度之和。Futures对象的Set集合将被保存以便从中获得计算用的值。如果需要保持返回值的顺序,则可换用一个List对象。

import java.util.*;
import java.util.concurrent.*;

public class CallableExample {

    public static class WordLengthCallable
            implements Callable {
        private String word;
        public WordLengthCallable(String word) {
            this.word = word;
        }
        public Integer call() {
            return Integer.valueOf(word.length());
        }
    }

    public static void main(String args[]) throws Exception {
        ExecutorService pool = Executors.newFixedThreadPool(3);
        Set<Future<Integer>> set = new HashSet<Future&lg;Integer>>();
        for (String word: args) {
            Callable<Integer> callable = new WordLengthCallable(word);
            Future<Integer> future = pool.submit(callable);
            set.add(future);
        }
        int sum = 0;
        for (Future<Integer> future : set) {
            sum += future.get();
        }
        system.out.printf("The sum of lengths is %s%n", sum);
        system.exit(sum);
    }
}
     WordLengthCallable保存了每个词并使用该词的长度作为call方法的返回值。这个值可能会花点儿时间
去生成,不过在这个例子中,可以立即知道它。 call方法的唯一要求是这个值要在call方法的结尾处返回。
当Future的get方法稍后被调用时,如果任务运行得很快的话,Future将会自动得到这个值(如同本例的情
况),否则将一直等到该值生成完毕为止。多次调用get方法不会导致任务从该线程返回。因为该程序的目
的是计划所有字的长度之和,它不会强令Callable任务结束。如果最后一个任务在前三个任务之前完成,
也是没错的。对 Future的get方法的第一次调用将只会等待Set中第一个任务结束,而不会阻塞其它的任务
分别执行完毕。它只会等待当次线程或任务结束。这个特定的例子使用固定数线程池来产生ExecutorService
对象,但其它有效的方法也是可行的。
分类: JAVA 标签:

java 异常编码规范

2009年10月18日 admin 没有评论

一直想整理一个java异常的编码规范,但偏偏异常处理可能是编程语言处理中最复杂的话题之一了,我对这方面知识掌握的也不是很全面。但想想作为一个规范来讲,先提供一些最基本的、能达成普遍共识的规范,对于推广还是很有好处的:-),毕竟规范是也是一步一步完善的。如果大家对java异常编码这块有自己的意见和建议的话请留言。

1.避免空的catch和finally块

public void doSomething() {
  try {
    FileInputStream fis = new FileInputStream("/tmp/bugger");
  } catch (IOException ioe) {
      // 错误
  }
  finally{
  //错误
  }
}
2.不要在Finally块中return,这回导致Exception的丢失。
public class Bar {
 public String foo() {
  try {
   throw new Exception( "My Exception" );
  } catch (Exception e) {
   throw e;
  } finally {
   return "A. O. K."; // Very bad.
  }
 }
}
3.保留StackTrace
在catch块中抛出新的Exception的时候要把原始的Exception传递给新定义的Exception,否则原来的StackTrace就会丢失。
 
public class Foo {
    void good() {
        try{
            Integer.parseInt("a");
        } catch(Exception e){
            throw new Exception(e);
        }
    }
    void bad() {
        try{
            Integer.parseInt("a");
        } catch(Exception e){
            throw new Exception(e.getMessage());
        }
    }
}
4. 避免在finally块中再次抛出异常
public class Foo
	{
		public void bar(){
			try {
				// Here do some stuff
			}
			catch( Exception e) {
				// Handling the issue
			}
			finally
			{
				// is this really a good idea ?
				throw new Exception();
			}
		}
	}
}
分类: JAVA 标签:

java 日志编码规范

2009年10月17日 admin 没有评论

应用程序的开发离不开日志日志对于分析应用程序的故障(日志可以记录出现问题的那个段时间程序的运行情况)、性能、执行效果等都非常有帮助。可就是这样一个非常有帮助的东西,大家在使用的时候还存在不少的误区和使用不当的地方。这篇文章的目的就是整理一些Java日志的编码规范,希望对大家有帮助,整理有错误的地方也请告知。

以下是一些最基本的Logger编码规范

1.在一个对象中通常只使用一个Logger对象。

2.Logger应该是Static和final的,只有在少数需要在构造函数中传递logger的情况下才使用private final。

3.不允许出现System print(包括System.out.println和System.error.println)语句。

4.不允许出现printStackTrace,例如

class Foo {
 void bar() {
  try {
   // do something
  } catch (Exception e) {
   e.printStackTrace();
  }
 }
}

5.在公共的日志里禁止打印程序的调试或者提示信息。

以下是针对最常使用的org.apache.commons.logging.Log 接口的编码规范

6.Logger接口的调用必须包含两个参数String message和Throwable。

因为logger.error( e ); 这样的日志输出方法会丢失掉最重要的StackTrace信息。

public class Main {
 private static final Log _LOG = LogFactory.getLog( Main.class );
 void bar() {
  try {
  } catch( Exception e ) {
   _LOG.error( e ); //错误
  _LOG.error(e.getMessage()) //错误
  } catch( OtherException oe ) {
   _LOG.error( oe.getMessage(), oe ); //正确
  }
 }
}

 

7.在输出日志时,低级别的输出一定要判断isXXEnabled info及以下级别。

参考

7 Good Rules to Log Exceptions

Logging: commons-logging and Log4j

Apache Common Logging Guide

Java Tips: Simplify logging

5 Common Log4J Mistakes

log4j Default Initialization Procedure

DOMConfigurator

PropertyConfigurator

分类: JAVA 标签:

摘录:线程的私有成员变量和ThreadLocal

2009年10月11日 admin 没有评论

本文摘录自Java并发编程—设计原则与模式的2.3.2.1至2.3.2.3节。Java并发编程—设计原则与模式真的是一本很好的书,推荐给每一位想学习Java并发编程的程序员。

2.3.2.1线程私有成员变量

线程中执行的方法调用除了要接收受限制的引用之外,还可以访问代表它们正在运行的Thread对象,以及由此可以访问到的更多信息。静态方法Thread.currentThread()可以被任何方法调用,并且返回调用者的Thread对象。

程序员可以利用这个特性,在Thread的子类中增加成员变量,并提供只在本线程内部访问这些成员变量的方法。例如:

class ThreadWithOutputStream extends Thread {
	private OutputStream output;
	ThreadWithOutputStream(Runnable r, OutputStream s) {
		super(r);
		output = s;
	}
	static ThreadWithOutputStream current() throws ClassCastException {
		return (ThreadWithOutputStream) (currentThread());
	}
	static OutputStream getOutput() { return current().output; }
	static void setOutput(OutputStream s) { current().output =s;}
}

 

这个类可以如下使用:

class ServiceUsingThreadWithOutputStream { // Fragments
// ...
	public void service() throws IOException {
		OutputStream output = new FileOutputStream("...");
		Runnable r = new Runnable() {
			public void run() {
				try { doService(); }
				catch (IOException e) { ... }
			}
		};
	new ThreadWithOutputStream(r, output).start();
	}
	void doService() throws IOException {
		ThreadWithOutputStream.current().getOutput().write(...);
	// ...
	}
}

 

2.3.2.2ThreadLocal

java.lang.ThreadLocal工具类排除了使用私有线程技术的一个障碍,这个障碍就是对特定Thread子类的依赖。java.lang.ThreadLocal工具类使得线程私有变量可以以特殊的形式增加到任意一段代码中。

TheadLocal类的内部维护了一张相关数据(Object引用)和Thread实例的表。ThreadLocal类中的set和get方法可以存取当前Thread控制的数据。从ThreadLocal类继承来的java.lang.InheriableThreadLocal类可以自动把本线程的变量传递给其创建的任何一个线程。

很多使用ThreadLocal的设计都被视为单态的扩展。多数的ThreadLocal应用程序为每个线程创建了一个资源实例,而不是每个为每个程序创建一个。ThreadLocal的变量被声明为静态的,并且是在包范围内可见,所以这个变量可以在运行于某个线程的一组方法中访问。

ThreadLocal可以以如下的这种方式运行在我们的程序中:

class ServiceUsingThreadLocal { // Fragments
	static ThreadLocal output = new ThreadLocal();
	public void service() {
		try {
			final OutputStream s = new FileOutputStream("...");
			Runnable r = new Runnable() {
				public void run() {
					output.set(s);
					try { doService(); }
					catch (IOException e) { ... }
					finally {
						try { s.close(); }
						catch (IOException ignore) {}
					}
				}
			};
			new Thread(r).start();
		}
		catch (IOException e) { ...}
	}
	void doService() throws IOException {
		((OutputStream)(output.get())).write(...);
		// ...
	}
}

2.3.2.3 应用和结果

拥有线程私有数据的ThreadLocal和Thread子类,一般只在没有更好选择的情况下选用。与其他设计相比(例如,基于会话的设计),其优缺点包括:

  • 把对象引用放在Thread对象内部(或者与其关联),使得运行于同一个线程的方法可以共享这些引用,而不需要以参数的形式传递。在维护诸如当前线程的AccessControlContext(类似于java.security包中那样)等上下文信息,或者为打开相关文件而保存的当前工作目录时,使用这种线程局部的方式是一个很好的选择。ThreadLocal还可以用来创建每个线程的资源池。
  • 使用线程私有变量会隐藏影响行为的参数,这使得更加难以进行错误或遗漏检查。从这个意义上说,线程私有数据存在着和静态全局变量同样的可追踪性问题,尽管没有静态全局变量那么严重。
  • 保证线程私有数据状态的改变(例如,关闭一个输出文件,打开另一个)会影响所有相关的代码。这点实现起来很简单,但是保证所有这些改变间的相互协调确实很难的。
  • 线程内部对线程私有变量进行读写时不需要同步。但是通过currentThread或内部的ThreadLocal表的访问路径不比没有竞争时的同步方法要付出的代价低。所以,只有在对象需要共享且在对个线程间竞争使用该对象的情况下,改用线程私有数据技术才有可能提高系统的整体性能。
  • 使用线程私有数据会增加代码的依赖关系,从而降低代码的重用性。在利用Thread子类的时候,该问题更加突出。例如,doService只有运行在ThreadWithOutputStream类型的线程中的时候才是可用的。如果不在这种情况下使用,调用current方法会引起ClassCastException异常。
  • 通过ThreadLocal增加上下文信息有时是惟一可以使组件和不在调用序列间传递信息的现有代码协调工作的方法。
  • 轻量级可执行程序框架只是间接地基于Thread类,尤其是工作者线程池。所以,在轻量级可执行程序框架中很难把相关数据和执行上下文联合起来。
分类: JAVA 标签: