Condition 将 Object 监视器方法(wait、notify和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意 Lock 实现组合使用,为每个对象提供多个等待 set (wait-set)。其中,Lock 替代了 synchronized 方法和语句的使用,Condition 替代了 Object 监视器方法的使用。
条件(也称为条件队列或条件变量)为线程提供了一个含义,以便在某个状态条件现在可能为 true 的另一个线程通知它之前,一直挂起该线程(即让其“等待”)。因为访问此共享状态信息发生在不同的线程中,所以它必须受保护,因此要将某种形式的锁定与该条件相关联。等待提供一个条件的主要属性是:以原子方式释放相关的锁定,并挂起当前线程,就像 Object.wait 做的那样。
Condition 实例实质上被绑定到一个锁定上。要为特定 Lock 实例获得 Condition 实例,请使用其 newCondition() 方法。
作为一个示例,假定有一个绑定的缓冲区,它支持 put 和 take 方法。如果试图在空的缓冲区上执行 take 操作,则在某一个项变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行 put 操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待 set 中保存 put 线程和 take 线程,这样就可以在缓冲区中的项或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个 Condition 实例来做到这一点。
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();
}
}
}
(ArrayBlockingQueue类提供了这项功能,因此没有理由去实现这个示例类。)
Condition 实现可以提供不同于 Object 监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁定。如果某个实现提供了这样特殊的语义,则该实现必须记录这些语义。
注意,Condition 实例只是一些普通的对象,它们自身可以用作 synchronized 语句中的目标,并且可以调用自己的 wait 和 notification 监视器方法。获取 Condition 实例的监视器锁定或者使用其监视器方法,与获取和该 Condition 相关的 Lock 或使用其 waiting 和 signalling 方法没有什么特定的关系。为了避免混淆,建议除了在其自身的实现中之外,切勿以这种方式使用 Condition 实例。
实例:
package com.bijian.thread; public class SaveThread extends Thread { private String name; // 操作人 private MyCount myCount; // 账户 private int x; // 存款金额 public SaveThread(String name, MyCount myCount, int x) { this.name = name; this.myCount = myCount; this.x = x; } public void run() { myCount.saving(x, name); } }
package com.bijian.thread; public class DrawThread extends Thread { private String name; // 操作人 private MyCount myCount; // 账户 private int x; // 存款金额 DrawThread(String name, MyCount myCount, int x) { this.name = name; this.myCount = myCount; this.x = x; } public void run() { myCount.drawing(x, name); } }
package com.bijian.thread; import java.util.concurrent.locks.Condition; import java.util.concurrent.locks.Lock; import java.util.concurrent.locks.ReentrantLock; public class MyCount { private String oid; // 账号 private int cash; // 账户余额 private Lock lock = new ReentrantLock(); // 账户锁 private Condition _save = lock.newCondition(); // 存款条件 private Condition _draw = lock.newCondition(); // 取款条件 MyCount(String oid, int cash) { this.oid = oid; this.cash = cash; } public void saving(int x, String name) { lock.lock(); // 获取锁 try { if (x > 0) { cash += x; // 存款 System.out.println(name + "存款" + x + ",当前余额为" + cash); } _draw.signalAll(); // 唤醒所有等待线程。 } finally { lock.unlock(); } } public void drawing(int x, String name) { lock.lock(); // 获取锁 try { if (cash - x < 0) { _draw.await(); // 阻塞取款操作 } cash -= x; // 取款 System.out.println(name + "取款" + x + ",当前余额为" + cash); _save.signalAll(); // 唤醒所有存款操作 } catch (InterruptedException e) { e.printStackTrace(); } finally { lock.unlock(); // 释放锁 } } }
package com.bijian.thread; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; public class Main { public static void main(String[] args) { //创建并发访问的账户 MyCount myCount = new MyCount("95599200901215522", 10000); //创建一个线程池 ExecutorService pool = Executors.newFixedThreadPool(2); Thread t1 = new SaveThread("张三", myCount, 2000); Thread t2 = new SaveThread("李四", myCount, 3600); Thread t3 = new DrawThread("王五", myCount, 2700); Thread t4 = new SaveThread("老张", myCount, 600); Thread t5 = new DrawThread("老牛", myCount, 1300); Thread t6 = new DrawThread("胖子", myCount, 800); //执行各个线程 pool.execute(t1); pool.execute(t2); pool.execute(t3); pool.execute(t6); pool.execute(t5); pool.execute(t4); //关闭线程池 pool.shutdown(); } }
运行结果:
张三存款2000,当前余额为12000 李四存款3600,当前余额为15600 王五取款2700,当前余额为12900 胖子取款800,当前余额为12100 老张存款600,当前余额为12700 老牛取款1300,当前余额为11400
相关推荐
在JDK 1.5及以后的版本中,Java引入了`java.util.concurrent`包,其中包含了一些强大的并发工具类,如`Lock`接口和`Condition`接口,这些工具大大提升了生产者消费者模式的实现效率和线程安全性。 生产者消费者模式...
- Condition接口提供了等待/通知机制,允许更精确的线程同步。每个Lock实例可以有多个Condition实例,从而可以实现不同的等待集合。 2. **Phaser** - 在3.1版本中,backport-util-concurrent引入了Phaser,这是一...
9. **并发工具类**:JDK 1.5引入了丰富的并发工具类,如`java.util.concurrent.locks`包中的`ReentrantLock`和`Condition`,提供了更高级的锁机制。 10. **Swing GUI**:虽然在`javax.swing`包中的Swing组件已经...
- Condition接口允许我们创建和管理线程等待队列。每个Lock实例都可以创建一个或多个Condition实例,每个Condition对应一个等待队列。 - `await()`: 当前线程调用此方法后,会被阻塞并从当前条件队列中移除,直到...
例如,Condition接口提供了与Object的wait/notify等效的机制,但功能更加丰富。Semaphere同步工具可以控制通过数量有限的许可。CyclicBarrier可以实现一组线程互相等待直到某个事件发生。CountDownLatch用于一个线程...
在Java中,从JDK 1.5版本开始,引入了java.util.concurrent.locks.Condition接口,它与java.util.concurrent.locks.Lock接口一起使用,提供了一种灵活的条件变量实现。 首先,我们来理解Lock接口。Lock接口提供了一...
Java提供了多种机制来实现这一点,如`synchronized`关键字、`wait()`, `notify()`和`notifyAll()`方法,以及JDK 1.5之后引入的`Lock`和`Condition`接口。`ThreadLocal`类则允许在线程范围内创建独立的数据副本,实现...
- 可以继承其他类(自 JDK 1.5 起无限制)。 - 可以实现接口。 3. **Static Nested Class 和 Inner Class** - Static Nested Class 不依赖于外部类实例,可以独立创建对象。 - Inner Class 必须在外部类实例中...
本示例将围绕“关于线程的例子”进行深入探讨,结合Eclipse3.3、MyEclipse6以及jdk1.5的软件环境,我们将学习如何在Java中创建、管理及同步线程。 首先,让我们了解线程的基本概念。线程是进程内的一个执行单元,每...
JDK 1.5引入了更多的同步工具类,如线程池`ExecutorService`,它允许更有效地管理线程,避免频繁创建和销毁线程带来的开销。`Callable`和`Future`接口提供了获取线程执行结果的能力,而`Future`的`get()`方法可以...
#### 五、JDK1.5之后的高级同步工具 1. **`ReentrantLock`**: - `ReentrantLock`是Java 1.5之后引入的一个可中断、可轮询、可定时的锁。 - 示例代码: ```java private ReentrantLock lock = new ...
例如,在JDK 1.5中引入了新的并发API,并在后续版本中持续改进和优化了这些并发工具,使得Java并发编程变得更加简便和安全。 总之,Java多线程编程极大地增强了程序处理并发任务的能力,但是在享受多线程带来的好处...
它们是基于Java Database Connectivity (JDBC) API的,JDBC是Java平台上的标准接口,允许Java程序与各种类型的数据库进行通信。 sqljdbc4.jar是针对Java RE 1.6及更高版本的,它包含了对JSR 221(JDBC 4.0)规范的...
#### 一、JDK1.5的新特性 ##### 1. 自动装箱与拆箱 - **自动装箱**:指的是将基本数据类型自动转换成对应的包装类对象。 - 例如:`int i = 1; Integer j = i;` - **自动拆箱**:则是将包装类对象自动转换成基本...
虽然也有诸如Apache Commons Pool等第三方库提供了部分支持,但这些解决方案相比JDK内置的支持仍然显得不够完善和便捷。 随着Java 1.5的发布,Sun公司(现在是Oracle公司的一部分)引入了`java.util.concurrent`包...
1.5. 一个“项目”的概念模型 1.6. Maven是Ant的另一种选择么? 1.7. 比较Maven和Ant 1.8. 总结 2. 安装和运行Maven 2.1. 验证你的Java安装 2.2. 下载Maven 2.3. 安装Maven 2.3.1. 在Mac OSX上安装Maven ...
1. 目录 1. 2. 目录 .........................................................................................................................................................1 JVM ........................