(友情提示:本博文章欢迎转载,但请注明出处:陈新汉,http://www.blogjava.net/hankchen)
一、并发集合类的选择
同步的集合类Hashtable
和Vector
,以及同步的包装器类Collections.synchronizedMap
和Collections.synchronizedList
,为Map
和List
提供了基本的有条件的线程安全的实现。然而,某些因素使得它们并不适用于具有高度并发性的应用程序中――它们的集合范围的单锁特性对于可伸缩性来说是一个障碍,而且,很多时候还必须在一段较长的时间内锁定一个集合,以防止出现ConcurrentModificationException
s异常。
ConcurrentHashMap
和CopyOnWriteArrayList
实现提供了更高的并发性,同时还保住了线程安全性,只不过在对其调用者的承诺上打了点折扣。ConcurrentHashMap
和CopyOnWriteArrayList
并不是在您使用HashMap
或ArrayList
的任何地方都一定有用,但是它们是设计用来优化某些特定的公用解决方案的。许多并发应用程序将从对它们的使用中获得好处。
总结:在多线程并发情况下,为了避免ConcurrentModificationException
s异常,建议使用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)