?? 俗话说“工欲善其身,必先利其器”。要想编写好的多线程并发系统,就必须要有一些好的封装类来作为我们的sychironiziton aid。java.util.concurrent包下面就有许多封装好了的类用来帮助我们写好多线程并发系统的新工具。
一,原子类:java.util.concurrent.atomic
??? AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference。它们的语义基本上和volatile一样,只不过封装在一个API了,这个API包含为操作提供的适当的原子方法(要么全做要么不做)。在编写这些实现时利用了现代处理器的特性,所以如果能从硬件和操作系统上得到适当的支持,它们可以是非阻塞(无须线程锁)的。如在Java语言中,++i和i++操作并不是线程安全的,在使用的时候,不可避免的会用到synchronized关键字,在AtomicInteger或AtomicLong上的原子操作getAndIncrement()方法就解决了这个问题。
?? 注:原子类不是从有相似名称的类继承下来的,所以AtomicBoolean不能当Boolean用。
二,线程锁:java.util.concurrent.locks
? sychronized同步方式是基于锁这样一个简单的概念,这种方式有几个缺点:
如果我们重构对线程锁的支持,有几处可以得到提升(这就是Lock出现的原因吧):
Lock接口的几个实现类:
Lock类的具体用法可以上网查查和看看帮助文档,这里就不写了。
?
三,CountDownLatch
???? JDK1.5 API 中文版的解释是:“一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。”想了一会儿还是觉得不好理解,于是看了看英文版的解释,英文是这样解释的:“A synchronization aid that allows one or more threads to wait until a set of operations being performed in other threads completes.”? 才发现又是中文翻译的不到位(还是我理解能力不好?)...直接简单的解释就是:CountDownLatch 可以让一个或者多个线程一直处于等待状态,直到其它线程完成各自的操作之后,一个或者多个线程才开始运行。
??? 具体用法可以查看帮助文档下面是一个例子(来自其它博客):
?
class="java">public class CountDownLatchTest {
// 模拟了100米赛跑,10名选手已经准备就绪,只等裁判一声令下。当所有人都到达终点时,比赛结束。
public static void main(String[] args) throws InterruptedException {
// 开始的倒数锁
final CountDownLatch begin = new CountDownLatch(1);
// 结束的倒数锁
final CountDownLatch end = new CountDownLatch(10);
// 十名选手
final ExecutorService exec = Executors.newFixedThreadPool(10);
for (int index = 0; index < 10; index++) {
final int NO = index + 1;
Runnable run = new Runnable() {
public void run() {
try {
// 如果当前计数为零,则此方法立即返回。
// 等待
begin.await();
Thread.sleep((long) (Math.random() * 10000));
System.out.println("No." + NO + " arrived");
} catch (InterruptedException e) {
} finally {
// 每个选手到达终点时,end就减一
end.countDown();
}
}
};
exec.submit(run);
}
System.out.println("Game Start");
// begin减一,开始游戏
begin.countDown();
// 等待end变为0,即所有选手到达终点
end.await();
System.out.println("Game Over");
exec.shutdown();
}
}
?
?
?三,ConcurrentHashMap,CopyOnWriteArrayList
????? ConcurrentHashMap类是标准HashMap的并发版本。它改进了Collections类中提供的SychironizedMap()功能,因为那些方法返回的集合中包含的锁要比需要的多。
????? CopyOnWriteArrayList的用法请参考Java中的CopyOnWrite容器 感觉写的比较好。
?四,Queue
???? Java中有些多线程编程模式在很大程度上都依赖于Queue实现 线程的安全性。Queue常用来在线程之间传递工作单元,这个模式通常适合用Queue最简单的并发扩展BlockingQueue来实现。
???? 1.BlockingQueue
?????? BlockingQueue的两个特性:
???? BlockingQueue接口的两个基本实现:LinkedBlockingQueue,ArrayBlockingQueue.它们的特性以及使用场景就不说了,看名字就知道了。
??? 2.使用工作单元
???? 比如说,你有一个表示工作单元的MyAwesomeClass类,想要用多线程方式处理,你可能会想到用BlockingQueue<MyAwesomeClass>来表示工作队列。但是请想想用BlockingQueue<WorkUnit<MyAwesomeClass>>这样会不会更好呢(WorkUnit这个名字随便自己怎么取)?
public class WorkUnit<T>
private final T workunit;
public WorkUnit(T workUnit){
this.workunit=workUnit
}
public T getWork(){
return workunit;
}
?
? 有了这层间接引用(有没有感觉有点Proxy模式的味道?),不用修改原来的类(这里就是MyAwesomeClass)就可以添加额外的数据或者处理方法了如:
一个BlockingQueue + workunit的例子:
?
package concurrent.blockingQueue;
public abstract class Pet {
protected final String name;
public Pet(String name) {
super();
this.name = name;
}
public abstract void examine();
}
class Cat extends Pet{
public Cat(String name) {
super(name);
}
@Override
public void examine() {
System.out.println("Meow!");
}
}
class Dog extends Pet{
public Dog(String name) {
super(name);
}
@Override
public void examine() {
System.out.println("Woof!");
}
}
?
?
package concurrent.blockingQueue;
//workUnit
public class Appointment<T> {
private final T toBeSeen;
public T getPatient() {
return toBeSeen;
}
public Appointment(T toBeSeen) {
this.toBeSeen = toBeSeen;
}
}
?
?
package concurrent.blockingQueue;
import java.util.concurrent.BlockingDeque;
public class Veterinarian extends Thread {
protected final BlockingDeque<Appointment<Pet>> appts;
protected String text="";
protected final int restTime;
private boolean shutdown=false;
public Veterinarian(BlockingDeque<Appointment<Pet>> appts, int restTime) {
super();
this.appts = appts;
this.restTime = restTime;
}
public synchronized void shutdown(){
shutdown=true;
}
public void run(){
while(!shutdown){
this.seePatient();
try {
Thread.sleep(restTime);
} catch (Exception e) {
shutdown=true;
}
}
}
public void seePatient(){
try {
Appointment<Pet> ap=appts.take();
Pet patient=ap.getPatient();
patient.examine();
} catch (Exception e) {
shutdown=true;
}
}
}
?
?除了简单的take()和offer() API ,BlockingQueue还提供了另外一种与队列交互的方式,这种控制力度更大。就是带有超时放入或者取出的操作,它还允许线程在遇到问题时可以从与队列的交互中退出来,转而做点其它的事情。
?
以上内容全部总结与《Java程序员修炼之道》如果想要具体,深入的了解建议看看原书。
?