1.CountDownLatch:可以实现线程计数,阻塞后续线程
CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,
此时就可以利用CountDownLatch来实现这种功能了。
countDown()实现计数器-1
await()等待拦截方法,等待计数器为0时再放行,否则则一直阻塞
getCount()获取当前计数器中计数数量
案例:
/** * 等待子线程全部执行完毕之后再执行主线程内容 * @param args */ private static CountDownLatch countDownLatch=new CountDownLatch(2); public static void main(String[] args) throws InterruptedException { System.out.println("======主线程开始运行====="); //第一个子线程 new Thread(()->{ System.out.println("子线程"+Thread.currentThread().getName()+"开始运行~"); //线程数量-1操作,通知该线程运行完毕 countDownLatch.countDown(); }).start(); //第二个子线程 new Thread(()->{ System.out.println("子线程"+Thread.currentThread().getName()+"开始运行~"); //线程数量-1操作,通知该线程运行完毕 countDownLatch.countDown(); }).start(); //等待,等待计数器中线程计数为0时才继续向下执行 countDownLatch.await(); System.out.println("子线程执行完毕,主线程继续执行"); //获取当前计数线程数量 /*while (true){ if(countDownLatch.getCount()==0){ System.out.println("子线程执行完毕,主线程继续执行"); break; } }*/ }
2.CyclicBarrier:类似于栅栏,进行拦截,等待所有线程都准备,然后统一放行,阻塞当前线程
//设置等待线程数量,当线程数量到达指定数量时,统一向下运行 private static CyclicBarrier cyclicBarrier=new CyclicBarrier(10); public static void main(String[] args) { //创建10个线程 for (int i = 1; i <=10 ; i++) { new Thread(()->{ try { Thread.sleep(100); System.out.println(Thread.currentThread().getName()+"准备就绪"); //等待 cyclicBarrier.await(); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName()+"开始比赛~"); }).start(); } }
3.Semaphore:可以做资源控制,容器中有几个资源,那么线程执行时先申请资源,资源如果可用则继续执行,如果资源不可用则阻塞等待
当资源占用完毕之后将该资源释放,其他线程排队占用
//最多允许三个线程同时执行 private static Semaphore semaphore=new Semaphore(3); public static void main(String[] args) { for (int i = 1; i <=5 ; i++) { new Thread(()->{ try { //申请资源,发生阻塞 System.out.println(Thread.currentThread().getName()+"申请车位~"); semaphore.acquire(); System.out.println(Thread.currentThread().getName()+"可以停车了"); //模拟上厕所时间 Thread.sleep(1000); System.out.println(Thread.currentThread().getName()+"溜了溜了~"); //释放资源 semaphore.release(); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); } }
4.Exchanger:可以执行线程的资源交换,线程数量必须为偶数,因为是两两相互交换资源,如果不是偶数默认情况下导致阻塞,可以设置交换资源超时时间
private static String str1="资源1"; private static String str2="资源2"; //构建资源交换对象 private static Exchanger<String> stringExchanger=new Exchanger<>(); public static void main(String[] args) { //第一个线程 new Thread(()->{ System.out.println(Thread.currentThread().getName()+"初始占用资源:"+str1); //资源交换,将资源交给其他线程和获取到其他线程交换过来的资源 try { String newStr = stringExchanger.exchange(str1); System.out.println(Thread.currentThread().getName()+"交换资源:"+newStr); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); //第二个线程 new Thread(()->{ System.out.println(Thread.currentThread().getName()+"初始占用资源:"+str2); //资源交换,将资源交给其他线程和获取到其他线程交换过来的资源 try { String newStr = stringExchanger.exchange(str2); System.out.println(Thread.currentThread().getName()+"交换资源:"+newStr); } catch (InterruptedException e) { e.printStackTrace(); } }).start(); //第三个线程 new Thread(()->{ System.out.println(Thread.currentThread().getName()+"初始占用资源:"+str2); //资源交换,将资源交给其他线程和获取到其他线程交换过来的资源 try { String newStr = stringExchanger.exchange(str2,1000, TimeUnit.MILLISECONDS); System.out.println(Thread.currentThread().getName()+"交换资源:"+newStr); } catch (InterruptedException | TimeoutException e) { e.printStackTrace(); } }).start(); }