Java多线程编程--(7)学习Java5.0 并发编程包--Lock & Condition

来源:互联网 发布:以公司名义开淘宝店 编辑:程序博客网 时间:2024/05/16 16:56

今天我们来试验一下Java5.0 并发编程包的java.util.concurrent.locks子包。这里提供了一些对多线程进行互斥同步控制的类,用以取代之前我们一直使用的synchronized关键字,wait()、notify()、notifyAll()方法。

【Lock】

Lock是一个接口,和关键字synchronized的功能一致,对多线程进行互斥控制!其使用更加面向对象化!对互斥代码段的控制也更加精细化!Lock接口有三个实现类:ReentrantLock,ReentrantReadWriteLock.ReadLock,ReentrantReadWriteLock.WriteLock。后两者都是ReentrantReadWriteLock的子类,这个我们后面再讲。这里先看一个ReentrantLock的例子:

package cn.test;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class ReentrantLockTest {Lock lock = new ReentrantLock();/** * 业务方法,需要进行并发互斥控制!我们此处不使用synchronized关键字 */public void doBisiness(){// 加锁,加锁成功的线程进入执行,不成功的线程阻塞在这里!lock.lock();try{// 相应的业务处理方法}catch(Exception e){}finally{// 释放锁,为防止业务方法处理有异常抛出,通常将释放锁的代码写到finally块中lock.unlock();}}}

这次加锁的过程就是调用一个对象的方法!感觉很舒服,比synchronized更易用并且容易理解!

【ReadWriteLock】

ReadWriteLock也是一个接口,用来实现读写锁的概念(在synchronized的时代,这个是不被支持的)。读写锁的概念,就是读锁与读锁之间不互斥,读锁与写锁,写锁和写锁之间互斥!这种加锁模式,在某些情况下能提高效率!ReentrantReadWriteLock是ReadWriteLock接口的唯一个实现类。其内部提供了两个子类,ReadLock,WriteLock分别来提供读锁和写锁的功能!我们看一个缓存系统的例子,我们需要一个并发环境下的缓存系统,可以缓存多个对象,要求稳定并且性能高效:

package cn.test;import java.util.HashMap;import java.util.Map;import java.util.concurrent.locks.ReadWriteLock;import java.util.concurrent.locks.ReentrantReadWriteLock;public class CacheDemo {// 将缓存系统设计为单例private static final CacheDemo INSTANCE = new CacheDemo();// 内部通过Map的形式来缓存数据private Map<String, Object> cacheMap = new HashMap<String, Object>();// 并发互斥通过读写锁进行private ReadWriteLock lock = new ReentrantReadWriteLock();private CacheDemo(){}public static CacheDemo getInstance(){return INSTANCE;}/** * 添加缓存的方法,这里是覆盖式添加! * @param key * @param value */public void put(String key, Object value){// 加写锁lock.writeLock().lock();try{cacheMap.put(key, value);}finally{lock.writeLock().unlock();}}/** * 从缓存中读取数据,使用读锁即可,多个线程可以同时从缓存中读取数据! * @param key * @return */public Object get(String key){// 局部变量,无需进行并发互斥控制Object value = null;// 加读锁lock.readLock().lock();try{value = cacheMap.get(key);}finally{lock.readLock().unlock();}return value;}/** * 从缓存中移出数据!需要使用写锁! * @param key * @return */public boolean remove(String key){boolean result = true;// 删除缓存,加写锁lock.writeLock().lock();try{cacheMap.remove(key);}catch(Exception e){result = false;}finally{lock.writeLock().unlock();}return result;}}

利用读写锁实现的缓存效率比利用ReentrantLock或synchronized(可以认为也就是一个写锁关键字)实现的缓存要高!因为这个缓存支持多个线程并发读取!以后当我们在实际情况中,遇到过在读写同时存在的情况下,需要进行并发控制,我们就要考虑到使用读写锁!关于读写锁,再提一点,读写锁有个特性,就是线程如果持有读锁,想升级获取写锁,就必须先释放读锁,再去申请写锁。如果线程持有写锁,可以在持有写锁的情况下,再申请一个读锁,然后再释放写锁!

【Condition】

Condition可以用来实现wait、notify、notifyAll方法的线程间同步通信。但其有增强的地方。我们先看一下wait等方法的使用情况:我们先得到一个对象的监视器,进入同步代码块,发现有些条件限制,我们的线程就要wait在这个对象监视器上,如果我们有100个线程,这100条线程有可能会因为不同的条件限制而要wait,但结果是他们都wait在同一个对象监视器上。一旦有另一个线程处理完了某种条件限制,这种限制的解除会让这100条线程中的5条可以继续执行,但这个线程无法通过notify去精确通知这5条线程,他只能调用notifyAll,去通知所有线程,然后其中95条再重新获取到对象监视器后发现不得不继续wait!!这是wait等方法低效的地方,Condition就对这种情况进行了很好的改进!在使用同一个锁进行互斥控制的线程,可以在不同的Condition对象上进行等待和被唤醒!这就是"多路Condition"的概念!

我们看个这方面的例子,有4条线程,老大,老二,老三,老四,最后实现的效果是:老大执行10次,接着老二再执行20次,接着老三再执行30次,接着老四再执行40次,再循环回去,进行10遍!这里涉及到了顺序执行的问题,我们就利用多路Condition进行精确控制:

package cn.test;import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class MultiConditionsTest {public static void main(String[] args) {final Task task = new Task();// 启动四个线程,分别执行任务类中的4个任务!new Thread(new Runnable(){@Overridepublic void run() {for(int j=0;j<10;j++){task.output1();}}}, "First Thread").start();new Thread(new Runnable(){@Overridepublic void run() {for(int j=0;j<10;j++){task.output2();}}}, "Second Thread").start();new Thread(new Runnable(){@Overridepublic void run() {for(int j=0;j<10;j++){task.output3();}}}, "Third Thread").start();new Thread(new Runnable(){@Overridepublic void run() {for(int j=0;j<10;j++){task.output4();}}}, "Forth Thread").start();}private static class Task {private int ctrlOrder = 0;// 创建一个互斥控制的锁private Lock lock = new ReentrantLock();// 调用锁的newCondtion方法,得到一个Condtion对象!private Condition op1Condition = lock.newCondition();private Condition op2Condition = lock.newCondition();private Condition op3Condition = lock.newCondition();private Condition op4Condition = lock.newCondition();public void output1() {lock.lock();try {// 使用Condition也会产生假醒现象!所以此处要用while循环进行判断!while (ctrlOrder % 4 != 0) {// 调用Condtion对象的await方法,进行等待op1Condition.await();}// 执行相应的业务逻辑for (int i = 0; i < 10; i++) {System.out.println(Thread.currentThread().getName()+ " out put " + i);}ctrlOrder++;// 通过调用特定的Condition的signal来唤醒在该Condition对象上等待的线程!精确唤醒!op2Condition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void output2() {lock.lock();try {// 使用Condition也会产生假醒现象!所以此处要用while循环进行判断!while (ctrlOrder % 4 != 1) {// 调用Condtion对象的await方法,进行等待op2Condition.await();}// 执行相应的业务逻辑for (int i = 0; i < 20; i++) {System.out.println(Thread.currentThread().getName()+ " out put " + i);}ctrlOrder++;// 通过调用特定的Condition的signal来唤醒在该Condition对象上等待的线程!精确唤醒!op3Condition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void output3() {lock.lock();try {// 使用Condition也会产生假醒现象!所以此处要用while循环进行判断!while (ctrlOrder % 4 != 2) {// 调用Condtion对象的await方法,进行等待op3Condition.await();}// 执行相应的业务逻辑for (int i = 0; i < 30; i++) {System.out.println(Thread.currentThread().getName()+ " out put " + i);}ctrlOrder++;// 通过调用特定的Condition的signal来唤醒在该Condition对象上等待的线程!精确唤醒!op4Condition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}public void output4() {lock.lock();try {// 使用Condition也会产生假醒现象!所以此处要用while循环进行判断!while (ctrlOrder % 4 != 3) {// 调用Condtion对象的await方法,进行等待op4Condition.await();}// 执行相应的业务逻辑for (int i = 0; i < 40; i++) {System.out.println(Thread.currentThread().getName()+ " out put " + i);}ctrlOrder++;// 通过调用特定的Condition的signal来唤醒在该Condition对象上等待的线程!精确唤醒!op1Condition.signal();} catch (InterruptedException e) {e.printStackTrace();} finally {lock.unlock();}}}}

输出结果为:

First Thread out put 0First Thread out put 1First Thread out put 2First Thread out put 3First Thread out put 4First Thread out put 5First Thread out put 6First Thread out put 7First Thread out put 8First Thread out put 9Second Thread out put 0Second Thread out put 1Second Thread out put 2Second Thread out put 3Second Thread out put 4Second Thread out put 5Second Thread out put 6Second Thread out put 7Second Thread out put 8Second Thread out put 9Second Thread out put 10Second Thread out put 11Second Thread out put 12Second Thread out put 13Second Thread out put 14Second Thread out put 15Second Thread out put 16Second Thread out put 17Second Thread out put 18Second Thread out put 19Third Thread out put 0Third Thread out put 1Third Thread out put 2Third Thread out put 3Third Thread out put 4Third Thread out put 5Third Thread out put 6Third Thread out put 7Third Thread out put 8Third Thread out put 9Third Thread out put 10Third Thread out put 11Third Thread out put 12Third Thread out put 13Third Thread out put 14Third Thread out put 15Third Thread out put 16Third Thread out put 17Third Thread out put 18Third Thread out put 19Third Thread out put 20Third Thread out put 21Third Thread out put 22Third Thread out put 23Third Thread out put 24Third Thread out put 25Third Thread out put 26Third Thread out put 27Third Thread out put 28Third Thread out put 29Forth Thread out put 0Forth Thread out put 1Forth Thread out put 2Forth Thread out put 3Forth Thread out put 4Forth Thread out put 5Forth Thread out put 6Forth Thread out put 7Forth Thread out put 8Forth Thread out put 9Forth Thread out put 10Forth Thread out put 11Forth Thread out put 12Forth Thread out put 13Forth Thread out put 14Forth Thread out put 15Forth Thread out put 16Forth Thread out put 17Forth Thread out put 18Forth Thread out put 19Forth Thread out put 20Forth Thread out put 21Forth Thread out put 22Forth Thread out put 23Forth Thread out put 24Forth Thread out put 25Forth Thread out put 26Forth Thread out put 27Forth Thread out put 28Forth Thread out put 29Forth Thread out put 30Forth Thread out put 31Forth Thread out put 32Forth Thread out put 33Forth Thread out put 34Forth Thread out put 35Forth Thread out put 36Forth Thread out put 37Forth Thread out put 38Forth Thread out put 39First Thread out put 0First Thread out put 1First Thread out put 2First Thread out put 3First Thread out put 4First Thread out put 5First Thread out put 6First Thread out put 7First Thread out put 8First Thread out put 9

上述控制通过synchronized也能实现,但无法做到同一个锁上,多路Condition精确控制的效果!

今天主要写了关于Lock和Condition一些内容,既然Java5.0中提供了这么好的东西,我们在以后写代码中,如果需要线程的互斥同步控制,就不要再使用老方式了,采用这个新的更高效易用的方式吧!

原创粉丝点击