继上一次讲的开始说吧。
new ThreadPoolExecutor() 自定义参数,理解每个参数什么含义,具体到某些业务场景,还要看具体怎么用。在此,再解释下核心线程数跟最大线程数到底有什么作用。举个例子:
new ThreadPoolExecutor(1,2,60,TimeUnit.Days,ArrayBlockingQueue),这个ArrayBlockingQueue = new ArrayBlockingQueue(4),某个业务场景,需要执行7个sql,交给了ThreadExecutor来执行,ThreadExecutor 核心线程数只有1个,所以ThreadExecutor会执行第一个sql,这时候没执行完,剩下的6个sql 会放到queue中,由于ArrayBlockingQueue是个有界队列,最多只会放下4个任务, (LinkedBlockingQueue是无界队列,所以想放多少任务都可以),剩下两个任务,ThreadPoolExecutor 会用 max线程数-核心线程数,直接new 对象去执行。上面例子,因为最多有2个线程,核心线程只有一个,所以还有一个线程,程序发现,我线程不够用啊,因为还有两个子任务,那我就报错吧。
demo:
public class UseThreadExecutorPool implements Runnable { private static AtomicInteger count = new AtomicInteger(); @Override public void run() { int all = count.incrementAndGet(); System.out.println(all); } //自定义线程池 各个参数什么意思 corePoolSize 核心线程数,maximumPoolSize 最大线程数, //当 任务数量 <= corePoolSize 直接执行, 当 maximumPoolSize =>任务数量 > corePoolSize ,任务数量-corePoolSize 的任务 加到queue里面,拿corePoolSize 线程执行, //当 maximumPoolSize < 任务数量 在不超过 maximumPoolSize的情况下 新建线程执行 加满queue之后的 任务,当超过了最大线程数,就报错, public static void main(String[] args) { ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<UseThreadExecutorPool>(4); ExecutorService executorService = new ThreadPoolExecutor(1, 2, 60, TimeUnit.DAYS, arrayBlockingQueue); UseThreadExecutorPool useThreadExecutorPool0 = new UseThreadExecutorPool(); UseThreadExecutorPool useThreadExecutorPool1 = new UseThreadExecutorPool(); UseThreadExecutorPool useThreadExecutorPool2 = new UseThreadExecutorPool(); UseThreadExecutorPool useThreadExecutorPool3 = new UseThreadExecutorPool(); UseThreadExecutorPool useThreadExecutorPool4 = new UseThreadExecutorPool(); UseThreadExecutorPool useThreadExecutorPool5 = new UseThreadExecutorPool(); UseThreadExecutorPool useThreadExecutorPool6 = new UseThreadExecutorPool(); executorService.execute(useThreadExecutorPool0); executorService.execute(useThreadExecutorPool1); executorService.execute(useThreadExecutorPool2); executorService.execute(useThreadExecutorPool3); executorService.execute(useThreadExecutorPool4); executorService.execute(useThreadExecutorPool5); executorService.execute(useThreadExecutorPool6); executorService.shutdown(); } },当然呢,这个错误是可以自己处理的,详细看上一篇博客。
-----------------------------------------------------------------------------
下面谈谈CyclicBarrier 、CountDownLatch 和 信号量。
先说下CyclicBarrier 和CountDownLatch吧。这俩都可以实现实时通知,类似于分布式的并行计算,区别就是CyclicBarrier 是唤起多个线程,CountDownLatch唤起一个线程,举例个业务场景,运动五个跑步选手比赛,发令枪没响之前,各自干各自的事,喝水啊,绑鞋带啊,但是喊准备的时候,就统一听发令员的枪声了,下面看下例子
public class UseCyclicbarrier { static class Runner implements Runnable { private CyclicBarrier barrier; private String name; public Runner(CyclicBarrier barrier, String name) { this.barrier = barrier; this.name = name; } @Override public void run() { try { Thread.sleep(5000); System.out.println(name + " 准备好了 "); barrier.await(); } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } System.out.println(name + "go"); } } public static void main(String[] args) { CyclicBarrier barrier = new CyclicBarrier(3); ExecutorService executor = Executors.newFixedThreadPool(3); executor.submit(new Thread(new Runner(barrier, "小明"))); executor.submit(new Thread(new Runner(barrier, "小白"))); executor.execute(new Thread(new Runner(barrier, "小红"))); executor.shutdown(); } }CyclicBarrier 初始化后面写个3,意思就是对象得调用三次await()方法,才能一同唤起,做自己的操作,两个就不行,会一直等待。三个线程都准备好了,才一起开始。这就是CyclicBarrier 。记住,CyclicBarrier 是唤起子线程,下面讲的CountDownLatch 是唤起一个线程,类似于统计的操作,举个CountDownLatch的demo
public class UseCountDownLatch { public static void main(String[] args) { final CountDownLatch countDownLatch = new CountDownLatch(2); Thread t1 = new Thread(new Runnable() { @Override public void run() { System.out.println("进入t1 等待其他线程执行完毕"); try { countDownLatch.await(); System.out.println("执行完毕"); } catch (InterruptedException e) { e.printStackTrace(); } } }); Thread t2 = new Thread(new Runnable() { @Override public void run() { System.out.println("进入t2 "); try { countDownLatch.countDown(); } catch (Exception e) { e.printStackTrace(); } } }); Thread t3 = new Thread(new Runnable() { @Override public void run() { System.out.println("进入t3 "); try { countDownLatch.countDown(); } catch (Exception e) { e.printStackTrace(); } } }); t1.start(); t2.start(); t3.start(); } }可以把代码粘贴到自己的编辑器里面,自己慢慢体会这两者的区别
信号量:说到信号量,不得不说的一个词叫分流,限流,这是在应用架构中经常性听到的词,什么意思呢,就是一个应用系统最大能承受10个客户,当前有20个客户,可以从网络层限制,也可以从服务层限制,还可以从java层面去限制,java层面就是用的信号量,信号量可以定义当前可以处理多少个请求,达到分流的效果。服务层呢可以用nginx做分流,限流。网络层的话 类似于cdn做镜像,这也是分流,限流的一种策略。代码不展示了,百度下一堆,知道什么意思就行。
下面说说英文单词;
pv:每个页面的访问量,不计算单个ip,刷新一个即计数。
uv:最小请求单位(ip,每天只统计一次)
qps(tps):每秒请求数
rt:每个request请求的返回时间。
这些名词,有可能在做自动化测试的时候提到。
----------------------------------------------------------------------------------------
下面说说 重入锁 跟 读写锁:也就是Lock 跟RetreentLock,这两者与sync 有什么区别呢,1.8以前sync性能不太好,1.8以后没区别,如果非要问1.8以后的区别的话,Lock锁更加简单化,随性化。RetreentLock 试用场景 读多写少 ,读少写多的话可以使用RetreentReadWriteLock 代码不展示,
还有两个锁是 readLock 跟 writeLock。记住一个方言就是,读读共享,读写互斥,写写互斥。
下篇博客谈谈disruptor并发框架。(一个很nb的并发框架)