Java并发编程实践 目录

并发编程 01—— ThreadLocal

并发编程 02—— ConcurrentHashMap

并发编程 03—— 阻塞队列和生产者-消费者模式

并发编程 04—— 闭锁CountDownLatch 与 栅栏CyclicBarrier

并发编程 05—— Callable和Future

并发编程 06—— CompletionService : Executor 和 BlockingQueue

并发编程 07—— 任务取消

并发编程 08—— 任务取消 之 中断

并发编程 09—— 任务取消 之 停止基于线程的服务

并发编程 10—— 任务取消 之 关闭 ExecutorService

并发编程 11—— 任务取消 之 “毒丸”对象

并发编程 12—— 任务取消与关闭 之 shutdownNow 的局限性

并发编程 13—— 线程池的使用 之 配置ThreadPoolExecutor 和 饱和策略

并发编程 14—— 线程池 之 整体架构

并发编程 15—— 线程池 之 原理一

并发编程 16—— 线程池 之 原理二

并发编程 17—— Lock

并发编程 18—— 使用内置条件队列实现简单的有界缓存

并发编程 19—— 显式的Conditon 对象

并发编程 20—— AbstractQueuedSynchronizer 深入分析

并发编程 21—— 原子变量和非阻塞同步机制

概述

第1部分  定义

第2部分 实例

参考

第1 部分 定义

Condition 是一种广义的内置条件队列,接口如下:

public interface Condition {
// 造成当前线程在接到信号或被中断之前一直处于等待状态。
void await();
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
boolean await(long time, TimeUnit unit);
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
long awaitNanos(long nanosTimeout);
// 造成当前线程在接到信号之前一直处于等待状态。
void awaitUninterruptibly();
// 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
boolean awaitUntil(Date deadline);
void signal(); // 唤醒一个等待线程
void signalAll(); // 唤醒所有等待线程
}

  一个Condition和一个Lock关联在一起,就像一个条件队列和一个内置锁相关联一样。要创建一个Condition,可以在相关联的Lock上调用Lock.newCondition方法。正如Lock比内置加锁提供了更为丰富的功能,Condition同样比内置条件队列提供了更丰富的功能:在每个锁上可存在多个等待、条件等待可以是可中断的或不可中断的、基于时限的等待,以及公平的或非公平的队列操作。

  与内置条件队列不同的是,对于每个Lock,可以有任意数量的Condition对象。Condition对象继承了相关的Lock对象的公平性,对于公平的锁,线程会依照FIFO顺序从Condition.await中释放。

  下面程序给出了有界缓存的另一种实现,即使用两个Condition,分别为notFull和notEmpty,用于表示“非满”与“非空”两个条件谓词。当缓存为空时,take将阻塞并等待notEmpty,此时put向notEmpty发送信号,可以解除任何在take中阻塞的线程。

 /**
* 14.11 使用显式条件变量的有界缓存
* @ClassName: ConditionBoundedBuffer
* @author xingle
* @param <T>
* @date 2015-2-9 上午11:16:32
*/
public class ConditionBoundedBuffer<T> {
protected final Lock lock = new ReentrantLock();
//条件谓词:notFull (count < items.length)
private final Condition notFull = lock.newCondition();
//条件谓词:notEmpty (count > 0)
private final Condition notEmpty = lock.newCondition();
private static final int BUFFER_SIZE = 100;
@GuardedBy("lock")
private final T[] items = (T[]) new Object[BUFFER_SIZE];
@GuardedBy("lock")
private int tail,head,count; //阻塞并直到:notFull
public void put(T x) throws InterruptedException {
lock.lock();
try{
while(count == items.length)
notFull.await();
items[tail] = x;
if(++tail == items.length)
tail = 0;
++count;
notEmpty.signal();
}finally{
lock.unlock();
}
} //阻塞并直到:notEmpty
public T take() throws InterruptedException{
lock.lock();
try{
while(count==0)
notEmpty.await();
T x = items[head];
items[head] = null;
if(++head == items.length)
head = 0;
--count;
notEmpty.signal();
return x;
}finally{
lock.unlock();
}
} }

  ConditionBoundedBuffer的行为和BoundedBuffer相同,但它对条件队列的使用方式更容易理解——在分析使用多个Condition的类时,比分析一个使用单一内部队列加多个条件队列的类简单得多。通过将两个条件谓词分开并放到两个等待线程集中,Condition使其更容易满足单次通知的需求。signal比singalAll更高效,它能极大地减少在每次缓存操作中发生的上下文切换与锁请求的次数。

