本章主要介绍和讲解concurrent.util里面的常用的工具类。
一、CountDownLatch使用:(用于阻塞主线程)
应用场景 :通知线程休眠和运行的工具类,是wait和notify的升级版本。notify不会释放锁,但是 countDown()会释放锁
实例化:final CountDownLatch countDown = new CountDownLatch(2); 使用在Thread里面: countDownLatch.countDown(); 相当于 notfiy; 特点: countDown()会释放锁 countDownLatch.await();相当于 wait;
二、CyclicBarrier使用:barrier(障碍) (用于多个阻塞线程等待,都通准备好后,一起开始执行当前线程的代码)
应用场景:多个线程,当任何一个线程都没准备好,都阻塞。当都准备好时,都执行自己的线程。
注意事项:多少个线程,需要多少个barrier.await();否则都会阻塞在此。
代码解析:
1 public class UseCyclicBarrier { 2 3 static class Runner implements Runnable { 4 private CyclicBarrier barrier; 5 private String name; 6 7 public Runner(CyclicBarrier barrier, String name) { 8 this.barrier = barrier; 9 this.name = name; 10 } 11 @Override 12 public void run() { 13 try { 14 Thread.sleep(1000 * (new Random()).nextInt(5)); 15 System.out.println(name + " 准备OK."); 16 barrier.await(); 17 } catch (InterruptedException e) { 18 e.printStackTrace(); 19 } catch (BrokenBarrierException e) { 20 e.printStackTrace(); 21 } 22 System.out.println(name + " Go!!"); 23 } 24 } 25 26 public static void main(String[] args) throws IOException, InterruptedException { 27 CyclicBarrier barrier = new CyclicBarrier(3); // 3 28 ExecutorService executor = Executors.newFixedThreadPool(3); 29 30 executor.submit(new Thread(new Runner(barrier, "zhangsan"))); 31 executor.submit(new Thread(new Runner(barrier, "lisi"))); 32 executor.submit(new Thread(new Runner(barrier, "wangwu"))); 33 34 executor.shutdown(); 35 } 36 37 }
三、Future模式补充和Callable
使用场景:需要大数量量处理数据的时候,异步去处理数据,提高程序的吞吐量
注意事项:真正进行业务逻辑处理的类, 类一定实现Callable接口,重写Call()方法。
1 private String para; 2 3 public UseFuture(String para){ 4 this.para = para; 5 } 6 7 /** 8 * 这里是真实的业务逻辑,其执行可能很慢 9 */ 10 @Override 11 public String call() throws Exception { 12 //模拟执行耗时 13 Thread.sleep(5000); 14 String result = this.para + "处理完成"; 15 return result; 16 } 17 18 19 20 String queryStr = "query"; 21 //构造FutureTask,并且传入需要真正进行业务逻辑处理的类,该类一定是实现了Callable接口的类 22 FutureTask<String> future = new FutureTask<String>(new UseFuture(queryStr)); 23 24 FutureTask<String> future2 = new FutureTask<String>(new UseFuture(queryStr)); 25 //创建一个固定线程的线程池且线程数为1, 26 ExecutorService executor = Executors.newFixedThreadPool(2); 27 //这里提交任务future,则开启线程执行RealData的call()方法执行 28 //submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值 29 30 Future f1 = executor.submit(future); //单独启动一个线程去执行的 31 Future f2 = executor.submit(future2); 32 System.out.println("请求完毕"); 33 34 try { 35 //这里可以做额外的数据操作,也就是主程序执行其他业务逻辑 36 System.out.println("处理实际的业务逻辑..."); 37 Thread.sleep(1000); 38 } catch (Exception e) { 39 e.printStackTrace(); 40 } 41 //调用获取数据方法,如果call()方法没有执行完成,则依然会进行等待 42 System.out.println("数据:" + future.get()); 43 System.out.println("数据:" + future2.get()); 44 45 executor.shutdown();
总结:FutureTask对象。当使用其get()方法时,会异步加载对应的返回结果。Future 对象。当使用其get()方法时。返回null,则表示该子线程已经完成。
submit和execute的区别: 第一点是submit可以传入实现Callable接口的实例对象, 第二点是submit方法有返回值。
四、Semaphore信号量
使用场景:当 系统上线之前,对系统进行信息并发量的评估。进行自动化测试。在业务逻辑层,进行限流
相关概念:
PV(page view) :网站总访问量,页面浏览量或点击量,每刷新一次都记录下来。
UV(unique Visitor):访问网站的IP总数,没一个Ip,一天内只记录一次。
QRS(Query per second):每秒的查询数。
RT(response time):访问相应时间。
代码解析:
创建一个无界阻塞的线程池。通过循环产生多个线程。每个线程里面进行业务逻辑实现。每次实现业务前。都获得semp.acquire()(许可),完成之后,都semp.release()(释放许可),做到限流。
用法:
1 // 只能5个线程同时访问 2 final Semaphore semp = new Semaphore(5); 3 4 // 获取许可 5 6 semp.acquire(); 7 System.out.println("Accessing: " + NO); 8 //模拟实际业务逻辑 9 Thread.sleep((long) (Math.random() * 10000)); 10 // 访问完后,释放 11 semp.release();
峰值计算:
峰值qp:(总PV*80%)/(60*60*24*20%)
80%的访问请求将在20%的时间内达到。