Lock 接口
ReentrantLock 是 Lock 的具体类, 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 提供的方法有:
ReadWriteLock 的例子:
public class FileOperator{ // 初始化一个 ReadWriteLock ReadWriteLock lock = new ReentrantReadWriteLock(); public String read() { // 得到 readLock 并锁定 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 提供以下方法:
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 { if(available){ consumed.await(); // 放弃 lock 进入睡眠 } /* 生产苹果 */ System.out.println("Apple produced."); available = true; produced.signal(); // 发信号唤醒等待这个 Condition 的线程 } finally { lock.unlock(); } }
public void consume() throws InterruptedException { lock.lock(); try { if(!available){ produced.await();// 放弃 lock 进入睡眠 } /* 吃苹果 */ System.out.println("Apple consumed."); available = false; consumed.signal();// 发信号唤醒等待这个 Condition 的线程 } finally { lock.unlock(); } } } |
ConditionTester:
public class ConditionTester {
public static void main(String[] args) throws InterruptedException{ final Basket basket = new Basket(); // 定义一个 producer Runnable producer = new Runnable() { public void run() { try { basket.produce(); } catch (InterruptedException ex) { ex.printStackTrace(); } } }; // 定义一个 consumer Runnable consumer = new Runnable() { public void run() { try { basket.consume(); } catch (InterruptedException ex) { ex.printStackTrace(); } } }; // 各产生 10 个 consumer 和 producer ExecutorService service = Executors.newCachedThreadPool(); for(int i=0; i < 10; i++) service.submit(consumer); Thread.sleep(2000); for(int i=0; i<10; i++) service.submit(producer); service.shutdown(); } } |
Java 5.0 里新加了 4 个协调线程间进程的同步装置,它们分别是 Semaphore, CountDownLatch, CyclicBarrier 和 Exchanger.
Semaphore:
用来管理一个资源池的工具, Semaphore 可以看成是个通行证,线程要想从资源池拿到资源必须先拿到通行证, Semaphore 提供的通行证数量和资源池的大小一致。如果线程暂时拿不到通行证,线程就会被阻断进入等待状态。以下是一个例子:
public class Pool { ArrayList pool = null; Semaphore pass = null; public Pool(int size){ // 初始化资源池 pool = new ArrayList(); for(int i=0; i pool.add("Resource "+i); } //Semaphore 的大小和资源池的大小一致 pass = new Semaphore(size); } public String get() throws InterruptedException{ // 获取通行证 , 只有得到通行证后才能得到资源 pass.acquire(); return getResource(); } public void put(String resource){ // 归还通行证,并归还资源 pass.release(); releaseResource(resource); } private synchronized String getResource() { String result = pool.get(0); pool.remove(0); System.out.println("Give out "+result); return result; } private synchronized void releaseResource(String resource) { System.out.println("return "+resource); pool.add(resource); } } |
SemaphoreTest:
public class SemaphoreTest { public static void main(String[] args){ final Pool aPool = new Pool(2); Runnable worker = new Runnable() { public void run() { String resource = null; try { // 取得 resource resource = aPool.get(); } catch (InterruptedException ex) { ex.printStackTrace(); } // 用 resource 做工作 System.out.println("I worked on "+resource); // 归还 resource aPool.put(resource); } }; ExecutorService service = Executors.newCachedThreadPool(); for(int i=0; i<20; i++){ service.submit(worker); } service.shutdown(); } } |