Java:线程并发工具类

来源:互联网 发布:mac 战网 改台服 编辑:程序博客网 时间:2024/06/05 02:33
一、CountDownLatch 
1.应用场景
 
在实际多线程并发开发过程中,我们会碰见很多等待子线程完毕后在继续执行的情况,(如多个子线程下载文件,所有子线程执行完毕后再重命名为文件名)。 
2.使用方式 
CountDownLatch的构造函数接受一个int类型的参数作为计数器,调用countDwon()方法,计数器减1,await()方法阻塞当前线程,直到计数器变为0;、 
补充: 
    计数器为0的时候,调用awaite()方法不会阻塞主线程; 
    初始化后,不能修改计数器的值; 
    可以使用await(long time,TimeUnit unit)等待特定时间后,就不阻塞主线程; 

3.实例代码

[java] view plain copy
  1. public class Main {     
  2.     //等待2个子线程执行完毕,计数器为2     
  3.     static CountDownLatch countDownLatch = new CountDownLatch(2);     
  4.      
  5.     public static void main(String[] args) {     
  6.         System.out.println("start subThread doing...");     
  7.         //创建并开启2个子线程     
  8.         SubThread subThread1 = new SubThread();     
  9.         SubThread subThread2 = new SubThread();     
  10.         subThread1.start();     
  11.         subThread2.start();     
  12.      
  13.         try {     
  14.             //阻塞主线程,等待子线程结束     
  15.             countDownLatch.await();     
  16.         } catch (InterruptedException e) {     
  17.             e.printStackTrace();     
  18.         }     
  19.      
  20.         System.out.println("subThread are finish...");     
  21.     }     
  22.      
  23.     static class SubThread extends Thread {     
  24.         @Override     
  25.         public void run() {     
  26.             //模拟执行任务     
  27.             try {     
  28.                 sleep(3000);     
  29.             } catch (InterruptedException e) {     
  30.                 e.printStackTrace();     
  31.             }     
  32.             //子线程执行完毕,减少计数器     
  33.             System.out.println(getName() + " done...");     
  34.             countDownLatch.countDown();     
  35.         }     
  36.     }     
  37. }     
运行结果:当Thread-1、Thread-0两个子线程执行完毕后,在运行main线程后续的逻辑 

[java] view plain copy
  1. start subThread doing...   
  2. Thread-1 done...   
  3. Thread-0 done...   
  4. subThread are finish...   
二、CyclicBarrier 
1.应用场景 

如果当你遇见需要让一组线程达到同一个屏障(同步点)时被阻塞,直到最后一个线程达到屏障时,屏障才会打开的情况。 
2.使用方式 
CycliBarrier默认的构造方法CyclicBarrier(int parties),参数标识屏障拦截的线程个数,每个线程调用await()方法告诉SyclicBarrier我们已经达到屏障了,然后当前线程被阻塞。当所有子线程都达到屏障后,则继续执行子线程的后续逻辑。 
补充: 
CyclicBarrier还提供了一个更高级的函数CyclicBarrier(int parties,Runnable barrierAction),用于在线程达到屏障时,优先执行barrierAction。 
3.实例代码 
[java] view plain copy
  1. public class Main {   
  2.     //拦截2个子线程屏障   
  3.     static CyclicBarrier cyclicBarrier = new CyclicBarrier(2);   
  4.    
  5.     public static void main(String[] args) {   
  6.         System.out.println("start subThread doing...");   
  7.         SubThread subThread1 = new SubThread();   
  8.         SubThread subThread2 = new SubThread();   
  9.         subThread1.start();   
  10.         subThread2.start();   
  11.     }   
  12.    
  13.     static class SubThread extends Thread {   
  14.         @Override   
  15.         public void run() {   
  16.             try {   
  17.                 System.out.println(getName() + " doing first things.");   
  18.                 //模拟子线程执行第一个任务   
  19.                 sleep(3000);   
  20.                 System.out.println(getName() + " done first things.");   
  21.             } catch (InterruptedException e) {   
  22.                 e.printStackTrace();   
  23.             }   
  24.                
  25.             try {   
  26.                 //完成第一个任务,告知达到屏障   
  27.                 cyclicBarrier.await();   
  28.             } catch (InterruptedException e) {   
  29.                 e.printStackTrace();   
  30.             } catch (BrokenBarrierException e) {   
  31.                 e.printStackTrace();   
  32.             }   
  33.    
  34.    
  35.             //所有子线程都完成第一个任务后,继续运行每个子线程的下一个任务   
  36.             System.out.println(getName() + " doing other things.");   
  37.         }   
  38.     }   
  39. }   
运行结果:当子线程都执行完第一个任务到达屏障后,执行下一个任务 
[java] view plain copy
  1. start subThread doing...   
  2. Thread-0 doing first things.   
  3. Thread-1 doing first things.   
  4. Thread-1 done first things.   
  5. Thread-0 done first things.   
  6. Thread-0 doing other things.   
  7. Thread-1 doing other things.  
三、Semaphore 
1.应用场景
 
