Java 5中提供了另一种实现线程同步或互斥的机制,即使用Lock和Condition。
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.wait和Object.notity的功能。在等待Condition时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为Condition应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。
一个锁内部可以有多个Condition,即有多路等待和通知,可以参看Jdk1.5提供的Lock和Condition实现的可阻塞队列的应用案例。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。
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();
- }
- }
- }