java架构《并发线程高级篇三》

    本章主要介绍和讲解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%的时间内达到。