在JDK1.5之前,Java中要进行业务并发时,通常需要有程序员独立完成代码实现,当然也有一些开源的框架提供了这些功能,但是这些依然没有JDK自带的功能使用起来方便。而当针对高质量Java多线程并发程序设计时,为防止死蹦等现象的出现,比如使用java之前的wait()、notify()和synchronized等,每每需要考虑性能、死锁、公平性、资源管理以及如何避免线程安全性方面带来的危害等诸多因素,往往会采用一些较为复杂的安全策略,加重了程序员的开发负担。
万幸的是,JDK1.5出现之后,Sun大神(Doug Lea)终于为我们这些可怜的小程序员推出了java.util.concurrent工具包以简化并发完成。开发者们借助于此,将有效的减少竞争条件(race conditions)和死锁线程。concurrent包很好的解决了这些问题,为我们提供了更实用的并发程序模型。
部分API类图如下:
实用类介绍
Semaphore
计数信号量,只对可用许可的号码进行计数,并采取相应的行动。Semephore通常用于限制可以访问某些资源的线程数目。acquire方法请求一个许可,有可用资源之前一直阻塞。release方法释放一个许可给Semaphore。这里是一个实际的情况,大家排队上厕所,厕所只有两个位置,来了10个人需要排队。
public class MySemaphore extends Thread {
private int id;
private Semaphore s;
public MySemaphore(int id, Semaphore s) {
this.id = id;
this.s = s;
}
public void run() {
if (s.availablePermits() > 0)
System.out.println("顾客[" + this.id + "]进入厕所,有空位");
else
System.out.println("顾客[" + this.id + "]进入厕所,无空位");
try {
s.acquire();
System.out.println("顾客[" + this.id + "]获得坑位");
Thread.sleep((int)(Math.random() * 1000));
System.out.println("顾客[" + this.id + "]使用完毕");
s.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
Semaphore s = new Semaphore(2);
ExecutorService service = Executors.newCachedThreadPool();
for (int i=0; i<10; i++) {
service.submit(new MySemaphore(i, s));
}
service.shutdown();
s.acquireUninterruptibly(2);// 请求2个信号量,在2个信号量可用之前,一直阻塞
System.out.println("使用完毕,需要清扫了");
s.release(2);// 释放2个新号量给Semaphore
}
}
CountDownLatch
同步辅助类。调用countDown(),在当前计数到达零之前,await()方法会一直阻塞。可以将初始化计数为1的CountDownLatch作为一个简单的开关锁存器。用初始化为N的CountDownLatch可以使一个线程在N个线程执行某项操作之前或者一个线程执行N次操作之前一直等待。
public class MyCountDownLatch {
static final CountDownLatch begin = new CountDownLatch(2);
static final CountDownLatch end = new CountDownLatch(10);
public static void main(String[] args) throws InterruptedException {
ExecutorService service = Executors.newFixedThreadPool(10);
for (int i=0; i<10; i++) {
final int number = i + 1;
Runnable task = new Runnable() {
@Override
public void run() {
try {
begin.await();// 一直阻塞
Thread.sleep((int)(Math.random() * 10000));
System.out.println("NO." + (number + 1) + " arrived");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
end.countDown();
}
}
};
service.submit(task);
}
System.out.println("Game start");
begin.countDown();
end.await();
System.out.println("Game over");
service.shutdown();
}
}
注:上面调用begin.await()时一直阻塞。除非在调用await()方法之前,调用begin.countDown(),让计数到达零。
CyclicBarrier
同步辅助类。允许一组线程相互等待,直到达到一个公共屏障点。最重要的参数为参与者个数,创建对象时通过构造方法传入。另外重要的方法是await(),当所有参与者对应的线程都调用await()方法,这些线程都可以继续执行,否则就会等待。
public class MyCyclicBarrier {
// 步行
private static final int[] timeWalk = {5, 8, 15, 15, 10};
// 自驾游
private static final int[] timeSelf = {1, 3, 4, 4, 5};
// 旅游大巴
private static final int[] timeBus = {2, 4, 6, 6, 7};
public static String now() {
return new SimpleDateFormat("HH:mm:ss").format(new Date());
}
static class Tour extends Thread {
private CyclicBarrier barrier;
private String tourName;
private int[] times;
public Tour(CyclicBarrier barrier, String tourName, int[] times) {
this.barrier = barrier;
this.tourName = tourName;
this.times = times;
}
public void run() {
try {
Thread.sleep(times[0] * 1000);
System.out.println(now() + ": " + tourName + " reached ShenZhen");
barrier.await();
Thread.sleep(times[1] * 1000);
System.out.println(now() + ": " + tourName + " reached GuangZhou");
barrier.await();
Thread.sleep(times[2] * 1000);
System.out.println(now() + ": " + tourName + " reached ShaoGuan");
barrier.await();
Thread.sleep(times[3] * 1000);
System.out.println(now() + ": " + tourName + " reached ChangSha");
barrier.await();
Thread.sleep(times[4] * 1000);
System.out.println(now() + ": " + tourName + " reached WhHan");
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
public static void main(String[] args) {
CyclicBarrier barrier = new CyclicBarrier(3);
ExecutorService service = Executors.newFixedThreadPool(3);
service.submit(new Tour(barrier, "WalkTour", timeWalk));
service.submit(new Tour(barrier, "SelfTour", timeSelf));
// 当我们把下面的程序注释了,会发现程序阻塞了,无法继续进行下去
service.submit(new Tour(barrier, "BusTour", timeBus));
service.shutdown();
}
}
ReentrantLock
互斥锁定Lock,与synchronized方法和语句具有相同的基本行为和语义,但功能更强大。lock方法请求锁,若资源未锁定,则可以获取锁。unlock方法释放持有的锁资源。
public class MyReentrantLock extends Thread {
private int id;
private TestReentrantLock test;
public MyReentrantLock(int id, TestReentrantLock test) {
this.id = id;
this.test = test;
}
public void run() {
this.test.print(id);
}
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
TestReentrantLock lock = new TestReentrantLock();
for (int i=0; i<10; i++) {
service.submit(new MyReentrantLock(i, lock));
}
service.shutdown();
}
}
class TestReentrantLock {
private ReentrantLock lock = new ReentrantLock();
public void print(int id) {
try {
lock.lock();
System.out.println(id + ", 获得!");
Thread.sleep((int)(Math.random() * 1000));
} catch (Exception e) {
e.printStackTrace();
} finally {
System.out.println(id + ", 释放!");
lock.unlock();
}
}
}
BlockingQueue
该接口是线程安全的,主要用于实现生产者-消费者队列。可以设置初始化大小,若不设置,默认Integer.MAX_VALUE。可以通过add(E)、put(E)、offer(E)添加元素,但添加元素不能为null,否则报NullPointerException。可以通过take()移除队列的头部元素。
public class MyBlockingQueue extends Thread {
private static BlockingQueue<String> queue = new LinkedBlockingQueue<String>(3);
private int id;
public MyBlockingQueue(int id) {
this.id = id;
}
public void run() {
try {
queue.put(String.valueOf(id));
System.out.println("{" + id + "} in queque");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
ExecutorService service = Executors.newCachedThreadPool();
for (int i=0; i<10; i++)
service.submit(new MyBlockingQueue(i));
Runnable task = new Runnable() {
@Override
public void run() {
while (true) {
try {
Thread.sleep((int)(Math.random() * 1000));
if (queue.isEmpty())
break;
System.out.println(queue.take() + " has take");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
};
service.submit(task);
service.shutdown();
}
}
CompletionService
将生产新的异步任务与已完成任务的结果分离开来的服务。submit方法提交一个Runnable任务执行,并返回一个Future代表任务。take()释放并移除代表下一个即将完成任务的Future。
public class MyCompletionService implements Callable<String> {
private int id;
public MyCompletionService(int id) {
this.id = id;
}
@Override
public String call() throws Exception {
int time = (int)(Math.random() * 1000);
try {
System.out.println("{" + this.id + "}, 启动");
Thread.sleep(time);
System.out.println("{" + this.id + "}, 关闭");
} catch (Exception e) {
e.printStackTrace();
}
return this.id + "," + time;
}
public static void main(String[] args) throws InterruptedException, ExecutionException {
ExecutorService executor = Executors.newCachedThreadPool();
final CompletionService<String> cs = new ExecutorCompletionService<String>(executor);
for (int i=0; i<10; i++) {
cs.submit(new MyCompletionService(i));
}
for (int i=0; i<10; i++)
System.out.println(cs.take().get());
executor.shutdown();
}
}
ScheduledExecutorService
使用Executors的execute(Runnable)方法和ExecutorService的submit(Runnable)方法,提交的请求,为0延迟进行安排。所有schedule接受相对周期和延迟作为参数,ScheduledExecutorService提供了scheduleAtFixedRate 和 scheduleWithFixedDelay 方法创建并执行某些在取消前一直定期运行的任务。
public class TestSchedule {
public static void main(String[] args) {
final ScheduledExecutorService scheduleService = Executors.newScheduledThreadPool(2);
final Runnable beeper = new Runnable() {
int count = 0;
@Override
public void run() {
System.out.println(new Date() + " beep " + (++count));
}
};
// 1秒后运行,每隔两秒运行一次
final ScheduledFuture beeperHandler = scheduleService.scheduleAtFixedRate(beeper, 1, 2, TimeUnit.SECONDS);
// 2秒后运行,上次任务结束后等待5秒继续运行
final ScheduledFuture beeperHandler2 = scheduleService.scheduleWithFixedDelay(beeper, 2, 5, TimeUnit.SECONDS);
// 30秒后关闭schedule
scheduleService.schedule(new Runnable() {
@Override
public void run() {
beeperHandler.cancel(true);
beeperHandler2.cancel(true);
}
}, 30, TimeUnit.SECONDS);
scheduleService.shutdown();
}
}
参考资料:http://www.open-open.com/bbs/view/1320131360999