多线程访问公共资源的情况在开发过程中经常遇见,如数据库连接,可能开启几十个线程进行并发读取,但是考虑到数据库连接性能和消耗,我们必须控制10个线程哪个是连接数据库。Semaphore就是用来控制同时访问特定资源的线程数量。 
2.使用方式 
Semaphore的构造方法Semaphore(int permits),permits标识许可证数量。执行任务前,acquire()方法获取一个许可证;任务执行完成后调用relese()方法归还许可证。没有获得许可证的子线程就阻塞等待。 
补充: 
tryAcquire():尝试获取许可证; 
intavaliablePermits():返回信号量中当前许可证的个数; 
intgetQueueLength():返回正在等待获取许可证的线程个数; 
booleanhasQueueThreads():是否有线程正在等待许可证; 
reducePermits(int reduction):减少reduction个许可证; 
getQueuedThreads():返回所有等待获取许可证的线程集合; 
3.实例代码 
[java] view plain copy
  1. public class Main {   
  2.     //创建2个许可证   
  3.     static Semaphore semaphore = new Semaphore(2);   
  4.    
  5.     public static void main(String[] args) {   
  6.         System.out.println("start subThread doing...");   
  7.         //同时开启4个子线程运行   
  8.         for (int i = 0; i < 4; i++) {   
  9.             SubThread subThread = new SubThread();   
  10.             subThread.start();   
  11.         }   
  12.     }   
  13.    
  14.     static class SubThread extends Thread {   
  15.         @Override   
  16.         public void run() {   
  17.             try {   
  18.                 //执行任务前获取许可证   
  19.                 semaphore.acquire();   
  20.                 System.out.println(getName() + "doing things.");   
  21.                 sleep(3000);   
  22.                 //执行完任务释放许可证   
  23.                 semaphore.release();   
  24.                 System.out.println(getName() + "finish things.");   
  25.             } catch (InterruptedException e) {   
  26.                 e.printStackTrace();   
  27.             }   
  28.         }   
  29.     }   
  30. }   
运行结果:同时只有2个线程运行,当某个线程运行完毕释放许可后,下一个线程才获取许可运行; 
[java] view plain copy
  1. start subThread doing...   
  2. Thread-0doing things.   
  3. Thread-1doing things.   
  4. Thread-1finish things.   
  5. Thread-2doing things.   
  6. Thread-0finish things.   
  7. Thread-3doing things.   
  8. Thread-2finish things.   
  9. Thread-3finish things.   
四、Exchanger 
1.应用场景 

在某些实际业务如流水录入中,为了避免错误。采用两个人同时录入,并对比录入的结果是否一致。Exchanger用于进行线程之间的数据交换,它提供了一个同步点,两个线程可以交换彼此的数据。 
2.使用方式 
两个线程通过exchange()方法交换数据,如果一个线程执行exchange()方法,它会一直等待第二个线程也执行exchange()方法。当两个线程都达到同步点时,就可以交换数据,将本线程产生的数据传递给对方。 
3.实例代码 
[java] view plain copy
  1. public class Main {   
  2.     //用户线程间交换数据(String)对象exchanger   
  3.     static Exchanger<String> exchanger = new Exchanger<>();   
  4.    
  5.     public static void main(String[] args) {   
  6.         //创建2个子线程分别执行   
  7.         SubThread1 subThread1 = new SubThread1();   
  8.         SubThread2 subThread2 = new SubThread2();   
  9.         subThread1.start();   
  10.         subThread2.start();    
  11.     }   
  12.    
  13.     static class SubThread1 extends Thread {   
  14.         @Override   
  15.         public void run() {   
  16.             try {   
  17.                 System.out.println(getName() + "start doing...");   
  18.                 //模拟执行完成后,获取结果result1,并将result1交换给对方线程   
  19.                 sleep(3000);   
  20.                 String result1 = "3000";   
  21.                 String result2 = exchanger.exchange(result1);   
  22.                 //待两个线程都执行完毕后,交换数据进行比较   
  23.                 System.out.println(getName() + " thread1 result:" + result1 + " is equals thread2 result:" + result2 +   
  24.                         "," + result1.equals(result2));   
  25.             } catch (InterruptedException e) {   
  26.                 e.printStackTrace();   
  27.             }   
  28.         }   
  29.     }   
  30.    
  31.     static class SubThread2 extends Thread {   
  32.         @Override   
  33.         public void run() {   
  34.             try {   
  35.                 System.out.println(getName() + "start doing...");   
  36.                 //模拟执行完成后,获取结果result2,并将result2交换给对方线程   
  37.                 sleep(2000);   
  38.                 String result2 = "2000";   
  39.                 String result1 = exchanger.exchange(result2);   
  40.                 //待两个线程都执行完毕后,交换数据进行比较   
  41.                 System.out.println(getName() + " thread1 result:" + result1 + " is equals thread2 result:" + result2 +   
  42.                         "," + result1.equals(result2));   
  43.             } catch (InterruptedException e) {   
  44.                 e.printStackTrace();   
  45.             }   
  46.         }   
  47.     }   
  48. }   

运行结果:线程1优先执行完毕,等待线程0执行完毕后,交换数据分别进行结果比较 

[java] view plain copy
  1. Thread-1start doing...   
  2. Thread-0start doing...   
  3. Thread-1finish doing...   
  4. Thread-0finish doing...   
  5. Thread-0 thread1 result:3000 is equals thread2 result:2000,false   
  6. Thread-1 thread1 result:3000 is equals thread2 result:2000,false  
原创粉丝点击