Java 并发(JUC 包-05)

百般思念 提交于 2019-12-13 03:56:45

>同步工具 Semaphore

这个类代表一个计数信号量。可以控制某个资源可被同时访问的个数,acquire() 获得一个许可,如果没有就等待,release() 释放一个许可。Semaphore维护了当前访问的个数,提供同步访问机制,控制同时访问的个数。

例子:控制线程两个两个的运行

public class TestSemaphore {

	public static void main(String[] args) {
		new TestSemaphore().go();
	}

	public void go() {
		// 这就像是一个“许可”“信号灯”
		Semaphore semaphore = new Semaphore(2);
		Dosth dosth = new Dosth();
		for (int i = 0; i < 10; i++) {
			new Thread(new Runnable() {
				@Override
				public void run() {
					try {
						semaphore.acquire();
						dosth.work();
						semaphore.release();
					} catch (InterruptedException e) {
						e.printStackTrace();
					}

				}
			}, "线程" + i).start();
		}
	}

	class Dosth {
		public void work() {
			System.out.println(Thread.currentThread().getName() + "线程work");
			try {
				Thread.sleep(2000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
}

 

>同步工具 CountDownLatch

可以把它看作是一个“计数器”,只不过同时只能有一个线程操作这个计数器,也就是同时只能有一个线程去减这个计数器里面的值。在完成一组其他线程中的操作之前,它允许一个或多个线程一直等待。

你可以在CountDownLatch对象设置一个初始值,作为计数值,任何调用这个对象上await() 方法都将阻塞,直到这个计数器的计数值被其他线程减到零为止。一般典型应用场景:一个任务想要进行,必须等待其他线程完成执行完成后再往下执行。

方法说明,很好理解:

void await():使当前线程在锁存器倒计数至零之前一直等待,除非线程被中断。
void countDown():递减锁存器的计数,如果计数到达零,则释放所有等待的线程。
long getCount():返回当前计数。
public class TestCountDownLatch {

	public void go() {
		final Random r = new Random();
		final CountDownLatch latch = new CountDownLatch(3);
		new Thread(new Runnable() {
			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("子线程1正在工作");
				try {
					Thread.sleep(r.nextInt(3000));
				} catch (InterruptedException e) {
					// TODO Auto-generated catch blo
					e.printStackTrace();
				}
				System.out.println("子线程1工作完毕");
				latch.countDown();
			}
		}, "子线程1").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("子线程2正在工作");
				try {
					Thread.sleep(r.nextInt(3000));
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("子线程2工作完毕");
				latch.countDown();
			}
		}, "子线程2").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("子线程3正在工作");
				try {
					Thread.sleep(r.nextInt(3000));
				} catch (InterruptedException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				System.out.println("子线程3工作完毕");
				latch.countDown();
			}
		}, "子线程3").start();
		new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				System.out.println("主线程等待子线程全部完毕……");
				try {
					latch.await();
				} catch (InterruptedException e1) {
					// TODO Auto-generated catch block
					e1.printStackTrace();
				}
				System.out.println("所有子线程工作完毕,主线程开始工作……");
			}
		}, "主线程").start();
	}

	public static void main(String[] args) {
		TestCountDownLatch test = new TestCountDownLatch();
		test.go();
	}
}

 

>同步工具CyclicBarrier(公共屏障点)

允许一组线程彼此互相等待,直到达到一个屏障点。

CyclicBarrier类似于CountDownLatch,也是用来计数的。不同的是CyclicBarrier数的是调用了CyclicBarrier.await()进入等待的线程数,当等待的线程数达到了CyclicBarrier初始时规定的数目时,所有进入等待状态的线程被唤醒并继续。CyclicBarrier就象它名字的意思一样,可看成是个障碍,所有的线程必须到齐后才能一起通过这个障碍。

方法说明:

await():在所有参与者线程都已经在此 barrier 上调用 await 方法之前,将一直等待。

await(long timeout,TimeUnit unit): 在所有参与者线程都已经在此屏障上调用 await 方法之前将一直等待,或者超出了指定的等待时间。

getNumberWaiting():返回当前在屏障处等待的参与者线程的数目。

getParties():返回要求启动此 barrier 的参与者线程的数目。

isBroken():查询此屏障是否处于损坏状态。

