//Cached ExecutorService
ExecutorService cachedService = Executors.newCachedThreadPool();
//Fixed number of ExecutorService
ExecutorService fixedService = Executors.newFixedThreadPool(3);
//Single ScheduledExecutorService
ScheduledExecutorService singleScheduledService =
Executors.newSingleThreadScheduledExecutor();
//Fixed number of ScheduledExecutorService
ScheduledExecutorService fixedScheduledService =
Executors.newScheduledThreadPool(3);
4:Lockers和Condition接口
在多线程编程里面一个重要的概念是锁定,如果一个资源是多个线程共享的,为了保证数据的完整性,在进行事务性操作时需要将共享资源锁定,这样可以保证在做事务性操作时只有一个线程能对资源进行操作,从而保证数据的完整性。在5.0以前,锁定的功能是由Synchronized关键字来实现的,这样做存在几个问题:
每次只能对一个对象进行锁定。若需要锁定多个对象,编程就比较麻烦,一不小心就会出现死锁现象。
如果线程因拿不到锁定而进入等待状况,是没有办法将其打断的
在Java 5.0里出现两种锁的工具可供使用,下图是这两个工具的接口及其实现:
Lock接口
ReentrantLock是Lock的具体类,Lock提供了以下一些方法:
lock(): 请求锁定,如果锁已被别的线程锁定,调用此方法的线程被阻断进入等待状态。
tryLock():如果锁没被别的线程锁定,进入锁定状态,并返回true。若锁已被锁定,返回false,不进入等待状态。此方法还可带时间参数,如果锁在方法执行时已被锁定,线程将继续等待规定的时间,若还不行才返回false。
unlock():取消锁定,需要注意的是Lock不会自动取消,编程时必须手动解锁。
代码:
//生成一个锁
Lock lock = new ReentrantLock();
public void accessProtectedResource() {
lock.lock(); //取得锁定
try {
//对共享资源进行操作
} finally {
//一定记着把锁取消掉,锁本身是不会自动解锁的
lock.unlock();
}
}
ReadWriteLock接口
为了提高效率有些共享资源允许同时进行多个读的操作,但只允许一个写的操作,比如一个文件,只要其内容不变可以让多个线程同时读,不必做排他的锁定,排他的锁定只有在写的时候需要,以保证别的线程不会看到数据不完整的文件。ReadWriteLock可满足这种需要。ReadWriteLock内置两个Lock,一个是读的Lock,一个是写的Lock。多个线程可同时得到读的Lock,但只有一个线程能得到写的Lock,而且写的Lock被锁定后,任何线程都不能得到Lock。ReadWriteLock提供的方法有:
readLock(): 返回一个读的lock
writeLock(): 返回一个写的lock, 此lock是排他的。
ReadWriteLock的例子:
public class FileOperator{
//初始化一个ReadWriteLock
ReadWriteLock lock = new ReentrantReadWriteLock();
public String read() {
Lock readLock = lock.readLock();
readLock.lock();
try {
//做读的工作
return "Read something";
} finally {
readLock.unlock();
}
}
public void write(String content) {
//得到writeLock并锁定
Lock writeLock = lock.writeLock();
writeLock.lock();
try {
//做读的工作
} finally {
writeLock.unlock();
}
}
}
需要注意的是ReadWriteLock提供了一个高效的锁定机理,但最终程序的运行效率是和程序的设计息息相关的,比如说如果读的线程和写的线程同时在等待,要考虑是先发放读的lock还是先发放写的lock。如果写发生的频率不高,而且快,可以考虑先给写的lock。还要考虑的问题是如果一个写正在等待读完成,此时一个新的读进来,是否要给这个新的读发锁,如果发了,可能导致写的线程等很久。等等此类问题在编程时都要给予充分的考虑。
Condition接口:
有时候线程取得lock后需要在一定条件下才能做某些工作,比如说经典的Producer和Consumer问题,Consumer必须在篮子里有苹果的时候才能吃苹果,否则它必须暂时放弃对篮子的锁定,等到Producer往篮子里放了苹果后再去拿来吃。而Producer必须等到篮子空了才能往里放苹果,否则它也需要暂时解锁等Consumer把苹果吃了才能往篮子里放苹果。在Java 5.0以前,这种功能是由Object类的wait(), notify()和notifyAll()等方法实现的,在5.0里面,这些功能集中到了Condition这个接口来实现,Condition提供以下方法:
await():使调用此方法的线程放弃锁定,进入睡眠直到被打断或被唤醒。
signal(): 唤醒一个等待的线程
signalAll():唤醒所有等待的线程
Condition的例子:
public class Basket {
Lock lock = new ReentrantLock();
//产生Condition对象
Condition produced = lock.newCondition();
Condition consumed = lock.newCondition();
boolean available = false;
public void produce() throws InterruptedException {
lock.lock();
try {