Java多线程编程---线程同步与通信技术(Lock & Condition)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zengxiantao1994/article/details/79882956

Java5的条件阻塞条件

        条件的功能类似在传统线程技术中的的Object.wait()和Object.natify()的功能,传统线程技术实现的互斥只能一个线程单独执行,不能说这个线程执行完了通知另一个线程来执行,条件就是解决这个问题的,实现线程间的通信。比如CPU让线程1做事,线程1说我先歇着并通知线程2,线程2就开始做事。

        公共接口条件

        条件将对象监视器方法(wait,notify和notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用,为每个对象提供多个等待set(wait-set)。其中,Lock替代了synchronized方法和语句的使用,条件替代了Object监视器方法的使用。

        Condition实例,请使用其newCondition()方法。

 

        JDK文档中的一个示例,假定有一个绑定的缓冲区,它支持put和take方法。如果试图在空的缓冲区上执行取操作,则在某个项目变得可用之前,线程将一直阻塞;如果试图在满的缓冲区上执行放置操作,则在有空间变得可用之前,线程将一直阻塞。我们喜欢在单独的等待set中保存放线程和take线程,这样就可以在缓冲区中的项目或空间变得可用时利用最佳规划,一次只通知一个线程。可以使用两个条件实例来做到这一点。

// 阻塞队列 满了不能放,空了不能取
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();
		}
	}
}


条件的使用方法

        锁定锁= newReerantrantLock();

        条件条件= lock.newCondition();

        this.wait()---> condition.await()

        this.notify()---> condition.signal()

        注意:判断条件时用,同时防止虚假唤醒,等待在那里,唤醒后再进行判断,确认符合要求后再执行任务。

 

        实例:实现效果:子线程循环10次,接着主线程循环100次,又回到子线程循环10次,接着再回到主线程又循环100次,如此循环50次。

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

/**
 * 
 * @Description: 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次
 * 				   使用 Lock 与 Condition 技术实现
 *
 * @author: zxt
 *
 * @time: 2018年4月6日 下午7:12:39
 *
 */
public class ThreadCommunication {

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

}

/**
 * 
 * @Description: 同步的功能封装在资源内部(这样调用它的线程则不再需要考虑线程同步问题)
 *
 * @author: zxt
 *
 * @time: 2018年4月6日 下午7:36:02
 *
 */
class Business {
	// 构造一把锁
	Lock lock = new ReentrantLock();
	// 由锁创建一个条件阻塞Condition对象
	Condition condition = lock.newCondition();
	
	// 表示是否应该sub函数执行(为true表示sub执行)
	private boolean bSubShould = true;
	