第2部分 实例

本示范简单模拟银行帐户的存取款活动,帐户余额大于等于取款金额时允许取款;帐户余额小于1000时允许存款(这与真实业务逻辑不符合,只是技术上需要才如此做的)。

1. 实体Account类

 /**
*
* @ClassName: Account
* @author xingle
* @date 2015-2-9 下午5:54:02
*/
public class Account {
private final Lock lock = new ReentrantLock(); // Condition对象
private final Condition condDeposit = lock.newCondition();
private final Condition condWithdraw = lock.newCondition();
private int balance; public Account(int balance){
this.balance = balance;
} //取钱
public void withdraw(int drawAmount) {
lock.lock();
try {
//如果账户余额不足,则取现方法阻塞
while (balance < drawAmount){
System.out.println("取钱阻塞");
condWithdraw.await();
}
//执行取钱
balance -= drawAmount;
System.out.println(Thread.currentThread().getName() + " 取钱:" + drawAmount + " 账户余额为:"+ balance);
//唤醒存钱线程
condDeposit.signal();
} catch (InterruptedException ex) {
ex.printStackTrace();
} finally {
lock.unlock();
}
} //存钱
public void deposit(int depositAmount){
lock.lock();
try{
//如果账户余额大于1000,存钱方法阻塞
while(balance >1000){
System.out.println("存钱阻塞");
condDeposit.await();
}
balance += depositAmount;
System.out.println(Thread.currentThread().getName() + " 存款:" + depositAmount + " 账户余额为:"+ balance);
//唤醒取钱线程
condWithdraw.signal();
} catch(InterruptedException ex){
ex.printStackTrace();
}
finally{
lock.unlock();
}
}
}

