Java 并发常用工具类

很多情况下 程序的主线程在做一项工作之前需要一系列的准备工作,只有这些准备工作都完成,主线程才能继续它的工作,
CountDownLatch允许一个或多个线程等待另外N个线程完成某个操作之后才能执行这个类似Thread里的join()的功能。当某一个或多个线程start()以后,
用join()去等待。必须对应的线程执行完毕,join()后续的代码才能继续执行下去,CountDownLatch提供了join()的类似功能,并且能提供的更多! 
CountDownLatch提供了int参数(计数器)的构造方法,输入N就代表等待N个点完成。当我们调用countDown()方法时N就减1,直到N为0,await()后续代码才能继续执行
import java.util.concurrent.CountDownLatch;

public class Test {
     public static void main(String[] args) {   
         final CountDownLatch latch = new CountDownLatch(2);
          
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                    Thread.sleep(3000);
                    System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                    latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
          
         new Thread(){
             public void run() {
                 try {
                     System.out.println("子线程"+Thread.currentThread().getName()+"正在执行");
                     Thread.sleep(3000);
                     System.out.println("子线程"+Thread.currentThread().getName()+"执行完毕");
                     latch.countDown();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
             };
         }.start();
          
         try {
             System.out.println("等待2个子线程执行完毕...");
            latch.await();
            System.out.println("2个子线程已经执行完毕");
            System.out.println("继续执行主线程");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
     }
}

-----------------------------------------------------------------------------------------------------------------------------------------
CyclicBarrier

允许两个或者多个线程在某个集合点同步。当一个线程到达集合点时,它将调用await()方法等待其它的线程。线程调用await()方法后,
CyclicBarrier将阻塞这个线程并将它置入休眠状态等待其它线程的到来。等最后一个线程调用await()方法时,CyclicBarrier将唤醒所有等待的线程然后这些线程将继续执行。
CyclicBarrier可以传入另一个Runnable对象作为初始化参数。当所有的线程都到达集合点后,CyclicBarrier类将Runnable对象作为线程执行.

示例1.
class TaskDemo implements Runnable{
	 private static Random rnd = new Random();
    private String id;
    TaskDemo(String id){
        this.id = id;
    }
    @Override
    public void run(){
        try {
            Thread.sleep(rnd.nextInt(1000));
            System.out.println("Thread " + id + " will wait");
            TestCyclicBarrier.cb.await();
            System.out.println("-------Thread " + id + " is over");
        } catch (InterruptedException e) {
        } catch (BrokenBarrierException e) {
        }
    }
}
public class TestCyclicBarrier {
    public static CyclicBarrier cb = new CyclicBarrier(4);
    public static void main(String[] args){
        ExecutorService es = Executors.newCachedThreadPool();
        es.submit(new TaskDemo("a"));
        es.submit(new TaskDemo("b"));
        es.submit(new TaskDemo("c"));
        es.submit(new TaskDemo("d"));
        es.shutdown();
    }
}

//输出结果如下:
Thread b will wait
Thread c will wait
Thread d will wait
Thread a will wait
-------Thread b is over
-------Thread c is over
-------Thread a is over
-------Thread d is over

CyclicBarrier还提供一个更高级的构造函数CyclicBarrier(int parties, Runnable barrierAction),用于在线程到达屏障时,先执行barrierAction再释放其他线程锁
,方便处理更复杂的业务场景。
代码如下:

public class CyclicBarrierTest2 {
	//在线程到达屏障时,先执行A任务,待A任务返回后再释放其他线程的锁
	static CyclicBarrier c = new CyclicBarrier(2, new A());
	public static void main(String[] args) {
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					c.await();
				} catch (Exception e) {
				}
				System.out.println(1);
			}
		}).start();
		new Thread(new Runnable() {
			@Override
			public void run() {
				try {
					c.await();
				} catch (Exception e) {
				}
				System.out.println(2);
			}
		}).start();
	}
	static class A implements Runnable {
		@Override
		public void run() {
			try {
				Thread.sleep(10000);
			} catch (InterruptedException e) {
				e.printStackTrace();
			}
			System.out.println(3);
		}
	}
}
//输出结果如下:
3
1
2
-----------------------------------------------------------------------------------------------------------------------------------------
Semaphore

Semaphore可以用于做流量控制,特别公用资源有限的应用场景,比如数据库连接。
假如有一个需求,要读取几万个文件的数据,因为都是IO密集型任务,我们可以启动几十个线程并发的读取,
但是如果读到内存后,还需要存储到数据库中,而数据库的连接数只有10个,
这时我们必须控制只有十个线程同时获取数据库连接保存数据,否则会报错无法获取数据库连接。
这个时候,我们就可以使用Semaphore来做流控.

Semaphore的用法也很简单,首先线程使用Semaphore的acquire()获取一个许可证,使用完之后调用release()归还许可证。
class SemaphoreTest {
	private static int threadCount = 100;
	static Semaphore semaphore = new Semaphore(10);
	static ExecutorService service = Executors.newFixedThreadPool(threadCount);// 创建大小为100的线程池

	public static void main(String[] argus) {
		for (int i = 0; i < threadCount; i++) {
			final int c = i;
			service.execute(new Runnable() {
				@Override
				public void run() {
					try {
						semaphore.acquire();// 获取许可(绿灯)
						System.out.println("执行一些频繁的资源逻辑操作" + c);
						Thread.sleep(1000);
						semaphore.release();// 释放一个许可
					} catch (InterruptedException e) {
						e.printStackTrace();
					}
				}
			});
		}
		service.shutdown();
	}
}
-----------------------------------------------------------------------------------------------------------------------------------------


Exchanger
Exchanger也很好理解,直译交换者,是用于2个线程之间交互数据的工具类,它提供了一个同步点,
其中一个线程先执行了exchange方法,会一直等待第二个线程执行到exchange方法,直到都达到了执行点,两个线程才交互数据

class TestExchanger {
    static Exchanger<Double> exchanger = new Exchanger<Double>();//定义交换者
    static ExecutorService service = Executors.newFixedThreadPool(2);//2个大小的线程池
    public static void main(String[] argus) {
        service.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    //例如录入银行流水计算一个总额 操作人A
                    Double a = new Double(100);
                    double exchangeB = exchanger.exchange(a);
                    System.out.println("A自己的数据为:"+a+" 交换得到了" + exchangeB);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        service.execute(new Runnable() {
            @Override
            public void run() {
                try {
                    //例如录入银行流水计算一个总额 操作人B
                    Double b = new Double(101);
                    double exchangeB = exchanger.exchange(b);
                    System.out.println("B自己的数据为:"+b+" 交换得到了:" + exchangeB);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        });
        service.shutdown();
    }
}
//输出
A自己的数据为:100.0 交换得到了101.0
B自己的数据为:101.0 交换得到了:100.0

猜你喜欢

转载自yangeoo.iteye.com/blog/2268254