reset():将屏障重置为其初始状态。
public class CyclicBarrierTest {
	public static void main(String[] args) {
		final CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
		ExecutorService service = Executors.newFixedThreadPool(10);
		for (int i = 0; i < 3; i++) {
			service.execute(new Runnable() {
				@Override
				public void run() {
					for (int i = 0; i < 5; i++) {
						try {
							Thread.sleep(new Random().nextInt(5000));
							System.out.print(Thread.currentThread().getName() + "已到达集合点" + (i + 1) + ",现在共有"
									+ (cyclicBarrier.getNumberWaiting() + 1) + "个线程到达,");
							if (cyclicBarrier.getNumberWaiting() + 1 == 3) {
								System.out.println("所有线程都到齐,进行下一操作……");
							} else {
								System.out.println("正在等待其他线程……");
							}
							cyclicBarrier.await();
						} catch (Exception e) {
							e.printStackTrace();
						}
					}
				}
			});
		}
		service.shutdown();
	}
}

Hadoop的MapReduce的思想,举个例子,并行计算:

一个三维数组,开三个线程,各自求一维之后,再将三个结果操作。

public class TestCyclicBarrier {
	int[][] rect = { { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } };
	int[] rowResult = new int[rect.length];
	Calc calc = new Calc();

	public static void main(String[] args) {
		TestCyclicBarrier test = new TestCyclicBarrier();
		test.go();
	}

	private void go() {
		CyclicBarrier barrier = new CyclicBarrier(3, new Runnable() {
			public void run() {
				System.out.println("所有计算线程执行完毕,这里开始汇总");
				int total = calc.add(rowResult);
				System.out.println(total);
			}
		});
		for (int i = 0; i < rect.length; i++) {
			final int row = i;
			new RowThread(barrier, row).start();
		}
	}

	class RowThread extends Thread {
		Random r = new Random();
		CyclicBarrier barrier;// 计数器
		int row;

		public RowThread(CyclicBarrier barrier, int row) {
			this.barrier = barrier;
			this.row = row;
		}

		@Override
		public void run() {
			rowResult[row] = calc.add(rect[row]);
			try {
				Thread.sleep(r.nextInt(4000));
				System.out.println(Thread.currentThread().getName() + "计算完毕,开始等待");
				barrier.await();
			} catch (InterruptedException e) {
				e.printStackTrace();
			} catch (BrokenBarrierException e) {
				e.printStackTrace();
			}
		}
	}

	class Calc {
		public int add(int[] row) {
			int total = 0;
			for (int i : row) {
				total += i;
			}
			return total;
		}
	}
}

代码的主要功能是:给出一个二维数组,然后根据二维数组的长度开启若干个线程,每一个线程计算二维数组中每一个“一维数组”的元素之和。然后当每一个一维数组的元素和计算完毕后,再由一个线程汇总前面的每一个结果,最终得到的就是整个二维数组元素之和。正如程序给出的二维数组是int[][] rect = {{1,2,3},{4,5,6},{7,8,9}};这个数组长度是3,每一个元素又是一个一维数组,然后程序开启三个线程,第一个线程计算{1,2,3}的和,就是1+2+3=6,第二个线程计算4,5,6的和,第三个线程计算7,8,9的和,最后再由一个线程计算前面三个线程的和进行相加,就得到了这个二维数组所有元素的和。这有点类似Map/Reduce的思想。什么是Map/Reduce?感兴趣的读者可以google一下。

这里需要注意一点,和上一个例子有所不同,这里创建的CyclicBarrier对象时,不仅给出了屏障集结的线程数量,还给出了当满足线程数量到达屏障点后继续运行的线程(也就是那个汇总线程)。创建的时候先创建CyclicBarrier对象,然后用这个对象到每个具体线程中进行await即可。

 

>同步工具Exchanger(线程间交换数据)

多个线程按先后运行先后顺序,两两交换

public class TestEx {

	public static void main(String[] args) {
		new TestEx().go();
	}

	private void go() {
		final Exchanger<Integer> exchanger = new Exchanger<>();

		ExecutorService service = Executors.newCachedThreadPool();

		service.execute(new Runnable() {
			@Override
			public void run() {
				int data = 123;
				System.out.println("线程" + Thread.currentThread().getName() + "要交换数据" + data + ",等待...");
				try {
					Thread.sleep(2000);
					Integer exchange = exchanger.exchange(data);
					System.out.println("线程" + Thread.currentThread().getName() + "换得数据" + exchange );
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});

		service.execute(new Runnable() {
			@Override
			public void run() {
				int data = 456;
				System.out.println("线程" + Thread.currentThread().getName() + "要交换数据" + data + ",等待...");
				try {
					Thread.sleep(2000);
					Integer exchange = exchanger.exchange(data);
					System.out.println("线程" + Thread.currentThread().getName() + "换得数据" + exchange );
				} catch (InterruptedException e) {
					e.printStackTrace();
				}
			}
		});

	}
}

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!