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

107 篇文章 0 订阅
27 篇文章 0 订阅

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();
			}
		}
	}
}



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值