2. 调用类(DepositDrawTest类)

 /**
*
* @ClassName: DepositDrawTest
* @author xingle
* @date 2015-2-10 上午10:38:44
*/
public class DepositDrawTest { public static void main(String[] args) {
// 创建一个账户,初始账户余额为0
Account acct = new Account(0);
new DrawThread("取钱者1", acct, 400).start();
new DrawThread("取钱者2", acct, 800).start();
new DepositThread("存款者甲", acct, 600).start();
new DepositThread("存款者乙", acct, 800).start();
new DepositThread("存款者丙", acct, 400).start(); }
} class DrawThread extends Thread {
// 模拟用户账户
private Account account;
// 每次取数数
private int drawAmount; public DrawThread(String name, Account account, int drawAmount) {
super(name);
this.account = account;
this.drawAmount = drawAmount;
} public void run() {
for (int i = 0; i < 3; i++) {
account.withdraw(drawAmount);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
} class DepositThread extends Thread {
// 模拟用户账户
private Account account;
// 每次存钱数
private int depositAmount; public DepositThread(String name, Account account, int depositAmount) {
super(name);
this.account = account;
this.depositAmount = depositAmount;
} public void run() {
for (int i = 0; i < 3; i++) {
account.deposit(depositAmount);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}

执行结果:

3. 总结

  1. 如果取款金额大于余额则不让取款,等存款队列继续存钱,余额足够支付时再让取款。
  2. 如果存款过多(大于1000),则存款不让存了,等取款队列把钱取走,余额降低到1000以下时,可以继续存款。
  3. 这样就允许多次连续取款(只要帐户有钱),多次连续存款(余额不能大于1000),而不是存款、取款依次调用。

参考:

1. Java:多线程,使用同步锁(Lock)时利用Condition类实现线程间通信

2. 怎么理解Condition

并发编程 19—— 显式的Conditon 对象的更多相关文章

  1. Java并发编程之显式锁机制

    我们之前介绍过synchronized关键字实现程序的原子性操作,它的内部也是一种加锁和解锁机制,是一种声明式的编程方式,我们只需要对方法或者代码块进行声明,Java内部帮我们在调用方法之前和结束时加 ...

  2. 使用显式的Lock对象取代synchronized关键字进行同步

    Java SE5的java.util.concurrent类库还包含有定义在java.util.concurrent.locks中的显式的互斥机制.Lock对象必须被显式地创建.锁定和释放.因此,它与 ...

  3. 《Java并发编程实战》第三章 对象的共享 读书笔记

    一.可见性 什么是可见性? Java线程安全须要防止某个线程正在使用对象状态而还有一个线程在同一时候改动该状态,并且须要确保当一个线程改动了对象的状态后,其它线程能够看到发生的状态变化. 后者就是可见 ...

  4. 《Java并发编程实战》第四章 对象的组合 读书笔记

    一.设计线程安全的类 在设计线程安全类的过程中,须要包括下面三个基本要素:  . 找出构成对象状态的全部变量.  . 找出约束状态变量的不变性条件.  . 建立对象状态的并发訪问管理策略. 分析对象的 ...

  5. Java编程思想学习笔记-使用显式的Lock对象

    若要保证后台线程在trylock()之前运行得到锁,可加“屏障”,如下1,2,3步,而trylock()不管设定时间与否都不会阻塞主线程而是立即返回: //: concurrency/AttemptL ...

  6. java并发编程读书笔记(1)-- 对象的共享

    1. 一些原则 RIM(Remote Method Invocation):远程方法调用 Race Condition:竞态条件 Servlet要满足多个线程的调用,必须是线程安全的 远程对象,即通过 ...

  7. JAVA并发编程实战---第三章:对象的共享(2)

    线程封闭 如果仅仅在单线程内访问数据,就不需要同步,这种技术被称为线程封闭,它是实现线程安全性的最简单的方式之一.当某个对象封闭在一个线程中时,这种方法将自动实现线程安全性,即使被封闭的对象本生不是线 ...

  8. java并发编程实战:第四章----对象的组合

    一.设计线程安全的类 找出构造对象状态的所有变量(若变量为引用类型,还包括引用对象中的域) 约束状态变量的不变性条件 建立对象状态的并发访问管理策略(规定了如何维护线程安全性) 1.收集同步需求(找出 ...

  9. Java并发编程学习笔记(三)——对象的组合

    重要概念: 1.在设计线程安全类的过程中,需要包含以下三个基本要素: (1)找出构成对象状态的所有变量. (2)找出约束状态变量的不变性条件. (3)建立对象状态的并发访问管理策略. 2.

随机推荐

  1. Array基本操作

    // defined array object val arr0= ) val arr1= Array(") println(arr1()) arr1()="Hello Spark ...

  2. YL-64 颜色传感器

    TCS3200颜色传感器是一款全彩的颜色检测器,包括了一块TAOS TCS3200RGB感应芯片和4个白光LED灯,TCS3200能在一定的范围内检测和测量几乎所有的可见光.它适合于色度计测量应用领域 ...

  3. Ubuntu 16.04 802.1x 有线连接

    Ubuntu下使用MentoHUST搞定 锐捷校园网认证网络 http://www.linuxidc.com/Linux/2013-10/91157.htm

  4. CentOS7安装memcached

    三台linux服务器系统CentOS7 一台memcached IP:192.168.155.134 一台Apache IP:192.168.155.130 一台nginx IP:192.168.15 ...

  5. JavaScript DOM 编程艺术(第2版)读书笔记 (9)

    三位一体的网页 结构层:由HTML或XHTML之类的标记语言负责创建: 表示层:由CSS负责完成: 行为层:负责内容应该如何响应事件这一问题.这是由JavaScript语言和DOM主宰的领域. 分离 ...

  6. FusionCharts或其它flash的div图层总是浮在最上层的问题

    div的图层由div的style中的z-index来决定,z-index是层垂直屏幕的坐标,0最小,越大的话位置越靠上. 由于FusionCharts的图表都放在div中,如果页面还有其他的div,将 ...

  7. 错误,这个如何解决呢?内存溢出的问提。把JAVA_OPTS="-server -XX:PermSize=64M -XX:MaxPermSize=128m 还是不行

    java.lang.OutOfMemoryError: PermGen space at java.lang.ClassLoader.defineClass1(Native Method) at ja ...

  8. error: linker command failed with exit code 1 解决方法之一

    出现这种错误的原因可能很多,以下是我遇到的一种情况: 向项目中添加了新文件,没有加入compile source 编译报错: ld: symbol(s) not found for architect ...

  9. Js 实现tab切换效果

    今天商城系统的后台要添加一个Tab切换的效果,一开始没有思路想要自己去实践这个效果 从网上找jquery 已经有了很好看的案例,实现之后我来学习下思路是如何完成的

  10. python协程与异步I/O

    协程 首先要明确,线程和进程都是系统帮咱们开辟的,不管是thread还是process他内部都是调用的系统的API,而对于协程来说它和系统毫无关系; 协程不同于线程的是,线程是抢占式的调度,而协程是协 ...