Java 5中提供了另一种实现线程同步或互斥的机制,即使用Lock和Condition。
Lock比传统线程模型中的synchronized方式更加面向对象,也提供了更多可选择的锁机制。与生活中的锁类似,锁本身也是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须使用同一个Lock对象。锁是上在代表要操作的资源的类的内部方法中,而不是线程代码中。
Lock使用示例:
1 import java.util.concurrent.locks.Lock; 2 import java.util.concurrent.locks.ReentrantLock; 3 4 // An example of using Lock. 5 public class LockTest { 6 7 public static void main(String[] args) { 8 new LockTest().init(); 9 10 } 11 12 private void init() { 13 final Outputer outputer = new Outputer(); 14 new Thread(new Runnable() { 15 @Override 16 public void run() { 17 while (true) { 18 try { 19 Thread.sleep(10); 20 21 } catch(InterruptedException e) { 22 e.printStackTrace(); 23 24 } 25 outputer.output("aaaaaaaaaaa"); 26 27 } 28 29 30 } 31 32 }).start(); 33 34 new Thread(new Runnable() { 35 @Override 36 public void run() { 37 while (true) { 38 try { 39 Thread.sleep(10); 40 41 } catch(InterruptedException e) { 42 e.printStackTrace(); 43 44 } 45 outputer.output("bbbbbbbbbbb"); 46 47 } 48 49 50 } 51 52 }).start(); 53 54 55 } 56 57 static class Outputer { 58 private Lock lock = new ReentrantLock(); 59 public void output(String name) { 60 int len = name.length(); 61 lock.lock(); 62 try { 63 for (int i = 0; i < len; i++) { 64 System.out.print(name.charAt(i)); 65 66 } 67 System.out.println(); 68 69 } finally { 70 lock.unlock(); 71 72 } 73 74 } 75 76 } 77 78 }
读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由JVM控制的,我们只需要上好相应的锁即可。如果代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果代码修改数据,只能有一个人在写,并不能同时读取,那就上写销锁。总之,读的时候上读锁,写的时候上写锁。
Java读写锁示例:
1 import java.util.Random; 2 import java.util.concurrent.locks.ReadWriteLock; 3 import java.util.concurrent.locks.ReentrantReadWriteLock; 4 5 public class ReadWriteLockTest { 6 public static void main(String[] args) { 7 final MyQueue queue = new MyQueue(); 8 for (int i = 0; i < 3; i++) { 9 new Thread() { 10 public void run() { 11 while (true) { 12 queue.get(); 13 } 14 } 15 16 }.start(); 17 18 new Thread() { 19 public void run() { 20 while (true) { 21 queue.put(new Random().nextInt(10000)); 22 } 23 } 24 25 }.start(); 26 } 27 28 } 29 } 30 31 class MyQueue { 32 // 共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。 33 private Object data = null; 34 ReadWriteLock rwl = new ReentrantReadWriteLock(); 35 36 public void get() { 37 rwl.readLock().lock(); 38 try { 39 System.out.println(Thread.currentThread().getName() 40 + " be ready to read data!"); 41 Thread.sleep((long) (Math.random() * 1000)); 42 System.out.println(Thread.currentThread().getName() 43 + "have read data :" + data); 44 } catch (InterruptedException e) { 45 e.printStackTrace(); 46 } finally { 47 rwl.readLock().unlock(); 48 } 49 } 50 51 public void put(Object data) { 52 53 rwl.writeLock().lock(); 54 try { 55 System.out.println(Thread.currentThread().getName() 56 + " be ready to write data!"); 57 Thread.sleep((long) (Math.random() * 1000)); 58 this.data = data; 59 System.out.println(Thread.currentThread().getName() 60 + " have write data: " + data); 61 } catch (InterruptedException e) { 62 e.printStackTrace(); 63 } finally { 64 rwl.writeLock().unlock(); 65 } 66 } 67 68 }
使用
1 import java.util.HashMap; 2 import java.util.Map; 3 import java.util.concurrent.locks.ReadWriteLock; 4 import java.util.concurrent.locks.ReentrantReadWriteLock; 5 6 // Using a ReadWriteLock to implement a cache. 7 public class CacheDemo { 8 9 private Map < String, 10 Object > cache = new HashMap < String, 11 Object > (); 12 private ReadWriteLock rwl = new ReentrantReadWriteLock(); 13 14 public static void main(String[] args) { 15 CacheDemo cache = new CacheDemo(); 16 Object obj = cache.getData(""); 17 System.out.println(obj.toString()); 18 19 } 20 21 // Get the value from DB if the value does not exist,and then return it. 22 public Object getData(String key) { 23 rwl.readLock().lock(); 24 Object value = null; 25 try { 26 value = cache.get(key); 27 if (value == null) { 28 // Must release read lock before acquiring write lock 29 rwl.readLock().unlock(); 30 rwl.writeLock().lock(); 31 try { 32 // Recheck state because another thread might have acquired 33 // write lock and changed state before we did. 34 if (value == null) { 35 // Here may access Database. 36 // ... 37 value = "Data"; 38 39 } 40 41 } finally { 42 rwl.writeLock().unlock(); 43 44 } 45 rwl.readLock().lock(); 46 47 } 48 49 } finally { 50 rwl.readLock().unlock(); 51 52 } 53 return value; 54 55 } 56 57 }
:
Condition的功能类似在传统线程技术中的Object.wait和Object.notity的功能。在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
一个锁内部可以有多个Condition,即有多路等待和通知,可以参看Jdk1.5提供的Lock和Condition实现的可阻塞队列的应用案例。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。
JDK文档中提供了一个很不错的示例(http://docs.oracle.com/javase/6/docs/api/ ),用Condition实现一个阻塞队列,代码如下:
1 import java.util.concurrent.locks.Condition; 2 import java.util.concurrent.locks.Lock; 3 import java.util.concurrent.locks.ReentrantLock; 4 5 public class BoundedBuffer { 6 final Lock lock = new ReentrantLock(); 7 final Condition notFull = lock.newCondition(); 8 final Condition notEmpty = lock.newCondition(); 9 10 final Object[] items = new Object[100]; 11 int putptr, 12 takeptr, 13 count; 14 15 public void put(Object x) throws InterruptedException { 16 lock.lock(); 17 try { 18 while (count == items.length) 19 notFull.await(); 20 items[putptr] = x; 21 if (++putptr == items.length) 22 putptr = 0; 23 ++count; 24 notEmpty.signal(); 25 26 } finally { 27 lock.unlock(); 28 29 } 30 31 } 32 33 public Object take() throws InterruptedException { 34 lock.lock(); 35 try { 36 while (count == 0) 37 notEmpty.await(); 38 Object x = items[takeptr]; 39 if (++takeptr == items.length) 40 takeptr = 0; 41 --count; 42 notFull.signal(); 43 return x; 44 45 } finally { 46 lock.unlock(); 47 48 } 49 50 } 51 52 }