農農  

1. 并发工具类

  1.1 CountDownLatch

  CountDownLatch类位于java.util.concurrent包下,利用它可以实现类似计数器的功能。比如有一个任务A,它要等待其他4个任务执行完毕之后才能执行,此时就可以利用CountDownLatch来实现这种功能了。

  countDown()实现计数器-1

  await()等待拦截方法,等待计数器为0时再放行,否则则一直阻塞

  getCount()获取当前计数器中计数数量

  

  编写代码测试

//等待子线程全部执行完毕后在执行主线程
    public static void main(String[] args) {
        System.out.println("主线程开始运行");
        //第一个 子线程
        new Thread(()->{
            System.out.println("子线程:"+Thread.currentThread().getName()+"开始运行");
    }).start();
        //第二个 子线程
        new Thread(()->{
            System.out.println("子线程:"+Thread.currentThread().getName()+"开始运行");
        }).start();
        System.out.println("子线程执行完毕,主线程继续执行");
    }

    

  控制台效果

从下图可看出问题主线程先抢占了资源然后是第一个子线程,但是当第一个子线程运行完后紧接着又是主线程运行最后这才是

第二个子线程运行,并没有使所有子线程走完后再走主线程

 

 

 

  使用CountDownLatch解决方案

  编写代码测试

//等待子线程全部执行完毕后在执行主线程
    //监视几个线程就写几个  当前有两个线程所以写2
    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("子线程执行完毕,主线程继续执行");
    }

  

  控制台效果

从下图可看出是所以有子线程走完后再执行的主线程

 

 

 

  1.2 CyclicBarrier

  CyclicBarrier初始化时规定一个数目,然后计算调用了CyclicBarrier.await()进入等待的线程数。当线程数达到了这个数目时,

    所有进入等待状态的线程被唤醒并继续。 

   CyclicBarrier就象它名字的意思一样,可看成是个障碍, 所有的线程必须到齐后才能一起通过这个障碍。 

  CyclicBarrier初始时还可带一个Runnable的参数, Runnable任务在CyclicBarrier的数目达到后,

    所有其它线程被唤醒前被执行。

 

  编写测试代码

 //创建10个线程
        for (int i=1;i<=10;i++){
            new Thread(()->{
                try {
                    Thread.sleep(100);
                    System.out.println(Thread.currentThread().getName()+"准备就绪");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println("开始比赛");
            }).start();
        }
    }

  

  控制台效果

 

 

 

  使用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("开始比赛~");
            }).start();
        }
    }

 

   控制台效果

 

 

 

  1.3 Semaphore

    Semaphore是一种基于计数的信号量。它可以设定一个阈值,基于此,多个线程竞争获取许可信号,做自己的申请后归还,

  超过阈值后,线程申请许可信号将会被阻塞。Semaphore可以用来构建一些对象池,资源池之类的,比如数据库连接池,我们也

  可以创建计数为1的Semaphore,将其作为一种类似互斥锁的机制,这也叫二元信号量,表示两种互斥状态。

  

  编写测试代码

  一共有三个茅坑,每个人上完之后让下一个人上

for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                System.out.println("终于有茅坑了,可以上厕所了");
            }).start();
        }

 

  控制台效果

  如图是不可以的

 

 

 

  使用Semaphore解决方案

  编写测试代码

private static Semaphore semaphore=new Semaphore(3);
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            new Thread(() -> {
                if (semaphore.availablePermits()>0){
                    System.out.println(Thread.currentThread().getName()+"终于有茅坑了,可以上厕所了");
                }else{
                    System.out.println(Thread.currentThread().getName()+"没有茅坑了");
                }
                try {
                    //申请资源
                    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();
        }
    }

 

  控制台 效果

 

 

 

  1.4 Exchanger

    可以执行线程的资源交换,线程数量必须为偶数,因为是两两相互交换资源,如果不是偶数默认情况下导致阻塞,

  可以设置交换资源超时时间

  编写测试代码

private static String str1="资源一";
    private static String str2="资源二";

    //构建资源交换
    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. 线程池

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

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

  2.2 线程池的作用

      线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时

    间,从而提高效率。

      如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池

    中线程的开始、挂起、和中止

  2.3 线程池的分类及使用

      线程池顶级类ThreadPoolExecutor最终实现Executor接口,在JUC包下,通过Executors类可以创建不同类型的线程池,

    分类如下

      1.newScheduledThreadPool 定时任务线程池,可以设置任务时间    

 //构建 一个线程池
    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(24);
        for (int i = 0; i < 10; i++) {
            //创建线程
            executorService.execute(()->{
                System.out.println("创建线程:"+Thread.currentThread().getName());
            });
        }
        //线程池停止
        executorService.shutdown();

 

 


      3.newSingleThreadExecutor 利用的是单线程,单线程处理任务,一般不用

 

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

 

 

 


      4.newCachedThreadPool 带缓存的线程池

 

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

 

 

 

  3.所有的线程池分类底层调用的都是ThreadPoolExecutor()构造方法,该构造方法中每一个参数含义
    public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,BlockingQueue<Runnable> workQueue)

    3.1 corePoolSize代表核心线程池大小,当有任务时,会创建对应线程处理对应任务,当线程到达一定数量后,则会缓存到

      队列当中,不会再次创建新的线程

    3.2 maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程
    3.3 keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。
    3.4 unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性

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

 

    

posted on 2020-03-19 18:54  Baekhyunne  阅读(129)  评论(0编辑  收藏  举报