关闭

线程锁ReentrantLock和Condition的使用

标签: 线程锁
161人阅读 评论(0) 收藏 举报

我们通过一个实际的例子来解释Condition的用法:

我们要打印1到9这9个数字,由A线程先打印1,2,3,然后由B线程打印4,5,6,然后再由A线程打印7,8,9. 这道题有很多种解法,现在我们使用Condition来做这道题(使用Object的wait,notify方法的解法在这里)。

package cn.outofmemory.locks;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class App {
	static class NumberWrapper {
		public int value = 1;
	}

	public static void main(String[] args) {
		//初始化可重入锁
		final Lock lock = new ReentrantLock();
		
		//第一个条件当屏幕上输出到3
		final Condition reachThreeCondition = lock.newCondition();
		//第二个条件当屏幕上输出到6
		final Condition reachSixCondition = lock.newCondition();
		
		//NumberWrapper只是为了封装一个数字,一边可以将数字对象共享,并可以设置为final
		//注意这里不要用Integer, Integer 是不可变对象
		final NumberWrapper num = new NumberWrapper();
		//初始化A线程
		Thread threadA = new Thread(new Runnable() {
			@Override
			public void run() {
				//需要先获得锁
				lock.lock();
				try {
					System.out.println("threadA start write");
					//A线程先输出前3个数
					while (num.value <= 3) {
						System.out.println(num.value);
						num.value++;
					}
					//输出到3时要signal,告诉B线程可以开始了
					reachThreeCondition.signal();
				} finally {
					lock.unlock();
				}
				lock.lock();
				try {
					//等待输出6的条件
					reachSixCondition.await();
					System.out.println("threadA start write");
					//输出剩余数字
					while (num.value <= 9) {
						System.out.println(num.value);
						num.value++;
					}

				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
			}

		});


		Thread threadB = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					lock.lock();
					
					while (num.value <= 3) {
						//等待3输出完毕的信号
						reachThreeCondition.await();
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				} finally {
					lock.unlock();
				}
				try {
					lock.lock();
					//已经收到信号,开始输出4,5,6
					System.out.println("threadB start write");
					while (num.value <= 6) {
						System.out.println(num.value);
						num.value++;
					}
					//4,5,6输出完毕,告诉A线程6输出完了
					reachSixCondition.signal();
				} finally {
					lock.unlock();
				}
			}

		});


		//启动两个线程
		threadB.start();
		threadA.start();
	}
}

 上述代码中有完整的注释,请参考注释,理解Condition的用法。

基本思路就是首先要A线程先写1,2,3,这时候B线程应该等待reachThredCondition信号,而当A线程写完3之后就通过signal告诉B线程“我写到3了,该你了”,这时候A线程要等嗲reachSixCondition信号,同时B线程得到通知,开始写4,5,6,写完4,5,6之后B线程通知A线程reachSixCondition条件成立了,这时候A线程就开始写剩下的7,8,9了。

为了更好的理解Condition的用法,我们再看下java官方提供的例子:

package locks;




import java.util.Random;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;




public class AppOfficial {

	/**
	 * BoundedBuffer 是一个定长100的集合,当集合中没有元素时,take方法需要等待,直到有元素时才返回元素
	 * 当其中的元素数达到最大值时,要等待直到元素被take之后才执行put的操作
	 * @author yukaizhao
	 *
	 */
	static 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 {
			System .out.println("put wait lock");
			lock.lock();
			System.out.println("put get lock");
			try {
				while (count == items.length) {
					System.out.println("buffer full, please wait");
					notFull.await();
				}
					
				items[putptr] = x;
				if (++putptr == items.length)
					putptr = 0;
				++count;
				notEmpty.signal();
			} finally {
				lock.unlock();
			}
		}




		public Object take() throws InterruptedException {
			System.out.println("take wait lock");
			lock.lock();
			System.out.println("take get lock");
			try {
				while (count == 0) {
					System.out.println("no elements, please wait");
					notEmpty.await();
				}
				Object x = items[takeptr];
				if (++takeptr == items.length)
					takeptr = 0;
				--count;
				notFull.signal();
				return x;
			} finally {
				lock.unlock();
			}
		}
	}
	
	public static void main(String[] args) {
		final BoundedBuffer boundedBuffer = new BoundedBuffer();
		
		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				System.out.println("t1 run");
				for (int i=0;i<1000;i++) {
					try {
						System.out.println("putting..");
						boundedBuffer.put(Integer.valueOf(i));
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			
		}) ;
		
		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				for (int i=0;i<1000;i++) {
					try {
						Object val = boundedBuffer.take();
						System.out.println(val);
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			}
			
		}) ;
		
		t1.start();
		t2.start();
	}
}

这个示例中BoundedBuffer是一个固定长度的集合,这个在其put操作时,如果发现长度已经达到最大长度,那么会等待notFull信号,如果得到notFull信号会像集合中添加元素,并发出notEmpty的信号,而在其take方法中如果发现集合长度为空,那么会等待notEmpty的信号,同时如果拿到一个元素,那么会发出notFull的信号。

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:47397次
    • 积分:1479
    • 等级:
    • 排名:千里之外
    • 原创:104篇
    • 转载:11篇
    • 译文:0篇
    • 评论:9条
    最新评论