1、CountDownLatch
1.应用场景
在实际多线程并发开发过程当中,咱们会遇见不少等待子线程完毕后在继续执行的状况,(如多个子线程下载文件,全部子线程执行完毕后再重命名为文件名)。
2.使用方式
CountDownLatch的构造函数接受一个int类型的参数做为计数器,调用countDwon()方法,计数器减1,await()方法阻塞当前线程,直到计数器变为0;、
补充:
计数器为0的时候,调用awaite()方法不会阻塞主线程;
初始化后,不能修改计数器的值;
可使用await(long time,TimeUnit unit)等待特定时间后,就不阻塞主线程; java
3.实例代码数据库
public class Main { //等待2个子线程执行完毕,计数器为2 static CountDownLatch countDownLatch = new CountDownLatch(2); public static void main(String[] args) { System.out.println("start subThread doing..."); //建立并开启2个子线程 SubThread subThread1 = new SubThread(); SubThread subThread2 = new SubThread(); subThread1.start(); subThread2.start(); try { //阻塞主线程,等待子线程结束 countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("subThread are finish..."); } static class SubThread extends Thread { @Override public void run() { //模拟执行任务 try { sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } //子线程执行完毕,减小计数器 System.out.println(getName() + " done..."); countDownLatch.countDown(); } } }
运行结果:当Thread-一、Thread-0两个子线程执行完毕后,在运行main线程后续的逻辑 多线程
start subThread doing... Thread-1 done... Thread-0 done... subThread are finish...
2、CyclicBarrier
1.应用场景
若是当你碰见须要让一组线程达到同一个屏障(同步点)时被阻塞,直到最后一个线程达到屏障时,屏障才会打开的状况。
2.使用方式
CycliBarrier默认的构造方法CyclicBarrier(int parties),参数标识屏障拦截的线程个数,每一个线程调用await()方法告诉SyclicBarrier咱们已经达到屏障了,而后当前线程被阻塞。当全部子线程都达到屏障后,则继续执行子线程的后续逻辑。
补充:
CyclicBarrier还提供了一个更高级的函数CyclicBarrier(int parties,Runnable barrierAction),用于在线程达到屏障时,优先执行barrierAction。
3.实例代码 并发
public class Main { //拦截2个子线程屏障 static CyclicBarrier cyclicBarrier = new CyclicBarrier(2); public static void main(String[] args) { System.out.println("start subThread doing..."); SubThread subThread1 = new SubThread(); SubThread subThread2 = new SubThread(); subThread1.start(); subThread2.start(); } static class SubThread extends Thread { @Override public void run() { try { System.out.println(getName() + " doing first things."); //模拟子线程执行第一个任务 sleep(3000); System.out.println(getName() + " done first things."); } catch (InterruptedException e) { e.printStackTrace(); } try { //完成第一个任务,告知达到屏障 cyclicBarrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } //全部子线程都完成第一个任务后,继续运行每一个子线程的下一个任务 System.out.println(getName() + " doing other things."); } } }
运行结果:当子线程都执行完第一个任务到达屏障后,执行下一个任务 ide
start subThread doing... Thread-0 doing first things. Thread-1 doing first things. Thread-1 done first things. Thread-0 done first things. Thread-0 doing other things. Thread-1 doing other things.
3、Semaphore
1.应用场景
多线程访问公共资源的状况在开发过程当中常常碰见,如数据库链接,可能开启几十个线程进行并发读取,可是考虑到数据库链接性能和消耗,咱们必须控制10个线程哪一个是链接数据库。Semaphore就是用来控制同时访问特定资源的线程数量。
2.使用方式
Semaphore的构造方法Semaphore(int permits),permits标识许可证数量。执行任务前,acquire()方法获取一个许可证;任务执行完成后调用relese()方法归还许可证。没有得到许可证的子线程就阻塞等待。
补充:
tryAcquire():尝试获取许可证;
intavaliablePermits():返回信号量中当前许可证的个数;
intgetQueueLength():返回正在等待获取许可证的线程个数;
booleanhasQueueThreads():是否有线程正在等待许可证;
reducePermits(int reduction):减小reduction个许可证;
getQueuedThreads():返回全部等待获取许可证的线程集合;
3.实例代码 函数
public class Main { //建立2个许可证 static Semaphore semaphore = new Semaphore(2); public static void main(String[] args) { System.out.println("start subThread doing..."); //同时开启4个子线程运行 for (int i = 0; i < 4; i++) { SubThread subThread = new SubThread(); subThread.start(); } } static class SubThread extends Thread { @Override public void run() { try { //执行任务前获取许可证 semaphore.acquire(); System.out.println(getName() + "doing things."); sleep(3000); //执行完任务释放许可证 semaphore.release(); System.out.println(getName() + "finish things."); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:同时只有2个线程运行,当某个线程运行完毕释放许可后,下一个线程才获取许可运行; 性能
start subThread doing... Thread-0doing things. Thread-1doing things. Thread-1finish things. Thread-2doing things. Thread-0finish things. Thread-3doing things. Thread-2finish things. Thread-3finish things.
4、Exchanger
1.应用场景
在某些实际业务如流水录入中,为了不错误。采用两我的同时录入,并对比录入的结果是否一致。Exchanger用于进行线程之间的数据交换,它提供了一个同步点,两个线程能够交换彼此的数据。
2.使用方式
两个线程经过exchange()方法交换数据,若是一个线程执行exchange()方法,它会一直等待第二个线程也执行exchange()方法。当两个线程都达到同步点时,就能够交换数据,将本线程产生的数据传递给对方。
3.实例代码 ui
public class Main { //用户线程间交换数据(String)对象exchanger static Exchanger<String> exchanger = new Exchanger<>(); public static void main(String[] args) { //建立2个子线程分别执行 SubThread1 subThread1 = new SubThread1(); SubThread2 subThread2 = new SubThread2(); subThread1.start(); subThread2.start(); } static class SubThread1 extends Thread { @Override public void run() { try { System.out.println(getName() + "start doing..."); //模拟执行完成后,获取结果result1,并将result1交换给对方线程 sleep(3000); String result1 = "3000"; String result2 = exchanger.exchange(result1); //待两个线程都执行完毕后,交换数据进行比较 System.out.println(getName() + " thread1 result:" + result1 + " is equals thread2 result:" + result2 + "," + result1.equals(result2)); } catch (InterruptedException e) { e.printStackTrace(); } } } static class SubThread2 extends Thread { @Override public void run() { try { System.out.println(getName() + "start doing..."); //模拟执行完成后,获取结果result2,并将result2交换给对方线程 sleep(2000); String result2 = "2000"; String result1 = exchanger.exchange(result2); //待两个线程都执行完毕后,交换数据进行比较 System.out.println(getName() + " thread1 result:" + result1 + " is equals thread2 result:" + result2 + "," + result1.equals(result2)); } catch (InterruptedException e) { e.printStackTrace(); } } } }
运行结果:线程1优先执行完毕,等待线程0执行完毕后,交换数据分别进行结果比较 线程
Thread-1start doing... Thread-0start doing... Thread-1finish doing... Thread-0finish doing... Thread-0 thread1 result:3000 is equals thread2 result:2000,false Thread-1 thread1 result:3000 is equals thread2 result:2000,false