线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯
1.Lock相关知识介绍

好比我同时种了几块地的麦子,然后就等待收割。收割时,则是哪块先熟了,先收割哪块。


下面举一个面试题的例子来引出Lock缓存读写锁的案例,一个load()和get()方法返回值为空时的情况;load()的返回值是一个代理对象,而get()却是一个实实在在的对象;所以当返回对象为空是,get()返回null,load()返回一个异常对象;具体分析如下:

一个读写锁的缓存库案例;用上面那道面试题分析则很好理解:

线程阻塞问题:运用多个Condition对象解决

2. Lock接口锁的使用
Lock与synchronized最大区别就是:前者更面向对象;Lock要求程序员手动释放锁,synchronized自动释放
package com.java5.thread.newSkill; //concurrent就是java5新增的线程并发库包
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* Lock接口锁的使用
* Lock与synchronized最大区别就是:前者更面向对象;
* Lock要求程序员手动释放锁,synchronized自动释放。
*/
public class LockTest { public static void main(String[] args) {
new LockTest().init(); } // 该方法的作用是:外部类的静态方法不能实例化内部类对象;所以不能直接在外部类的main实例化,要创建一个中介的普通方法
private void init() {
final Outputer outputer = new Outputer();
// 线程1
new Thread(new Runnable() { @Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("yangkai");
}
}
}).start();
// 线程2
new Thread(new Runnable() { @Override
public void run() {
while (true) {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
outputer.output("123456");
}
}
}).start();
} static class Outputer {
Lock lock = new ReentrantLock(); public void output(String name) {
//上锁
lock.lock();
try {
for (int i = 0; i < name.length(); i++) {
// 读取字符串内一个一个的字符
System.out.print(name.charAt(i));
}
System.out.println();
} finally {
//释放锁;
/*这里放到finally里的原因是:万一上锁的这个方法中有异常发生;
* 那就不执行释放锁代码了,也就是成死锁了;好比你上厕所晕里面了;
* 后面的人等啊等的永远进不去似的
*/
lock.unlock();
}
} } /*
* 如果不使用线程锁Lock会出现以下情况: yangkai 123456 yangkai 1y2a3n4g5k6 ai
*/
}
3、读写锁的案例
package com.java5.thread.newSkill; 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 Queues queues = new Queues();
for ( int i = 0; i < 10; i++) {
final int j = i;
new Thread() {
public void run() {
//此处打标记A,下面注释会提到
/*if(j<10)*/ while(true){
queues.get();
}
}
}.start();
new Thread() {
public void run() {
/*if(j<10)*/while(true) {
queues.put(new Random().nextInt(10000));
}
}
}.start();
}
}
} class Queues {
// 共享数据;只能有一个线程能写改数据,但能有多个线程同时读数据
private Object data = null;
/*这里如果这么写:
* ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
上面的标记A处,如果用while(true)会一直写不读,但是如果不用while死循环则可以正确执行,
比如用if(i<10);目前还没找到原因,希望大牛们看到后指点迷津;
个人猜测:没有用面向接口编程,上锁后,死循环中的内容会无穷之的执行,执行不完不会开锁
*/
ReadWriteLock rwl = new ReentrantReadWriteLock(); // 读的方法,用的是读锁readLock()
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();
}
} // 写的方法;用到写的锁:writeLock()
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();
}
}
}
4. 缓存系统的模拟编写;读写锁的实际应用价值
package com.java5.thread.newSkill; import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock; public class CacheDemo { /**
* 缓存系统的模拟编写;读写锁的实际应用价值
*/
private Map<String, Object> cache = new HashMap<String, Object>(); public static void main(String[] args) { } private ReadWriteLock rwl = new ReentrantReadWriteLock(); public Object getData(String key) {
//如果客户一来读取value数据,则在客户一进去后上一把读锁;防止其他客户再次进行读,产生并发问题
rwl.readLock().lock();
Object value = null;
try {
value = cache.get(key);
if (value == null) {
//如果如果读到的值为空则释放读锁,打开写锁,准备给value赋值
rwl.readLock().unlock();
rwl.writeLock().lock();
try {
//如果打开写锁还为空,则给value赋值aaa
if (value == null) {
value = "aaa"; //实际失去queryDB()
}
} finally {
//使用完写锁后关掉
rwl.writeLock().unlock();
}
//释放写锁后,再次打开读锁,供客户读取value的数据
rwl.readLock().lock();
}
} finally {
//最后客户一读完后释放掉读锁
rwl.readLock().unlock();
}
return value;
}
}
5.新技术condition案例分析;代替wait()和notify()方法
package com.java5.thread.newSkill; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* 面试题: 子线程循环10次,接着主线程循环100次,接着又回到子线程循环10次, 接着再回到主线程又循环100次,如此循环50次,代码如下:
*
* 思路: 编写一个业务类,是为了不把自己绕进去,体现代码的的高聚类特性和代码的健壮性,
* 即共同数据(比如这里的同步锁)或共同算法的若干个方法都可以提到同一个类中编写
*
* 注意:wait()和notify()必须在synchronized关键字内使用;
* 因为this.watit()中用到的this是synchronized()括号内的
* 内容;如果不使用synchronized直接就用wait()会包状态不对的错误
*/
public class ConditionCommunication { public static void main(String[] args) {
final Business business = new Business();
new Thread(new Runnable() { @Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i);
}
}
}).start(); for (int i = 1; i <= 50; i++) {
business.main(i);
} }
} // 编写一个有子方法(用来调用子线程)和主方法(调用主线程)的业务类 class Business {
private boolean bShouldSub = true;
Lock lock = new ReentrantLock();
// condition必须基于lock锁之上的
Condition condition = lock.newCondition(); public void sub(int i) {
try {
lock.lock();
while (!bShouldSub) {
try {
// this.wait();
/*
* condition使用的是await()注意与wait()的区别;
* 因为condition也是Object对象所以也可以调用wait()方法,所以千万别调错了
*/
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub thread sequence of " + j
+ " ,loop of " + i);
}
bShouldSub = false;
// this.notify();
condition.signal();
} finally {
lock.unlock();
}
} public void main(int i) {
/*
* 这里最好用while,但是跟if的效果一样,只是前者代码更健壮, while可以防止线程自己唤醒自己,即通常所说的伪唤醒;
* 相当于一个人做梦不是被别人叫醒而是自己做噩梦突然惊醒; 这时用while可以防止这种情况发生
*/
try {
lock.lock();
while (bShouldSub) {
try {
// this.wait();
condition.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("main thread sequence of " + j
+ " ,loop of " + i);
}
bShouldSub = true;
// this.notify();
condition.signal();
} finally {
lock.unlock();
}
}
}
6. 多个Condition的应用场景;以下是三个condition通讯的代码:
package com.java5.thread.newSkill; import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock; /**
* 多个Condition的应用场景
* 以下是三个condition通讯的代码:
*/
public class ThreeConditionCommunication { public static void main(String[] args) {
final Business business = new Business();
//线程2,老二线程
new Thread(new Runnable() { @Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub(i);
}
}
}).start(); //线程3,老三线程
new Thread(new Runnable() { @Override
public void run() {
for (int i = 1; i <= 50; i++) {
business.sub2(i);
}
}
}).start(); //主线程1,老大线程
for (int i = 1; i <= 50; i++) {
business.main(i);
} } /*编写一个有子方法(用来调用子线程)和主方法(调用主线程)的业务类
* 这个项目下虽然有两个Business类;但是在不同包下所以不影响; 如果在同一包下,那么就要改名或者将其弄成内部类,如果又想要把他当外部类使用,
* 那么将其弄成static 静态的就可以了
*/
static class Business {
private int shouldSub = 1;
Lock lock = new ReentrantLock(); Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition(); public void sub(int i) {
try {
lock.lock();
while (shouldSub != 2) {
try {
condition2.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub thread sequence of " + j
+ " ,loop of " + i);
}
shouldSub = 3;
condition3.signal();
} finally {
lock.unlock();
}
}
public void sub2(int i) {
try {
lock.lock();
while (shouldSub != 3) {
try {
condition3.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 10; j++) {
System.out.println("sub2 thread sequence of " + j
+ " ,loop of " + i);
}
shouldSub = 1;
condition1.signal();
} finally {
lock.unlock();
}
} public void main(int i) {
try {
lock.lock();
while (shouldSub != 1) {
try {;
condition1.await();
} catch (Exception e) {
e.printStackTrace();
}
}
for (int j = 1; j <= 100; j++) {
System.out.println("main thread sequence of " + j
+ " ,loop of " + i);
}
shouldSub = 2;
condition2.signal();
} finally {
lock.unlock();
}
}
}
}
线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯的更多相关文章
- 线程高级应用-心得7-java5线程并发库中阻塞队列Condition的应用及案例分析
1.阻塞队列知识点 阻塞队列重要的有以下几个方法,具体用法可以参考帮助文档:区别说的很清楚,第一个种方法不阻塞直接抛异常:第二种方法是boolean型的,阻塞返回flase:第三种方法直接阻塞. 2. ...
- 线程高级应用-心得8-java5线程并发库中同步集合Collections工具类的应用及案例分析
1. HashSet与HashMap的联系与区别? 区别:前者是单列后者是双列,就是hashmap有键有值,hashset只有键: 联系:HashSet的底层就是HashMap,可以参考HashSe ...
- 线程高级应用-心得4-java5线程并发库介绍,及新技术案例分析
1. java5线程并发库新知识介绍 2.线程并发库案例分析 package com.itcast.family; import java.util.concurrent.ExecutorServi ...
- Java中的线程--并发库中的集合
线程中的知识点基本都已经学完了,看看Java5并发库中提供的集合... 一.可堵塞队列 队列包含固定长度的队列和不固定长度的队列 ArrayBlockQueue中只有put()方法和take()方法才 ...
- Java 并发编程中的 Executor 框架与线程池
Java 5 开始引入 Conccurent 软件包,提供完备的并发能力,对线程池有了更好的支持.其中,Executor 框架是最值得称道的. Executor框架是指java 5中引入的一系列并发库 ...
- Java 并发编程中的 CyclicBarrier 用于一组线程互相等待
Java 5 引入的 Concurrent 并发库软件包中的 CyclicBarrier 是一个同步辅助类,它允许一组线程互相等待,直到到达某个公共屏障点 (common barrier point) ...
- Java中的线程--Lock和Condition实现线程同步通信
随着学习的深入,我接触了更多之前没有接触到的知识,对线程间的同步通信有了更多的认识,之前已经学习过synchronized 实现线程间同步通信,今天来学习更多的--Lock,GO!!! 一.初时Loc ...
- 线程高级应用-心得6-java5线程并发库中同步工具类(synchronizers),新知识大用途
1.新知识普及 2. Semaphore工具类的使用案例 package com.java5.thread.newSkill; import java.util.concurrent.Executor ...
- 细数Android开源项目中那些频繁使用的并发库中的类
这篇blog旨在帮助大家 梳理一下前面分析的那些开源代码中喜欢使用的一些类,这对我们真正理解这些项目是有极大好处的,以后遇到类似问题 我们就可以自己模仿他们也写 出类似的代码. 1.ExecutorS ...
随机推荐
- Linux编译安装Mysql步骤
一. Centos 用 wget 下载需要的软件,保存到目录/home/zwl/MySql/下 wget http://dev.mysql.com/get/Downloads/MySQL-5.5/my ...
- class打包成exe方式
首先运行dos命令 jar -cvf **.jar *.class 把所有文件打包成jar包 用解压器打开jar包 打开meta-inf文件夹 打开用记事本打开main.mf文件 增加语句Main-C ...
- poj1703 Lost Cows
给定集合{1,2,...,n}的一个置换,指定每个位置上在其左方且比其小的数的个数,求该置换. 这题我目前还只会O(n^2)的做法. 以后再用更高效的算法解决. http://poj.org/prob ...
- Mix and Build(简单DP)
Mix and Build Time Limit: 5000MS Memory Limit: 65536K Total Submissions: 3936 Accepted: 1203 Case Ti ...
- C++运算法优先级
- SharePoint自动化系列——Add/Remove “Hold” from items
转载请注明出自天外归云的博客园:http://www.cnblogs.com/LanTianYou/ 问题1: 1.如果SharePoint item被添加了hold,通过UI界面来对SharePoi ...
- php使用 set_include_path
通过set_include_path引用home/lib/image.func.php 1.创建include.php 2.添加如下代码 3.在需要引用的文件中包含include.php文件 < ...
- Linux加载DTS设备节点的过程(以高通8974平台为例)
DTS是Device Tree Source的缩写,用来描述设备的硬件细节.在过去的ARM Linux中,arch/arm/plat-xxx和arch/arm/mach-xxx中充斥着大量的垃圾代码, ...
- 电量检测芯片BQ27510使用心得
最近接触到一款TI的电量检测芯片BQ27510,网上很少有人提及该芯片如何使用,大部分博文都是搬得BQ27510的datasheet,至于真正使用过的很少,该芯片我个人感觉还是非常强大的,能自动学习你 ...
- iOS各版本特性
iOS1 最大特性是具有其他手机无法比拟的触屏功能,使捏拉缩放和慢性滚动变得近乎完美.从而使应用的体验变得更加自然而即时. 缺点:1.不支持复制/粘贴文本. 2.无法在发邮件时添加附件. ...