zoukankan      html  css  js  c++  java
  • 【面试专栏】Java常用并发工具类

    1. 简介

      Java中常见的四种并发工具类:

    • CountDownLatch
    • CyclicBarrier
    • Semaphore
    • Exchanger

    2. 主线程等待子线程之CountDownLatch

    • 原理
        CountDownLatch允许一个或多个线程等待其他一组线程完成操作,再继续执行。
        CountDownLatch的构造函数传入一个int类型的参数N作为计数器,这个参数N作为需要等待的线程的数量。当调用CountDownLatch的countDown方法时N减1,CountDownLatch的await方法会阻塞当前线程(即主线程在闭锁上等待),直到N减为零,此时闭锁的主线程继续执行任务。
    • 示例图
    • 使用场景
        超市的某一种商品,当促销时往往会有多人从货架上取出商品,只到该商品销售完后,促销结束。
    import java.util.concurrent.CountDownLatch;
    
    public class CountDownLatchDemo {
    
    	public static void main(String[] args) throws InterruptedException {
    		CountDownLatch countDownLatch = new CountDownLatch(5);
    		System.out.println("商品货源充足,促销开始!");
    		for (int i = 1; i <= 5; i++) {
    			new Thread(() -> {
    				System.out.println("商品编号[" + Thread.currentThread().getName() + "]已售出");
    				countDownLatch.countDown();
    			}, String.valueOf(i)).start();
    		}
    
    		countDownLatch.await();
    		System.out.println("商品已售罄,促销结束!");
    	}
    
    }
    
    商品货源充足,促销开始!
    商品编号[2]已售出
    商品编号[3]已售出
    商品编号[1]已售出
    商品编号[4]已售出
    商品编号[5]已售出
    商品已售罄,促销结束!
    

    3. 同步到达屏障之CyclicBarrier

    • 原理
        让一组线程在到达一处屏障(即同步点)时被阻塞,只到最后一个线程达到屏障点,所有被阻塞的线程被释放继续执行。
        CyclicBarrier的构造函数传入一个int类型的参数N作为计数器,这个参数N作为需要等待的线程的数量。每个线程调用CyclicBarrier的await方法使自己被阻塞,当N个线程调用了await方法后,所有线程停止等待,继续执行。
    • 与CountDownLatch对比
        相同点:都需要等待线程然后才能继续执行。
        不相同点:可以循环使用。假如,将CyclicBarrier的计数器设置为10,当等待到10个线程后,计数器归0,然后可以继续等待10个线程。
    • 示例图
    • 使用场景
        班主任收集学生信息,当全班同学的信息收集完成后,才可以交给教务处。
    import java.util.concurrent.BrokenBarrierException;
    import java.util.concurrent.CyclicBarrier;
    
    public class CyclicBarrierDemo {
    
    	public static void main(String[] args) {
    		CyclicBarrier cyclicBarrier = new CyclicBarrier(5, () -> {
    			System.out.println("收集信息完成,转交教务处!");
    		});
    
    		for (int i = 1; i <= 5; i++) {
    			new Thread(() -> {
    				System.out.println("收集到学生[" + Thread.currentThread().getName() + "]的信息");
    				try {
    					cyclicBarrier.await();
    				} catch (InterruptedException | BrokenBarrierException e) {
    					e.printStackTrace();
    				}
    			}, String.valueOf(i)).start();
    		}
    
    	}
    
    }
    
    收集到学生[1]的信息
    收集到学生[5]的信息
    收集到学生[3]的信息
    收集到学生[2]的信息
    收集到学生[4]的信息
    收集信息完成,转交教务处!
    

    4. 控制并发线程数之Semaphore

    • 原理
        JDK1.5就提供了Semaphore来统计一定数量的线程获得许可而对公共资源的访问。
        Semaphore的构造函数入一个int类型的参数N作为可颁发的许可数量,线程通过调用acquire方法获取许可,只有获得许可的线程才可以执行任务,执行完成后调用release方法归还许可。当线程获取许可失败时,表示已经到达了最大的并发线程数,即许可已经颁发完毕,需要等待有线程归还后再次尝试获取。
    • 示例图
    • 使用场景
        餐厅只有两个位置可以就餐,每个人需要就餐时,需要先看位置是否为空。为空时可直接就餐,不为空时则等待。
    import java.util.concurrent.Semaphore;
    import java.util.concurrent.TimeUnit;
    
    public class SemaphoreDemo {
    
    	public static void main(String[] args) {
    		Semaphore semaphore = new Semaphore(2);
    
    		for (int i = 1; i <= 5; i++) {
    			new Thread(() -> {
    				try {
    					semaphore.acquire();
    					TimeUnit.SECONDS.sleep(3);
    
    					System.out.println("顾客[" + Thread.currentThread().getName() + "]就餐,时间为3秒钟");
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				} finally {
    					semaphore.release();
    				}
    			}, String.valueOf(i)).start();
    		}
    	}
    
    }
    
    顾客[1]就餐,时间为3秒钟
    顾客[2]就餐,时间为3秒钟
    顾客[4]就餐,时间为3秒钟
    顾客[3]就餐,时间为3秒钟
    顾客[5]就餐,时间为3秒钟
    

    5. 交换数据之Exchanger

    • 原理
        Exchanger是一个线程间协作的工具类,用于线程间的数据交换。线程A调用exchange方法到达同步点,等待数据交换,线程B也调用exchange方法到达同步点,此时线程A和线程B进行数据交换。
    • 示例图
    • 使用场景
        幼儿园进行玩具分享会,当两个小朋友都原因交换玩具时,进行交换玩具。
    import java.util.concurrent.Exchanger;
    
    public class ExchangerDemo {
    
    	public static void main(String[] args) {
    		Exchanger<String> exchanger = new Exchanger<>();
    
    		for (int i = 1; i <= 6; i++) {
    			new Thread(() -> {
    				final String treadName = Thread.currentThread().getName();
    				try {
    					String data = "[物体" + treadName + "]";
    					System.out.println("小朋友[" + treadName + "]交换前拥有" + data);
    					data = exchanger.exchange(data);
    
    					System.err.println("小朋友[" + treadName + "]交换后拥有" + data);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    			}, String.valueOf(i)).start();
    		}
    	}
    
    }
    
    小朋友[1]交换前拥有[物体1]
    小朋友[3]交换前拥有[物体3]
    小朋友[6]交换前拥有[物体6]
    小朋友[4]交换前拥有[物体4]
    小朋友[2]交换前拥有[物体2]
    小朋友[5]交换前拥有[物体5]
    小朋友[3]交换后拥有[物体1]
    小朋友[1]交换后拥有[物体3]
    小朋友[4]交换后拥有[物体6]
    小朋友[6]交换后拥有[物体4]
    小朋友[5]交换后拥有[物体2]
    小朋友[2]交换后拥有[物体5]
    
  • 相关阅读:
    oracle中Blob和Clob类型的区别
    为什么要分库分表
    Enable file editing in Visual Studio's debug mode
    SQL Server Dead Lock Log
    Debug .NET Framework Source
    SQL Server text field里面有换行符的时候copy到excel数据会散乱
    诊断和修复Web测试记录器(Web Test Recorder)问题
    Can't load Microsoft.ReportViewer.ProcessingObjectModel.dll
    'telnet' is not recognized as an internal or external command
    Linq to XML
  • 原文地址:https://www.cnblogs.com/cao-lei/p/14277053.html
Copyright © 2011-2022 走看看