ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

并发编程(二)

2020-03-19 19:04:23  阅读:194  来源: 互联网

标签:Thread getName 编程 System 并发 线程 println out


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

 

    

标签:Thread,getName,编程,System,并发,线程,println,out
来源: https://www.cnblogs.com/szhhhh/p/12526487.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有