8.Lock&Condition实现线程同步通信

来源:互联网 发布:上海犇迩网络骗局 编辑:程序博客网 时间:2024/05/14 07:01

一、Lock比传统线程模型中的synchronized方式更加面向对象,与生活中的锁类似,锁本身也应该是一个对象。两个线程执行的代码片段要实现同步互斥的效果,它们必须用同一个Lock对象。

import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;public class LockTest {public static void main(String[] args) {new LockTest().init();}private void init(){final Outputer outputer = new Outputer();new Thread(new Runnable(){public void run() {while(true){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}outputer.output("zhangxiaoxiang");}}}).start();new Thread(new Runnable(){public void run() {while(true){try {Thread.sleep(10);} catch (InterruptedException e) {e.printStackTrace();}outputer.output("lihuoming");}}}).start();}static class Outputer{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();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();//关闭锁}}}}
二、读写锁:分为读锁和写锁,多个读锁不互斥,读锁与写锁互斥,写锁与写锁互斥,这是由jvm自己控制的,你只要上好相应的锁即可。如果你的代码只读数据,可以很多人同时读,但不能同时写,那就上读锁;如果你的代码修改数据,只能有一个人在写,且不能同时读取,那就上写锁。总之,读的时候上读锁,写的时候上写锁!

import java.util.Random;import java.util.concurrent.locks.ReentrantReadWriteLock;public class ReadWriteLockTest {public static void main(String[] args) {final Queue3 q3 = new Queue3();for(int i=0;i<3;i++){new Thread(){public void run(){while(true){q3.get();}}}.start();}for(int i=0;i<3;i++){new Thread(){public void run(){while(true){q3.put(new Random().nextInt(10000));}}}.start();}}}class Queue3{private Object data = null;//共享数据,只能有一个线程能写该数据,但可以有多个线程同时读该数据。private ReentrantReadWriteLock 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();//写锁释放}}}
三、在等待 Condition 时,允许发生“虚假唤醒”,这通常作为对基础平台语义的让步。对于大多数应用程序,这带来的实际影响很小,因为 Condition 应该总是在一个循环中被等待,并测试正被等待的状态声明。某个实现可以随意移除可能的虚假唤醒,但建议应用程序程序员总是假定这些虚假唤醒可能发生,因此总是在一个循环中等待。

import java.util.concurrent.locks.Condition;import java.util.concurrent.locks.Lock;import java.util.concurrent.locks.ReentrantLock;/** * 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次, * 接着再回到主线程又循环100,如此循环50次,请写出程序。  */public class ConditionCommunication {public static void main(String[] args) {final Business business = new Business();new Thread(new Runnable() {public void run() {for (int i = 1; i <= 50; i++) {business.sub(i);}}}).start();for (int i = 1; i <= 50; i++) {business.main(i);}} static class Business {Lock lock = new ReentrantLock();Condition condition = lock.newCondition();private boolean bShouldSub = true;public void sub(int i) {lock.lock();try {while (!bShouldSub) {try {condition.await();//condition的特有方法} 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();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}public void main(int i) {lock.lock();try {while (bShouldSub) {try {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();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}}}
四、一个锁内部可以有多个Condition,即有多路等待和通知,可以参看jdk1.5提供的Lock与Condition实现的可阻塞队列的应用案例,从中除了要体味算法,还要体味面向对象的封装。在传统的线程机制中一个监视器对象上只能有一路等待和通知,要想实现多路等待和通知,必须嵌套使用多个同步监视器对象。(如果只用一个Condition,两个放的都在等,一旦一个放的进去了,那么它通知可能会导致另一个放接着往下走。)
API案例:

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();     }   } }
/** *三个线程:主线程-->sub2--->sub3--->主线程--->sub2--->sub3 以此类推  */public class ThreeConditionCommunication {public static void main(String[] args) {final Business business = new Business();new Thread(new Runnable() {public void run() {for (int i = 1; i <= 2; i++) {business.sub2(i);}}}).start();new Thread(new Runnable() {public void run() {for (int i = 1; i <= 2; i++) {business.sub3(i);}}}).start();for (int i = 1; i <= 50; i++) {business.main(i);}} static class Business {Lock lock = new ReentrantLock();Condition condition1 = lock.newCondition();Condition condition2 = lock.newCondition();Condition condition3 = lock.newCondition();private int shouldSub = 1;public void sub2(int i) {lock.lock();try {while (shouldSub!=2) {try {condition2.await();//condition的特有方法} catch (Exception e) {e.printStackTrace();}}for (int j = 1; j <= 2; j++) {System.out.println("sub2 thread sequence of " + j + ",loop of " + i);}shouldSub = 3;condition3.signal();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}public void sub3(int i) {lock.lock();try {while (shouldSub!=3) {try {condition3.await();//condition的特有方法} catch (Exception e) {e.printStackTrace();}}for (int j = 1; j <= 2; j++) {System.out.println("sub3 thread sequence of " + j + ",loop of " + i);}shouldSub = 1;condition1.signal();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}public void main(int i) {lock.lock();try {while (shouldSub!=1) {try {condition1.await();} catch (Exception e) {e.printStackTrace();}}for (int j = 1; j <= 2; j++) {System.out.println("main thread sequence of " + j + ",loop of " + i);}shouldSub = 2;condition2.signal();} catch (Exception e) {e.printStackTrace();}finally{lock.unlock();}}}}
面试题:设计一个缓存系统
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){rwl.readLock().lock();//开启读锁Object value = null;try {value = cache.get(key);if(value == null){rwl.readLock().unlock();/** * 如果读到的数据为空,则开启写锁,此时先关闭读锁 * 如果多个线程都上写锁,因为写锁之间是互斥的,所以只有第一个可以写成功 * 当第一个线程的读锁解锁以后,后面的线程才可以进入写锁,当此时的数据不为空时,就不用再去查询数据库获取数据 */rwl.writeLock().lock();try {if(value == null){value = "aaa";//实际是去查询数据库的数据}} catch (Exception e) {e.printStackTrace();}finally{//关闭写锁同时开启读锁rwl.writeLock().unlock();}rwl.readLock().lock();}} catch (Exception e) {e.printStackTrace();}finally{rwl.readLock().unlock();//关闭读锁}return value;}}

结果:

main thread sequence of 1,loop of 1main thread sequence of 2,loop of 1sub2 thread sequence of 1,loop of 1sub2 thread sequence of 2,loop of 1sub3 thread sequence of 1,loop of 1sub3 thread sequence of 2,loop of 1main thread sequence of 1,loop of 2main thread sequence of 2,loop of 2sub2 thread sequence of 1,loop of 2sub2 thread sequence of 2,loop of 2sub3 thread sequence of 1,loop of 2sub3 thread sequence of 2,loop of 2main thread sequence of 1,loop of 3main thread sequence of 2,loop of 3

0 0
原创粉丝点击