水滴石穿--多线程wait notfiy 方法CountDownLatch、CyclicBarrier、Semaphore

前言

在前面介绍了线程入门、线程安全、线程的一些琐碎的知识点,现在我们讲讲如何让线程变得听话,指挥线程执行。

wait notfiy

  • 首先声明一点wait( ),notify( ),notifyAll( )都不属于Thread类,而是属于Object基础类,也就是每个对象都有wait( ),notify( ),notifyAll( ) 的功能,因为每个对象都有锁,锁是每个对象的基础,当然操作锁的方法也是最基础了。
  • notify( )方法只会通知等待队列中的第一个相关线程(不会通知优先级比较高的线程)notifyAll( )通知所有等待该竞争资源的线程(也不会按照线程的优先级来执行)
  • 假设有三个线程执行了obj.wait( ),那么obj.notifyAll( )则能全部唤醒tread1,thread2,thread3,但是要继续执行obj.wait()的下一条语句,必须获得obj锁,因此,tread1,thread2,thread3只有一个有机会获得锁继续执行,例如tread1,其余的需要等待thread1释放obj锁之后才能继续执行。
  • 当调用obj.notify/notifyAll后,调用线程依旧持有obj锁,因此,thread1,thread2,thread3虽被唤醒,但是仍无法获得obj锁。直到调用线程退出synchronized块,释放obj锁后,thread1,thread2,thread3中的一个才有机会获得锁继续执行

写几个例子

/**
 * @classDesc:wait notify的使用
 * @author: hj
 * @date:2018年12月12日 下午3:18:40
 */

public class ListAdd0 {
	private volatile static List list = new ArrayList();

	public void add() {
		list.add("bjsxt");
	}

	public int size() {
		return list.size();
	}

	public static void main(String[] args) {

		final ListAdd2 list2 = new ListAdd2();

		// 1 实例化出来一Object lock
		// 当使用wait notify 的时一定要配合synchronized关键字去使用
		final Object lock = new Object();

		Thread t1 = new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					synchronized (lock) {
						for (int i = 0; i < 10; i++) {
							list2.add();
							System.out.println("当前线程名称" + Thread.currentThread().getName() + "添加了一个元素..");
							Thread.sleep(500);
							if (list2.size() == 5) {
								System.out.println("已经发出通知..");

								lock.notify();
							}
						}
					}
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		}, "t1");

		Thread t2 = new Thread(new Runnable() {
			@Override
			public void run() {
				synchronized (lock) {
					if (list2.size() != 5) {
						try {
							System.out.println("t2进入...");
							lock.wait();

						} catch (InterruptedException e) {
							e.printStackTrace();
						}
					}
					System.out.println("当前线程名称" + Thread.currentThread().getName() + "收到通知线程停止..");
					throw new RuntimeException();
				}
			}
		}, "t2");
		t2.start();
		t1.start();
	}
}

t2进入...
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
已经发出通知..
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
当前线程名称t1添加了一个元素..
当前线程名称t2收到通知线程停止..
Exception in thread "t2" 

从结果看出,在t1发出通知后,t2没有立即停止,t1还在添加数据。原因:wait()释放了锁,所以不能立即开始执行。

CountDownLatch

CountDownLatch就像是计时器一样,在完成倒计时后发令让所有的线程开始起跑。再举个例子吧!!

/**
 * @classDesc: 监听,当初始化完成之后,才能通知主线程运行
 * @author: hj
 * @date:2018年12月12日 下午3:48:24
 */

public class UseCountDownLatch {

	public static void main(String[] args) {
		CountDownLatch countDown = new CountDownLatch(2);
		Thread t1 = new Thread(new Runnable() {

			@Override
			public void run() {
				try {
					System.out.println("进入线程t1" + "等待其他线程处理完成...");
					countDown.await();
					System.out.println("t1线程继续执行...");
				} catch (Exception e) {
					e.printStackTrace();
				}

			}
		});
		Thread t2 = new Thread(new Runnable() {

			@Override
			public void run() {
				System.out.println("t2线程进行初始化操作...");
				try {
					Thread.sleep(3000);
					System.out.println("t2线程初始化完毕,通知t1线程继续...");
					countDown.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		});
		Thread t3 = new Thread(new Runnable() {

			@Override
			public void run() {
				System.out.println("t3线程进行初始化操作...");
				try {
					Thread.sleep(4000);
					System.out.println("t3线程初始化完毕,通知t1线程继续...");
					countDown.countDown();
				} catch (InterruptedException e) {
					e.printStackTrace();
				}

			}
		});
		t1.start();
		t2.start();
		t3.start();
	}
}

 t2线程进行初始化操作...
t3线程进行初始化操作...
进入线程t1等待其他线程处理完成...
t2线程初始化完毕,通知t1线程继续...
t3线程初始化完毕,通知t1线程继续...
t1线程继续执行...

 CyclicBarrier

 CyclicBarrier和CountDownLatch有些相似,每个线程都是一个远动员,只有所有的运动员都准备好后才能一起出发。

/**
 * @classDesc: 每个线程都是一个远动员,只有所有的运动员都准备好后才能一起出发
 * @author: hj
 * @date:2018年12月12日 下午3:50:08
 */

public class UseCyclicBarrier {

	static class Runner implements Runnable {
		private CyclicBarrier barrier;
		private String name;

		public Runner(CyclicBarrier barrier, String name) {
			this.barrier = barrier;
			this.name = name;
		}

		@Override
		public void run() {
			try {
				Thread.sleep(1000 * (new Random()).nextInt(5));
				System.out.println(name + " 准备OK.");
				barrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
			System.out.println(name + " Go!!");
		}
	}

	public static void main(String[] args) throws IOException, InterruptedException {
		CyclicBarrier barrier = new CyclicBarrier(3);
		ExecutorService pool = Executors.newFixedThreadPool(3);
		pool.execute(new Thread(new Runner(barrier, "zhangsan")));
		pool.execute(new Thread(new Runner(barrier, "hj")));
		pool.execute(new Thread(new Runner(barrier, "papa")));
		pool.shutdown();
	}

}

进入线程t1等待其他线程处理完成...
t3线程进行初始化操作...
t2线程进行初始化操作...
t2线程初始化完毕,通知t1线程继续...
t3线程初始化完毕,通知t1线程继续...
t1线程继续执行...

 Semaphore

Semaphore的主要方法主要有获取资源(acquire)方法和释放资源(release)方法。

/**
 * @classDesc: Semaphore用法
 * @author: hj
 * @date:2018年12月12日 下午3:54:32
 */

public class UseSemaphore {
	public static void main(String[] args) {
		// 线程池
		ExecutorService exec = Executors.newCachedThreadPool();
		// 只能5个线程同时访问
		Semaphore semp = new Semaphore(5);
		// 模拟20个客户端访问
		for (int index = 0; index < 20; index++) {
			final int NO = index;
			Runnable run = new Runnable() {
				public void run() {
					try {
						// 获取许可
						semp.acquire();
						System.out.println("Accessing: " + NO);
						// 模拟实际业务逻辑
						Thread.sleep((long) (Math.random() * 10000));
						// 访问完后,释放
						semp.release();
					} catch (InterruptedException e) {
					}
				}
			};
			exec.execute(run);
		}
		try {
			Thread.sleep(10);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		// System.out.println(semp.getQueueLength());
		// 退出线程池
		exec.shutdown();
	}

}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值