	public void sub(int i) {
		lock.lock();
		try {
			while(!bSubShould) {
				try {
					// 非sub函数执行时,则等待
					condition.await();
					
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			for (int j = 1; j <= 10; j++) {
				System.out.println("this is sub thread sequence of " + j + ", loop of " + i);
			}
			
			// 下次执行是main函数
			bSubShould = false;
			// 同时需要唤醒等待的线程(由condition给一个信号)
			condition.signal();
			
		} finally {
			// 释放锁对象
			lock.unlock();
		}
	}

	public void main(int i) {
		lock.lock();
		try {
			while(bSubShould) {
				try {
					// 非住线程执行时,则等待
					condition.await();
					
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
			for (int j = 1; j <= 100; j++) {
				System.out.println("this is main thread sequence of " + j + ", loop of " + i);
			}
			
			// 下次执行的是sub函数
			bSubShould = true;
			// 同时需要唤醒等待的线程(由condition给一个信号)
			condition.signal();
			
		} finally {
			// 释放锁对象
			lock.unlock();
		}
	}
}

        在Condition中,用await()替换wait(),用signal()替换notify(),用signalAll()替换notifyAll(),传统线程的通信方式,Condition都可以实现。

        Condition实例实质上被绑定到一个锁上。要为特定Lock实例获得Condition 实例,请使用其newCondition() 方法。

 

        通过上述实例发现,Condition和传统的线程通信没什么区别,而Condition的强大之处在于它可以为多个线程间建立不同的Condition,这样可以控制多个线程之间的运行顺序。

        例如修改上述实例:实现子线程1循环10次,子线程2循环5次,接着主线程循环10次,然后又回到子线程1循环10次,子线程2循环5次,再回到主线程又循环10次,如此循环50次。

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

/**
 * 
 * @Description: 子线程循环10次,接着主线程循环100,接着又回到子线程循环10次,接着再回到主线程又循环100,如此循环50次
 * 				   使用 Lock 与 Condition 技术实现
 *
 * @author: zxt
 *
 * @time: 2018年4月6日 下午7:12:39
 *
 */
public class ThreeThreadCommunication {

	public static void main(String[] args) {
		final Business business = new Business();
		
		// 子线程1
		new Thread(new Runnable() {

			public void run() {
				for (int i = 1; i <= 50; i++) {
					business.sub(i);
				}

			}
		}).start();
		
		// 子线程2
		new Thread(new Runnable() {

			public void run() {
				for (int i = 1; i <= 50; i++) {
					business.sub2(i);
				}

			}
		}).start();

		// 主线程
		for (int i = 1; i <= 50; i++) {
			business.main(i);
		}
	}
	
	/**
	 * 
	 * @Description: 同步的功能封装在资源内部(这样调用它的线程则不再需要考虑线程同步问题)
	 *
	 * @author: zxt
	 *
	 * @time: 2018年4月6日 下午7:36:02
	 *
	 */
	static class Business {
		// 构造一把锁
		Lock lock = new ReentrantLock();
		// 由锁创建条件阻塞Condition对象  (子线程1 通知 子线程2, 子线程2 通知 主线程, 主线程 通知  子线程1)
		Condition condition1 = lock.newCondition();
		Condition condition2 = lock.newCondition();
		Condition condition3 = lock.newCondition();
		
		// 表示是否应该哪个函数执行(1:子线程1,2:子线程2, 3:主线程)
		private int subShould = 1;
		
		public void sub(int i) {
			lock.lock();
			try {
				while(subShould != 1) {
					try {
						// 非sub函数执行时,则等待
						condition1.await();
						
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println(Thread.currentThread().getName() + " sequence of " + j + ", loop of " + i);
				}
				
				// 下次执行是sub2函数
				subShould = 2;
				// 同时需要唤醒等待的线程2(由condition给一个信号)
				condition2.signal();
				
			} finally {
				// 释放锁对象
				lock.unlock();
			}
		}
		
		public void sub2(int i) {
			lock.lock();
			try {
				while(subShould != 2) {
					try {
						// 非sub2函数执行时,则等待
						condition2.await();
						
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 10; j++) {
					System.out.println(Thread.currentThread().getName() + " sequence of " + j + ", loop of " + i);
				}
				
				// 下次执行是m函数
				subShould = 3;
				// 同时需要唤醒等待的主线程(由condition给一个信号)
				condition3.signal();
				
			} finally {
				// 释放锁对象
				lock.unlock();
			}
		}

		public void main(int i) {
			lock.lock();
			try {
				while(subShould != 3) {
					try {
						// 非主线程执行时,则等待
						condition3.await();
						
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
				for (int j = 1; j <= 100; j++) {
					System.out.println(Thread.currentThread().getName() + " sequence of " + j + ", loop of " + i);
				}
				
				// 下次执行的是sub函数
				subShould = 1;
				// 同时需要唤醒等待的线程1(由condition给一个信号)
				condition1.signal();
				
			} finally {
				// 释放锁对象
				lock.unlock();
			}
		}
	}
}


总结

        对于传统线程通信synchronized方式与Lock结合Condition的方式,Lock 替代了synchronized 方法和语句的使用,Condition 替代了Object 监视器方法的使用。而对比来说Lock (本身为一个对象)更加面向对象,Condition 可以为多个线程间建立不同的条件,这样可以控制多个线程之间的运行顺序。


猜你喜欢

转载自blog.csdn.net/zengxiantao1994/article/details/79882956