线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

时间:2023-11-19 10:42:50

1.Lock相关知识介绍

线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

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

线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

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

线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

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

线程高级应用-心得5-java5线程并发库中Lock和Condition实现线程同步通讯

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

线程高级应用-心得5-java5线程并发库中Lock和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();
}
}
}
}