jiangxinyu的专栏

叶子的离开,是因为风的追求还是树的不挽留?

Java并发之读写锁Lock和条件阻塞Condition的应用

Java 5中提供了另一种实现线程同步或互斥的机制,即使用LockCondition

Lock比传统线程模型中的synchronized方式更加面向对象,也提供了更多可选择的锁机制。与生活中的锁类似,锁本身也是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须使用同一个Lock对象。锁是上在代表要操作的资源的类的内部方法中,而不是线程代码中。

 

Lock使用示例:


import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 // An example of using Lock.
 public class LockTest {
 
     public static void main(String[] args) {
         new LockTest().init();
 
     }
 
     private void init() {
         final Outputer outputer = new Outputer();
         new Thread(new Runnable() {
             @Override
             public void run() {
                 while (true) {
                     try {
                         Thread.sleep(10);
 
                     } catch(InterruptedException e) {
                         e.printStackTrace();
 
                     }
                     outputer.output("aaaaaaaaaaa");
 
                 }
 
 
             }
 
         }).start();
 
         new Thread(new Runnable() {
             @Override
             public void run() {
                 while (true) {
                     try {
                         Thread.sleep(10);
 
                     } catch(InterruptedException e) {
                         e.printStackTrace();
 
                     }
                     outputer.output("bbbbbbbbbbb");
 
                 }
 
 
             }
 
         }).start();
 
 
     }
 
     static class Outputer {
         private Lock lock = new ReentrantLock();
         public void output(String name) {
             int len = name.length();
             lock.lock();
             try {
                 for (int i = 0; i < len; i++) {
                     System.out.print(name.charAt(i));
 
                 }
                 System.out.println();
 
             } finally {
                 lock.unlock();
 
             }
 
         }
 
     }
 
 }

读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由JVM控制的,我们只需要上好相应的锁即可。如果代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果代码修改数据,只能有一个人在写,并不能同时读取,那就上写销锁。总之,读的时候上读锁,写的时候上写锁。

Java读写锁示例:

import java.util.Random;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
  
 public class ReadWriteLockTest {
     public static void main(String[] args) {
         final MyQueue queue = new MyQueue();
         for (int i = 0; i < 3; i++) {
             new Thread() {
                 public void run() {
                     while (true) {
                         queue.get();
                     }
                 }
  
             }.start();
  
             new Thread() {
                 public void run() {
                     while (true) {
                         queue.put(new Random().nextInt(10000));
                     }
                 }
  
             }.start();
         }
  
     }
 }
  
 class MyQueue {
     // 共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。
     private Object data = null; 
     ReadWriteLock  rwl  = new ReentrantReadWriteLock();
  
     public void get() {
         rwl.readLock().lock();
         try {
             System.out.println(Thread.currentThread().getName()
                     + " be ready to read data!");
             Thread.sleep((long) (Math.random() * 1000));
             System.out.println(Thread.currentThread().getName()
                     + "have read data :" + data);
         } catch (InterruptedException e) {
             e.printStackTrace();
         } finally {
             rwl.readLock().unlock();
         }
     }
  
     public void put(Object data) {
  
         rwl.writeLock().lock();
         try {
             System.out.println(Thread.currentThread().getName()
                     + " be ready to write data!");
             Thread.sleep((long) (Math.random() * 1000));
             this.data = data;
             System.out.println(Thread.currentThread().getName()
                     + " have write data: " + data);
         } catch (InterruptedException e) {
             e.printStackTrace();
         } finally {
             rwl.writeLock().unlock();
         }
     }
     
 }

使用

import java.util.HashMap;
 import java.util.Map;
 import java.util.concurrent.locks.ReadWriteLock;
 import java.util.concurrent.locks.ReentrantReadWriteLock;
 
 // Using a ReadWriteLock to implement a cache.
 public class CacheDemo {
 
     private Map < String,
     Object > cache = new HashMap < String,
     Object > ();
     private ReadWriteLock rwl = new ReentrantReadWriteLock();
 
     public static void main(String[] args) {
         CacheDemo cache = new CacheDemo();
         Object obj = cache.getData("");
         System.out.println(obj.toString());
 
     }
 
     // Get the value from DB if the value does not exist,and then return it.
     public Object getData(String key) {
         rwl.readLock().lock();
         Object value = null;
         try {
             value = cache.get(key);
             if (value == null) {
                 // Must release read lock before acquiring write lock
                 rwl.readLock().unlock();
                 rwl.writeLock().lock();
                 try {
                     // Recheck state because another thread might have acquired
                     // write lock and changed state before we did.
                     if (value == null) {
                         // Here may access Database.
                         // ...
                         value = "Data";
 
                     }
 
                 } finally {
                     rwl.writeLock().unlock();
 
                 }
                 rwl.readLock().lock();
 
             }
 
         } finally {
             rwl.readLock().unlock();
 
         }
         return value;
 
     }
 
 }

Condition的功能类似在传统线程技术中的Object.waitObject.notity的功能。在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

 

一个锁内部可以有多个Condition,即有多路等待和通知,可以参看Jdk1.5提供的LockCondition实现的可阻塞队列的应用案例。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。

 

JDK文档中提供了一个很不错的示例(http://docs.oracle.com/javase/6/docs/api/ ),用Condition实现一个阻塞队列,代码如下:

import java.util.concurrent.locks.Condition;
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
 public class BoundedBuffer {
     final Lock lock = new ReentrantLock();
     final Condition notFull = lock.newCondition();
     final Condition notEmpty = lock.newCondition();
 
     final Object[] items = new Object[100];
     int putptr,
     takeptr,
     count;
 
     public void put(Object x) throws InterruptedException {
         lock.lock();
         try {
             while (count == items.length)
             notFull.await();
             items[putptr] = x;
             if (++putptr == items.length)
             putptr = 0;
             ++count;
             notEmpty.signal();
 
         } finally {
             lock.unlock();
 
         }
 
     }
 
     public Object take() throws InterruptedException {
         lock.lock();
         try {
             while (count == 0)
             notEmpty.await();
             Object x = items[takeptr];
             if (++takeptr == items.length)
             takeptr = 0;
             --count;
             notFull.signal();
             return x;
 
         } finally {
             lock.unlock();
 
         }
 
     }
 
 }


阅读更多
个人分类: Java AND Android
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