(友情提示:本博文章欢迎转载,但请注明出处:陈新汉,http://www.blogjava.net/hankchen)
一、并发集合类的选择
同步的集合类Hashtable和Vector,以及同步的包装器类Collections.synchronizedMap和Collections.synchronizedList,为Map和List提供了基本的有条件的线程安全的实现。然而,某些因素使得它们并不适用于具有高度并发性的应用程序中――它们的集合范围的单锁特性对于可伸缩性来说是一个障碍,而且,很多时候还必须在一段较长的时间内锁定一个集合,以防止出现ConcurrentModificationExceptions异常。
ConcurrentHashMap和CopyOnWriteArrayList实现提供了更高的并发性,同时还保住了线程安全性,只不过在对其调用者的承诺上打了点折扣。ConcurrentHashMap和CopyOnWriteArrayList并不是在您使用HashMap或ArrayList的任何地方都一定有用,但是它们是设计用来优化某些特定的公用解决方案的。许多并发应用程序将从对它们的使用中获得好处。
总结:在多线程并发情况下,为了避免ConcurrentModificationExceptions异常,建议使用ConcurrentHashMap和CopyOnWriteArrayList。
还有下面的几个可以考虑:ConcurrentLinkedQueue、CopyOnWriteArraySet、LinkedBlockingQueue、ArrayBlockingQueue
二、高效的乘除运算
服务器计算时,对于乘除运算,采用下面的方式:
A*2=a<<1
A/2=a>>1
这样可以提高运算效率。
三、原子自增器
多线程环境下, AtomicInteger 可用在应用程序中(如以原子方式增加的计数器),并且不能用于替换Integer。但是,此类确实扩展了 Number,允许那些处理基于数字类的工具和实用工具进行统一访问。
例如:
private AtomicInteger bomdIdCreator = new AtomicInteger(); //自增序列号
/**
*得到新的炸弹ID,保持自增
*@return
*/
public int getNewBombID(){
return bomdIdCreator.addAndGet(1);
}
四、多线程锁机制实现
多线程环境下,为了避免资源竞争,引入了锁机制。一般实现锁机制有下面几种方法:
1. 同步方法、同步块:synchronized
2. 监视器方法:(wait、notify和notifyAll)
3. ReentrantLock
注意:ReentrantLock是一个可重入的互斥锁Lock,它具有与使用 synchronized 方法和语句所访问的隐式监视器锁相同的一些基本行为和语义,但功能更强大。
例如:建议总是立即实践,使用 lock 块来调用 try,在之前/之后的构造中,最典型的代码如下:
class X {
private final ReentrantLock lock = new ReentrantLock();
// ...
public void m() {
lock.lock(); // block until condition holds
try {
// ... method body
} finally {
lock.unlock()
}
}
}
五、线程池的实现方式
Doug Lea 编写了一个优秀的并发实用程序开放源码库 util.concurrent,它包括互斥、信号量、诸如在并发访问下执行得很好的队列和散列表之类集合类以及几个工作队列实现。该包中的 ThreadPoolExecutor类是一种有效的、广泛使用的以工作队列为基础的线程池的正确实现。您无须尝试编写您自己的线程池,这样做容易出错,相反您可以考虑使用 util.concurrent中的一些实用程序。
线程池可以解决两个不同问题:由于减少了每个任务调用的开销,它们通常可以在执行大量异步任务时提供增强的性能,并且还可以提供绑定和管理资源(包括执行集合任务时使用的线程)的方法。
强烈建议程序员使用较为方便的Executors工厂方法Executors.newCachedThreadPool()(无界线程池,可以进行自动线程回收)、Executors.newFixedThreadPool(int)(固定大小线程池)和Executors.newSingleThreadExecutor()(单个后台线程),它们均为大多数使用场景预定义了设置。
例如:
public class ThreadPoolExecutorTest {
final static ExecutorService threadPool=Executors.newCachedThreadPool(); //简单线程池实现
/**
* @param args
*/
public static void main(String[] args) {
for(int i=0;i<10;i++){
threadPool.execute(new Runnable(){
public void run() {
System.out.println("aaa"+this.getClass().getName());
//do other things
}
});
}
}
}
六、实现定时任务的几种方式比较
1. 使用原始的Timer类
2. ScheduledThreadPoolExecutor (JDK 1.5新增)
3. Quatz开源项目
从Java 5.0开始,java.util.concurrent包中增加了一个ScheduledThreadPoolExecutor类,用来实现定时任务和线程池的管理,比起Timer简陋的实现是要强大得多。利用ScheduledThreadPoolExecutor的scheduleAtFixedRate()和scheduleWithFixedDelay()两个方法就可以实现任务调度的基本功能,从前用Timer实现的功能应该要迁移到scheduleWithFixedDelay()上了。
注意:ScheduledThreadPoolExecutor是实现ScheduledExecutorService接口的具体类。
1)public static final ScheduledExecutorService scheduledExecutor = Executors.newScheduledThreadPool(2);
2)public static final ScheduledThreadPoolExecutor scheduledExecutor=new ScheduledThreadPoolExecutor(2);
这两种方式是一样的,都是得到一个可调度的线程池。
ScheduledThreadPoolExecutor与Timer的区别:
1. Timer的主要方法有:
// 安排在指定的时间执行
void schedule(TimerTask task, Date time)
// 安排在指定的时间开始以重复的延时执行
void schedule(TimerTask task, Date firstTime, long period)
// 安排在指定的延迟后执行
void schedule(TimerTask task, long delay)
// 安排在指定的延迟后开始以重复的延时执行
void schedule(TimerTask task, long delay, long period)
// 安排在指定的时间开始以重复的速率执行
void scheduleAtFixedRate(TimerTask task, Date firstTime, long period)
// 安排在指定的延迟后开始以重复的速率执行
void scheduleAtFixedRate(TimerTask task, long delay, long period)
注:重复的延时和重复的速率的区别在于,前者是在前一个任务的执行结束后间隔period时间再开始下一次执行;而scheduleAtFixedRate则是会尽量按照任务的初始时间来按照间隔period时间执行。如果一次任务执行由于某些原因被延迟了,用schedule()调度的后续任务同样也会被延迟,而用scheduleAtFixedRate()则会快速的开始两次或者多次执行,是后续任务的执行时间能够赶上来。
2. ScheduledThreadPoolExecutor的主要方法:
// 在指定的延迟后执行
<V>ScheduledFuture<V> schedule(Callable<V> callable, long delay, TimeUnit unit)
// 在指定的延迟后执行
ScheduledFuture<?> schedule(Runnable command, long delay, TimeUnit unit)
// 在指定的延迟后以固定速率执行(类似Timer.scheduleAtFixedRate())
ScheduledFuture<?> scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit)
// 在指定的延迟后以固定间隔执行(类似Timer.schedule())
ScheduledFuture<?> scheduleWithFixedDelay(Runnable command, long initialDelay, long delay, TimeUnit unit)
比较:
(1)Timer对调度的支持是基于绝对时间的,因此任务对系统时间的改变是敏感的;而ScheduledThreadPoolExecutor支持相对时间。
(2)Timer使用单线程方式来执行所有的TimerTask,如果某个TimerTask很耗时则会影响到其他TimerTask的执行;而ScheduledThreadPoolExecutor则可以构造一个固定大小的线程池来执行任务。
(3)Timer不会捕获由TimerTask抛出的未检查异常,故当有异常抛出时,Timer会终止,导致未执行完的TimerTask不再执行,新的TimerTask也不能被调度;ScheduledThreadPoolExecutor对这个问题进行了妥善的处理,不会影响其他任务的执行。
结论:Timer有这么多的缺点,如果是使用JDK1.5以上的话,应该没什么理由要使用Timer来进行调度。
(友情提示:本博文章欢迎转载,但请注明出处:陈新汉,http://www.blogjava.net/hankchen)