• JUC——并发编程—第二部分


    集合类不安全

    list不安全

    1. //报错 java.util.ConcurrentModificationException
    2. public class ListTest {
    3. public static void main(String[] args) {
    4. List list= new CopyOnWriteArrayList<>();
    5. //并发下Arrayist边读边写会不安全的
    6. /**
    7. * 解决方案:
    8. * 1.List list= new Vector<>();
    9. * 2.List list= Collections.synchronizedList(new ArrayList<>());
    10. * 3.List list= new CopyOnWriteArrayList<>();
    11. */
    12. for(int i=0;i<50;i++){
    13. new Thread(()->{
    14. list.add(UUID.randomUUID().toString().substring(0,5));
    15. System.out.println(list);
    16. },String.valueOf(i)).start();
    17. }
    18. }
    19. }

    上面这个多个线程边读边写时会出现如下报错

    java.util.ConcurrentModificationException

    在CopyOnWriteArrayList的底层用一个这样的数据实现

    volatile内存,模型中规定保证线程间的可见性,但不保证原子性

    CopyOnWrite使用的是lock锁,Vertor使用的是synchronized,有sync都会很慢。

    list的解决方案有使用vector这个安全类和工具类和 juc.

    工具类是将其变成synchronized,但很慢,juc是用写入时复制。

    Set不安全

    set和List是一个同级的关系,都是Collection的子类。

    所以set在边读边写时也会有java.util.ConcurrentModificationException报错。

    但是set没有vector,只有工具类和juc的解决方案。

    1. public class SetList {
    2. public static void main(String[] args) {
    3. Set set = new HashSet<>();
    4. // Set set = Collections.synchronizedSet(new HashSet<>());
    5. // Set set = new CopyOnWriteArraySet<>();
    6. for(int i=0;i<50;i++){
    7. new Thread(()->{
    8. set.add(UUID.randomUUID().toString().substring(0,5));
    9. System.out.println(set);
    10. },String.valueOf(i)).start();
    11. }
    12. }
    13. }

    hashset底层就是hashmap。

    hashset的add方法就是hashmap的put方法。

    map不安全

    这个也有" java.util.ConcurrentModificationException报错

     这里的解决方案是juc下的ConcurrentHashMap。

    1. public class MapList {
    2. public static void main(String[] args) {
    3. //加载因子,初始化容量 0.75和16
    4. // Map map=new HashMap<>();
    5. Map map=new ConcurrentHashMap<>();
    6. for(int i=0;i<50;i++){
    7. new Thread(()->{
    8. map.put(Thread.currentThread().getName(),UUID.randomUUID().toString().substring(0,5));
    9. System.out.println(map);
    10. },String.valueOf(i)).start();
    11. }
    12. }
    13. }

    走进Callable

    callable接口就类似于runnable接口,然而runnable接口不会返回结果也不会抛出异常,callable就可以。一个是call()方法,一个是run()方法.

    callable接口需要提供一个泛型,泛型的参数等于方法的返回值。 

    如何用new Thread().start接收callable接口并开启线程

    Thread()只能接收runnable参数,不认识callable()参数,所以callable要通过runnable去做一个桥梁,在runnable里面有如下的一些实现。

    其中FutureTask这个实现类与Callable有联系,如下所示,有一个构造参数就是Callable

    这里用到的应该就是适配器模式,这里的futuretask就是一个适配器。

    1.两个同样的callable实现类开启的线程内的输出结果会被缓存。

    2.结果可能会等待,会阻塞。

    1. public class CallavkeTest {
    2. public static void main(String[] args) throws ExecutionException, InterruptedException {
    3. // new Thread(new Runnable()).start(); //传统方式
    4. // new Thread(new FutureTask()).start();
    5. // new Thread(new FutureTask(Callable)).start();
    6. mythread mythread=new mythread(); //callable接口的实现类
    7. FutureTask futureTask = new FutureTask(mythread); //callable接口的适配类
    8. new Thread(futureTask,"A").start();
    9. new Thread(futureTask,"B").start(); //只会输出一个call,结果被缓存了,达到提高效率的目的
    10. String str=(String)futureTask.get(); //获取callable的返回结果,get方法会等待结果,可能产生阻塞,要将其放在最后
    11. //或者通过异步通信来处理!
    12. System.out.println(str);
    13. }
    14. }
    15. class mythread implements Callable {
    16. @Override
    17. public String call() throws Exception {
    18. System.out.println("call方法被调用");
    19. //耗时操作
    20. return "1230";
    21. }
    22. }
    23. //class mythread implements Runnable{
    24. //
    25. // @Override
    26. // public void run() {
    27. //
    28. // }
    29. //}

     

    常用的辅助类

    CountDownLatch

    原理: 

    countDownLatch.countDown(); //-1

    countDownLatch.await(); //等待计数器归零再向下执行

    1. //计数器
    2. public class CountDownLatchDemo {
    3. public static void main(String[] args) throws InterruptedException {
    4. //总数是6
    5. CountDownLatch countDownLatch = new CountDownLatch(6);
    6. for(int i=0;i<6;i++){
    7. new Thread(()->{
    8. System.out.println(Thread.currentThread().getName()+"go out");
    9. countDownLatch.countDown(); //-1
    10. },String.valueOf(i)).start();
    11. }
    12. countDownLatch.await(); //等待计数器归零再向下执行
    13. System.out.println("Close door");
    14. // countDownLatch.countDown(); //-1
    15. }
    16. }

    CyclicBarrier

    加法计数器

     有两种构造参数,一个是传个计数,一个是传个计数完之后要执行的线程。

    1. public class CyclicBarrierDemo {
    2. public static void main(String[] args) {
    3. CyclicBarrier cyclicBarrier=new CyclicBarrier(7,()->{
    4. System.out.println("g盖亚!!!");
    5. });
    6. for(int i=0;i<7;i++){
    7. final int temp=i;
    8. //lamda表达式能操作到i吗?
    9. new Thread(()->{
    10. System.out.println(Thread.currentThread().getName()+"收集"+temp+"个"); //间接获得
    11. try {
    12. cyclicBarrier.await(); //等待
    13. } catch (InterruptedException e) {
    14. throw new RuntimeException(e);
    15. } catch (BrokenBarrierException e) {
    16. throw new RuntimeException(e);
    17. }
    18. }).start();
    19. }
    20. }
    21. }

    如果计数为8但是线程只有7个的话,就会永远卡死在一个地方。 

    Semaphore(信号量)

    这个 的实现也有两种参数

    1. public class SemaphoreDemo {
    2. public static void main(String[] args) {
    3. //线程数量:停车位
    4. Semaphore semaphore = new Semaphore(3);
    5. for(int i=1;i<=6;i++){
    6. new Thread(()->{
    7. //acquire()得到
    8. try {
    9. semaphore.acquire();
    10. System.out.println(Thread.currentThread().getName()+"抢到车位");
    11. TimeUnit.SECONDS.sleep(2);
    12. System.out.println(Thread.currentThread().getName()+"离开车位 ");
    13. } catch (InterruptedException e) {
    14. throw new RuntimeException(e);
    15. }finally {
    16. //realease() 释放
    17. semaphore.release();
    18. }
    19. },String.valueOf(i)).start();
    20. }
    21. }
    22. }

    一开始只有三个进去了,后面三个都出去了,后三个才能进来,这里的主要应用场景就是限流。 

    原理:

    semaphore.acquire();  //获取,假设已经的满了,就等待到资源被释放为止。

    semaphore.release();  //释放,将当前信号量释放+1,然后唤醒等待线程。

    作用:多个共享资源的互斥使用。并发限流,控制最大线程数。

    读写锁

    readwritelock只有一个实现类,可重入的读写锁. 读的时候可以多个线程同时读,但是写的时候只能一个线程在写。

    如下所示的一个自定义缓存类读写操作

    1. /**
    2. * readwritelock
    3. */
    4. public class readwritelockdemo {
    5. public static void main(String[] args) {
    6. MyCache myCache=new MyCache();
    7. //写入
    8. for(int i=0;i<5;i++){
    9. final int temp=i;
    10. new Thread(()->{
    11. myCache.put(temp+"",temp+"");
    12. },String.valueOf(i)).start();
    13. }
    14. //读取
    15. for(int i=0;i<5;i++){
    16. final int temp=i;
    17. new Thread(()->{
    18. myCache.get(temp+"");
    19. },String.valueOf(i)).start();
    20. }
    21. }
    22. }
    23. /**
    24. * 自定义缓存
    25. */
    26. class MyCache{
    27. private volatile Map map=new HashMap<>();
    28. //存入
    29. public void put(String key,Object value){
    30. System.out.println(Thread.currentThread().getName()+"写入"+key);
    31. map.put(key,value);
    32. System.out.println(Thread.currentThread().getName()+"写入完毕");
    33. }
    34. //读取
    35. public void get(String key){
    36. System.out.println(Thread.currentThread().getName()+"读取"+key);
    37. Object o=map.get(key);
    38. System.out.println(Thread.currentThread().getName()+"读取完毕");
    39. }
    40. }

    输出如下,在一个线程写入的过程中另一个线程也在写入,这种情况是不能发生的

    使用了读写锁之后,写操作只会允许一个线程执行,读操作则会有多个线程同时进行.。

    1. /**
    2. * readwritelock
    3. */
    4. public class readwritelockdemo {
    5. public static void main(String[] args) {
    6. // MyCache myCache=new MyCache();
    7. MyCacheLock myCacheLock=new MyCacheLock();
    8. //写入
    9. for(int i=0;i<5;i++){
    10. final int temp=i;
    11. new Thread(()->{
    12. myCacheLock.put(temp+"",temp+"");
    13. },String.valueOf(i)).start();
    14. }
    15. //读取
    16. for(int i=0;i<5;i++){
    17. final int temp=i;
    18. new Thread(()->{
    19. myCacheLock.get(temp+"");
    20. },String.valueOf(i)).start();
    21. }
    22. }
    23. }
    24. /**
    25. * 加上锁之后
    26. */
    27. class MyCacheLock{
    28. private volatile Map map=new HashMap<>();
    29. //读写锁,可以更加细力度的控制
    30. private ReadWriteLock readWriteLock=new ReentrantReadWriteLock();
    31. //存入,只有一个线程写
    32. public void put(String key,Object value){
    33. readWriteLock.writeLock().lock();
    34. try{
    35. System.out.println(Thread.currentThread().getName()+"写入"+key);
    36. map.put(key,value);
    37. System.out.println(Thread.currentThread().getName()+"写入完毕");
    38. }catch (Exception e){
    39. e.printStackTrace();
    40. }finally {
    41. readWriteLock.writeLock().unlock();
    42. }
    43. }
    44. //读取,所有线程都能读
    45. public void get(String key){
    46. readWriteLock.readLock().lock();
    47. try{
    48. System.out.println(Thread.currentThread().getName()+"读取"+key);
    49. Object o=map.get(key);
    50. System.out.println(Thread.currentThread().getName()+"读取完毕");
    51. }catch (Exception e){
    52. e.printStackTrace();
    53. }finally {
    54. readWriteLock.readLock().unlock();
    55. }
    56. }
    57. }

    阻塞队列

    JUC有这样一个阻塞队列的接口,t它的实现有一个SynchronousQueue同步队列,还有一些数组阻塞队列,和链表阻塞队列等等

    可以看见Queue和List和Set是同一级别的,在Queue接口下有这个BlockingQueue接口和Deque和AbstractQueue。

     典型使用场景: 多线程并发处理,线程池,生产者消费者。

    阻塞队列四组API

    1.抛出异常2.不会抛出异常3.阻塞等待4.超时等待

    方式抛出异常有返回值,不抛出异常阻塞等待超时等待
    添加addoffer()put()offer()
    移除removepoll()take()poll()
    检测队首元素elementpeek
    1. public class Test {
    2. public static void main(String[] args) throws InterruptedException {
    3. // test1();
    4. test4();
    5. }
    6. /**
    7. * 抛出异常
    8. */
    9. public static void test1(){
    10. //设置队列大小
    11. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    12. System.out.println(blockingQueue.add("a"));
    13. System.out.println(blockingQueue.add("b"));
    14. System.out.println(blockingQueue.add("c"));
    15. //Exception in thread "main" java.lang.IllegalStateException: Queue full
    16. // blockingQueue.add("d");
    17. System.out.println("_______________");
    18. System.out.println(blockingQueue.remove());
    19. System.out.println(blockingQueue.remove());
    20. System.out.println(blockingQueue.remove());
    21. //Exception in thread "main" java.util.NoSuchElementException
    22. // System.out.println(blockingQueue.remove());
    23. }
    24. /**
    25. * 有返回值,不抛出异常
    26. */
    27. public static void test2(){
    28. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    29. System.out.println(blockingQueue.offer("a"));
    30. System.out.println(blockingQueue.offer("b"));
    31. System.out.println(blockingQueue.offer("c"));
    32. System.out.println(blockingQueue.offer("d")); //不抛出异常,返回false
    33. System.out.println("_______________————————————————————");
    34. System.out.println(blockingQueue.poll());
    35. System.out.println(blockingQueue.poll());
    36. System.out.println(blockingQueue.poll());
    37. System.out.println(blockingQueue.poll()); //也不抛出异常,返回null
    38. }
    39. /**
    40. * 等待,阻塞(一直阻塞)
    41. */
    42. public static void test3() throws InterruptedException {
    43. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    44. blockingQueue.put("a");
    45. blockingQueue.put("b");
    46. blockingQueue.put("c");
    47. // blockingQueue.put("d"); //一直阻塞
    48. System.out.println(blockingQueue.take());
    49. System.out.println(blockingQueue.take());
    50. System.out.println(blockingQueue.take());
    51. System.out.println(blockingQueue.take());//也是一直阻塞
    52. }
    53. /**
    54. * 等待,阻塞(等待超时)
    55. */
    56. public static void test4() throws InterruptedException {
    57. ArrayBlockingQueue blockingQueue = new ArrayBlockingQueue<>(3);
    58. blockingQueue.offer("a");
    59. blockingQueue.offer("b");
    60. blockingQueue.offer("c");
    61. blockingQueue.offer("d",2, TimeUnit.SECONDS); //超时时间和单位
    62. System.out.println(blockingQueue.poll());
    63. System.out.println(blockingQueue.poll());
    64. System.out.println(blockingQueue.poll());
    65. System.out.println(blockingQueue.poll(2, TimeUnit.SECONDS));
    66. }
    67. }

     

    同步队列SynchronousQueue

    没有容量,进去一个元素必须等待取出来之后,才能再往里面放一个元素。

    1. /**
    2. * 同步队列
    3. */
    4. public class SynchronousQueueDemo {
    5. public static void main(String[] args) {
    6. SynchronousQueue blockingQueue = new SynchronousQueue<>();//同步队列
    7. new Thread(()->{
    8. try {
    9. System.out.println(Thread.currentThread().getName()+"put 1");
    10. blockingQueue.put("1");
    11. System.out.println(Thread.currentThread().getName()+"put 2");
    12. blockingQueue.put("2");
    13. System.out.println(Thread.currentThread().getName()+"put 3");
    14. blockingQueue.put("3");
    15. } catch (InterruptedException e) {
    16. throw new RuntimeException(e);
    17. }
    18. },"t1").start();
    19. new Thread(()->{
    20. try {
    21. TimeUnit.SECONDS.sleep(3);
    22. System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
    23. TimeUnit.SECONDS.sleep(3);
    24. System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
    25. TimeUnit.SECONDS.sleep(3);
    26. System.out.println(Thread.currentThread().getName()+"=>"+blockingQueue.take());
    27. } catch (InterruptedException e) {
    28. throw new RuntimeException(e);
    29. }
    30. },"t2").start();
    31. }
    32. }

    线程池(重点)

    线程池:三大方法,7大参数,4中拒绝策略。

    池化技术:事先准备好资源,来人就用,用完放回。

    程序的运行,本质:占用系统的资源!优化资源的使用!=>池化技术

    线程池,连接池,内存池,对象池。

    线程池的好处:

    1、降低资源的消耗

    2、提高响应的速度

    3、方便管理。

    线程复用、可以控制最大并发数、管理线程

    线程的三大方法

    1. //Executors 工具类、三大方法。
    2. public class Demo01 {
    3. public static void main(String[] args) {
    4. // ExecutorService threadPoll = Executors.newSingleThreadExecutor();//单个线程
    5. // ExecutorService threadPoll =Executors.newFixedThreadPool(5); //创建一个固定线程池的大小
    6. ExecutorService threadPoll =Executors.newCachedThreadPool(); //可伸缩
    7. try {
    8. for(int i=0;i<10;i++){
    9. //使用了线程池后,使用线程池创建线程
    10. threadPoll.execute(()->{
    11. System.out.println(Thread.currentThread().getName()+" OK");
    12. });
    13. }
    14. } catch (Exception e) {
    15. throw new RuntimeException(e);
    16. }finally {
    17. //线程池用完,程序结束,关闭线程池
    18. threadPoll.shutdown();
    19. }
    20. }
    21. }

    7大参数

    源码分析

    第一个方法的源码

    1. public static ExecutorService newSingleThreadExecutor() {
    2. return new FinalizableDelegatedExecutorService
    3. (new ThreadPoolExecutor(1, 1,
    4. 0L, TimeUnit.MILLISECONDS,
    5. new LinkedBlockingQueue()));
    6. }

     

    第二个方法的源码 

    1. public static ExecutorService newFixedThreadPool(int nThreads) {
    2. return new ThreadPoolExecutor(nThreads, nThreads,
    3. 0L, TimeUnit.MILLISECONDS,
    4. new LinkedBlockingQueue());
    5. }

    第三个方法的源码 

    1. public static ExecutorService newCachedThreadPool() {
    2. return new ThreadPoolExecutor(0, Integer.MAX_VALUE,
    3. 60L, TimeUnit.SECONDS,
    4. new SynchronousQueue());
    5. }

    可以看见三种开启方法都是用的ThreadPoolExecutor,它的源码如下

     可以看见有7个参数

    1.核心线程池大小2.最大核心线程池大小3.存活时间,4.超时单位5.阻塞队列6.线程工厂,用于创建线程7.拒绝策略。

    1. public ThreadPoolExecutor(int corePoolSize,
    2. int maximumPoolSize,
    3. long keepAliveTime,
    4. TimeUnit unit,
    5. BlockingQueue workQueue,
    6. ThreadFactory threadFactory,
    7. RejectedExecutionHandler handler) {
    8. if (corePoolSize < 0 ||
    9. maximumPoolSize <= 0 ||
    10. maximumPoolSize < corePoolSize ||
    11. keepAliveTime < 0)
    12. throw new IllegalArgumentException();
    13. if (workQueue == null || threadFactory == null || handler == null)
    14. throw new NullPointerException();
    15. this.acc = System.getSecurityManager() == null ?
    16. null :
    17. AccessController.getContext();
    18. this.corePoolSize = corePoolSize;
    19. this.maximumPoolSize = maximumPoolSize;
    20. this.workQueue = workQueue;
    21. this.keepAliveTime = unit.toNanos(keepAliveTime);
    22. this.threadFactory = threadFactory;
    23. this.handler = handler;
    24. }

    前面三个方法的前两个参数分别是1,1  5,5   0,Inter.MaxValue。(21亿大小)

    因此阿里巴巴的手册里面才会这样写。

    四大策略 

    核心线程池大小为2,最大为5,一开始只有2个,但是阻塞队列里面满了之后又来人了会开放剩下三个,又慢了之后就不给进了,这就是拒绝策略。

    等到了那三个队列空闲后后,经过了超时时间就会关闭释放。

    四个实现类对应四大策略 

     自定义线程池、

    拒绝策略

    会抛出异常.

    1. //Executors 工具类、三大方法。
    2. public class Demo01 {
    3. public static void main(String[] args) {
    4. ExecutorService threadPoll =new ThreadPoolExecutor(
    5. 2,
    6. 5,
    7. 3,
    8. TimeUnit.SECONDS,
    9. new LinkedBlockingQueue<>(3),
    10. Executors.defaultThreadFactory(), //一般不变
    11. new ThreadPoolExecutor.AbortPolicy()); //该拒绝策略是银行满了,还有人进来时就不处理该人并抛出异常。
    12. try {
    13. for(int i=0;i<10;i++){
    14. //使用了线程池后,使用线程池创建线程
    15. threadPoll.execute(()->{
    16. System.out.println(Thread.currentThread().getName()+" OK");
    17. });
    18. }
    19. } catch (Exception e) {
    20. throw new RuntimeException(e);
    21. }finally {
    22. //线程池用完,程序结束,关闭线程池
    23. threadPoll.shutdown();
    24. }
    25. }
    26. }

    第二策略

    哪来的回哪里去,由原本的线程来执行。

    1. //Executors 工具类、三大方法。
    2. public class Demo01 {
    3. public static void main(String[] args) {
    4. ExecutorService threadPoll =new ThreadPoolExecutor(
    5. 2,
    6. 5,
    7. 3,
    8. TimeUnit.SECONDS,
    9. new LinkedBlockingQueue<>(3),
    10. Executors.defaultThreadFactory(), //一般不变
    11. new ThreadPoolExecutor.CallerRunsPolicy()); //哪来的去哪里!
    12. try {
    13. for(int i=0;i<10;i++){
    14. //使用了线程池后,使用线程池创建线程
    15. threadPoll.execute(()->{
    16. System.out.println(Thread.currentThread().getName()+" OK");
    17. });
    18. }
    19. } catch (Exception e) {
    20. throw new RuntimeException(e);
    21. }finally {
    22. //线程池用完,程序结束,关闭线程池
    23. threadPoll.shutdown();
    24. }
    25. }
    26. }

    第三策略

    队列满了不会抛出异常。会直接丢掉任务。

    1. package com.yhy.pool;
    2. import java.util.concurrent.*;
    3. /**
    4. * new ThreadPoolExecutor.AbortPolicy()); //该拒绝策略是银行满了,还有人进来时就不处理该人并抛出异常。
    5. * new ThreadPoolExecutor.CallerRunsPolicy()); //哪来的去哪里!
    6. * new ThreadPoolExecutor.DiscardPolicy()); //队列满了就踢了并且不抛出异常。
    7. * new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!
    8. */
    9. public class Demo01 {
    10. public static void main(String[] args) {
    11. ExecutorService threadPoll =new ThreadPoolExecutor(
    12. 2,
    13. 5,
    14. 3,
    15. TimeUnit.SECONDS,
    16. new LinkedBlockingQueue<>(3),
    17. Executors.defaultThreadFactory(), //一般不变
    18. new ThreadPoolExecutor.DiscardPolicy());
    19. try {
    20. for(int i=0;i<10;i++){
    21. //使用了线程池后,使用线程池创建线程
    22. threadPoll.execute(()->{
    23. System.out.println(Thread.currentThread().getName()+" OK");
    24. });
    25. }
    26. } catch (Exception e) {
    27. throw new RuntimeException(e);
    28. }finally {
    29. //线程池用完,程序结束,关闭线程池
    30. threadPoll.shutdown();
    31. }
    32. }
    33. }

    可以看见只有8条输出,有两条被踢了。

    第四策略

    1. //Executors 工具类、三大方法。
    2. public class Demo01 {
    3. public static void main(String[] args) {
    4. ExecutorService threadPoll =new ThreadPoolExecutor(
    5. 2,
    6. 5,
    7. 3,
    8. TimeUnit.SECONDS,
    9. new LinkedBlockingQueue<>(3),
    10. Executors.defaultThreadFactory(), //一般不变
    11. new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!
    12. try {
    13. for(int i=0;i<10;i++){
    14. //使用了线程池后,使用线程池创建线程
    15. threadPoll.execute(()->{
    16. System.out.println(Thread.currentThread().getName()+" OK");
    17. });
    18. }
    19. } catch (Exception e) {
    20. throw new RuntimeException(e);
    21. }finally {
    22. //线程池用完,程序结束,关闭线程池
    23. threadPoll.shutdown();
    24. }
    25. }
    26. }

      

    CPU密集型,IO密集型(扩展)

    经常会被问,池的最大大小如何去设置。

    CPU密集型: 12核的CPU最多12条线程同时执行,多少核就

    IO密集型:       程序 15个大型任务 IO十分占用资源 ,一般设置为两倍30个线程。

    1. /**
    2. * new ThreadPoolExecutor.AbortPolicy()); //该拒绝策略是银行满了,还有人进来时就不处理该人并抛出异常。
    3. * new ThreadPoolExecutor.CallerRunsPolicy()); //哪来的去哪里!
    4. * new ThreadPoolExecutor.DiscardPolicy()); //队列满了就踢了并且不抛出异常。
    5. * new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!
    6. */
    7. public class Demo01 {
    8. public static void main(String[] args) {
    9. //自定义线程池! 工作 ThreadPoolExecutor
    10. //最大线程池如何定义
    11. //1、CPU 密集型,几核,就是几,可以保CPu的效率最高!
    12. //2、IO密集型
    13. // 程序 15个大型任务 IO十分占用资源
    14. //获取CPU核数
    15. System.out.println(Runtime.getRuntime().availableProcessors());
    16. ExecutorService threadPoll =new ThreadPoolExecutor(
    17. 2,
    18. Runtime.getRuntime().availableProcessors(),
    19. 3,
    20. TimeUnit.SECONDS,
    21. new LinkedBlockingQueue<>(3),
    22. Executors.defaultThreadFactory(), //一般不变
    23. new ThreadPoolExecutor.DiscardOldestPolicy()); //队列满了不会抛出异常,尝试去和最早的竞争,也不会抛出异常!
    24. try {
    25. for(int i=0;i<10;i++){
    26. //使用了线程池后,使用线程池创建线程
    27. threadPoll.execute(()->{
    28. System.out.println(Thread.currentThread().getName()+" OK");
    29. });
    30. }
    31. } catch (Exception e) {
    32. throw new RuntimeException(e);
    33. }finally {
    34. //线程池用完,程序结束,关闭线程池
    35. threadPoll.shutdown();
    36. }
    37. }
    38. }

  • 相关阅读:
    Rust 实战丨绘制曼德博集
    【SpringBoot教程】配置文件详细总结
    App测试中ios和Android有哪些区别呢?
    计算机网络-应用层(1)
    android_root后的玩机:magisk模块&root隐藏
    军队状态出现的六种结果,是将帅的过失
    Anaconda虚拟环境配置Python库与Spyder编译器
    Spring Cloud之API网关(Gateway)
    SpringBoot 请求参数解析全过程
    CDN网络基础入门:CDN原理及架构
  • 原文地址:https://blog.csdn.net/m0_62327332/article/details/133215172