zoukankan      html  css  js  c++  java
  • 并发工具和线程池

    1.并发工具类


    1.CountDownLatch:可以实现线程计数,阻塞后续线程


    CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。
    countDown()实现计数器-1
    await()等待拦截方法,等待计数器为0时再放行,否则则一直阻塞
    getCount()获取当前计数器中计数数量

    案例:
    /**
    * 等待子线程全部执行完毕之后再执行主线程内容
    * @param args
    */
    private static CountDownLatch countDownLatch=new CountDownLatch(2);
    public static void main(String[] args) throws InterruptedException {
    System.out.println("======主线程开始运行=====");
    //第一个子线程
    new Thread(()->{
    System.out.println("子线程"+Thread.currentThread().getName()+"开始运行~");
    //线程数量-1操作,通知该线程运行完毕
    countDownLatch.countDown();
    }).start();

    //第二个子线程
    new Thread(()->{
    System.out.println("子线程"+Thread.currentThread().getName()+"开始运行~");
    //线程数量-1操作,通知该线程运行完毕
    countDownLatch.countDown();
    }).start();

    //等待,等待计数器中线程计数为0时才继续向下执行
    countDownLatch.await();
    System.out.println("子线程执行完毕,主线程继续执行");

    //获取当前计数线程数量
    /*while (true){
    if(countDownLatch.getCount()==0){
    System.out.println("子线程执行完毕,主线程继续执行");
    break;
    }
    }*/
    }


    2.CyclicBarrier:类似于栅栏,进行拦截,等待所有线程都准备,然后统一放行,阻塞当前线程


    //设置等待线程数量,当线程数量到达指定数量时,统一向下运行
    private static CyclicBarrier cyclicBarrier=new CyclicBarrier(10);
    public static void main(String[] args) {
    //创建10个线程
    for (int i = 1; i <=10 ; i++) {
    new Thread(()->{
    try {
    Thread.sleep(100);
    System.out.println(Thread.currentThread().getName()+"准备就绪");
    //等待
    cyclicBarrier.await();
    } catch (InterruptedException | BrokenBarrierException e) {
    e.printStackTrace();
    }
    System.out.println(Thread.currentThread().getName()+"开始比赛~");
    }).start();
    }
    }

    3.Semaphore:可以做资源控制,容器中有几个资源,那么线程执行时先申请资源,资源如果可用则继续执行,如果资源不可用则阻塞等待


    当资源占用完毕之后将该资源释放,其他线程排队占用

    private static Semaphore semaphore=new Semaphore(3);
    public static void main(String[] args) {
    for (int i = 1; i <=10 ; i++) {
    new Thread(()->{
    try {
    //申请资源,发生阻塞
    System.out.println(Thread.currentThread().getName()+"申请茅坑~");
    semaphore.acquire();
    System.out.println(Thread.currentThread().getName()+"可以安心上厕所了");
    //模拟上厕所时间
    Thread.sleep(1000);
    System.out.println(Thread.currentThread().getName()+"舒服,上完了~");
    //释放资源
    semaphore.release();
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }).start();
    }
    }


    4.Exchanger:可以执行线程的资源交换,线程数量必须为偶数,因为是两两相互交换资源,如果不是偶数默认情况下导致阻塞,可以设置交换资源超时时间


    public class ExchangerDemo {
    private static String str1="资源1";
    private static String str2="资源2";
    //构建资源交换对象
    private static Exchanger<String> stringExchanger=new Exchanger<>();
    public static void main(String[] args) {
    //第一个线程
    new Thread(()->{
    System.out.println(Thread.currentThread().getName()+"初始占用资源:"+str1);
    //资源交换,将资源交给其他线程和获取到其他线程交换过来的资源
    try {
    String newStr = stringExchanger.exchange(str1);
    System.out.println(Thread.currentThread().getName()+"交换资源:"+newStr);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }).start();

    //第二个线程
    new Thread(()->{
    System.out.println(Thread.currentThread().getName()+"初始占用资源:"+str2);
    //资源交换,将资源交给其他线程和获取到其他线程交换过来的资源
    try {
    String newStr = stringExchanger.exchange(str2);
    System.out.println(Thread.currentThread().getName()+"交换资源:"+newStr);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    }).start();

    //第三个线程
    new Thread(()->{
    System.out.println(Thread.currentThread().getName()+"初始占用资源:"+str2);
    //资源交换,将资源交给其他线程和获取到其他线程交换过来的资源
    try {
    //设置资源交换超时时间
    String newStr = stringExchanger.exchange(str2,1000, TimeUnit.MILLISECONDS);
    System.out.println(Thread.currentThread().getName()+"交换资源:"+newStr);
    } catch (InterruptedException | TimeoutException e) {
    e.printStackTrace();
    }
    }).start();
    }
    }


    2.线程池


    类似于一个池子,可以存放/管理线程


    1.使用线程池的好处


    第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
    第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。
    第三:提高线程的可管理性


    2.如何使用线程池


    2.1线程池分类
    线程池顶级类ThreadPoolExecutor最终实现Executor接口,在JUC包下,通过Executors类可以创建不同类型的线程池,分类如下
    1.newScheduledThreadPool 定时任务线程池,可以设置任务时间
    2.newFixedThreadPool 定长线程池
    3.newSingleThreadExecutor 利用的是单线程,单线程处理任务,一般不用
    4.newCachedThreadPool 带缓存的线程池
    2.2 构建线程池
    1.newCachedThreadPool线程池,可缓存,可以重复利用
    //构建一个线程池,可以重复利用线程
    ExecutorService executorService = Executors.newCachedThreadPool();
    for (int i = 1; i <=10 ; i++) {
    //创建线程池
    executorService.execute(()->{
    System.out.println("创建线程池"+Thread.currentThread().getName());
    });
    }
    2.newFixedThreadPool创建一个固定线程数量的线程池
    //构建线程池对象
    ExecutorService executorService = Executors.newFixedThreadPool(3);
    for (int i = 0; i < 10; i++) {
    //创建线程
    executorService.execute(()->{
    System.out.println("创建线程:"+Thread.currentThread().getName());
    });
    }


    3.newScheduledThreadPool


    //创建一个线程池
    ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(3);
    for (int i = 0; i < 10; i++) {
    scheduledExecutorService.schedule(()->{
    System.out.println("创建线程:"+Thread.currentThread().getName());
    },1000, TimeUnit.MILLISECONDS);
    }


    4.newSingleThreadExecutor单线程池


    //创建一个线程池
    ExecutorService executorService = Executors.newSingleThreadExecutor();
    for (int i = 0; i < 10; i++) {
    executorService.execute(()->{
    System.out.println("创建线程:"+Thread.currentThread().getName());
    });
    }

    3.所有的线程池分类底层调用的都是ThreadPoolExecutor()构造方法,该构造方法中每一个参数含义


    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)

    1.corePoolSize代表核心线程池大小,当有任务时,会创建对应线程处理对应任务,当线程到达一定数量后,则会缓存到队列当中,不会再次创建新的线程
    2.maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程
    3.keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。
    4.unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性

    4.如何确定线程池中创建线程数量
    考虑CPU密集和IO密集,如果不考虑IO情况下,一般是处理器数量+1,如果考虑IO,则处理器*2

  • 相关阅读:
    poj 3068 Bridge Across Islands
    XidianOJ 1086 Flappy v8
    XidianOJ 1036 分配宝藏
    XidianOJ 1090 爬树的V8
    XidianOJ 1088 AK后的V8
    XidianOJ 1062 Black King Bar
    XidianOJ 1091 看Dota视频的V8
    XidianOJ 1098 突击数论前的xry111
    XidianOJ 1019 自然数的秘密
    XidianOJ 1109 Too Naive
  • 原文地址:https://www.cnblogs.com/ringqq/p/12524873.html
Copyright © 2011-2022 走看看