并发编程包的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(可以认为也就是一个写锁关键字)实现的缓存要高!因为这个缓存支持多个线程并发读取!以后当我们在实际情况中,遇到过在读写同时存在的情况下,需要进行并发控制,我们就要考虑到使用读写锁!关于读写锁,再提一点,读写锁有个特性,就是线程如果持有读锁,想升级获取写锁,就必须先释放读锁,再去申请写锁。如果线程持有写锁,可以在持有写锁的情况下,再申请一个读锁,然后再释放写锁!
http://my.oschina.net/sharkbobo/blog/270188 --使用ReentrantReadWriteLock进行线程通信
【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(){
- @Override
- public void run() {
- for(int j=0;j<10;j++){
- task.output1();
- }
- }}, "First Thread").start();
- new Thread(new Runnable(){
- @Override
- public void run() {
- for(int j=0;j<10;j++){
- task.output2();
- }
- }}, "Second Thread").start();
- new Thread(new Runnable(){
- @Override
- public void run() {
- for(int j=0;j<10;j++){
- task.output3();
- }
- }}, "Third Thread").start();
- new Thread(new Runnable(){
- @Override
- public 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();
- }
- }
- }
- }