Fork me on GitHub

多线程常用工具类

countDownLatch

简介

  • countDownLatch这个类使一个线程等待其他线程各自执行完毕后再执行。
  • 是通过一个计数器来实现的,计数器的初始值是线程的数量。每当一个线程执行完毕后,计数器的值就-1,当计数器的值为0时,表示所有线程都执行完毕,然后在闭锁上等待的线程就可以恢复工作了。

代码

//初始化count数为2
    final CountDownLatch latch=new CountDownLatch(2);

    public void latchDemo(){
        System.out.println("-----------主线程执行开始--------");
        //创建一个线程
        Thread t1=new Thread(()->{
            try {
                System.out.println("------t1线程执行开始-----");
                TimeUnit.SECONDS.sleep(2);
                System.out.println("------t1线程执行结束-----");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //count数减1
            latch.countDown();
        });

        //创建第二个线程
        Thread t2=new Thread(()->{
            try {
                System.out.println("------t2线程执行开始-----");
                TimeUnit.SECONDS.sleep(2);
                System.out.println("------t2线程执行结束-----");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //count数减1
            latch.countDown();
        });
        //开启线程
        t1.start();
        t2.start();
        //等待count数变为0,再继续执行主线程
        try {
            latch.await();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("-----------主线程执行结束--------");
    }

CyclicBarrier(栅栏)

简介

  • 让所有线程都等待完成后才会继续下一步行动。

例子,就像生活中我们会约朋友们到某个餐厅一起吃饭,有些朋友可能会早到,有些朋友可能会晚到,但是这个餐厅规定必须等到所有人到齐之后才会让我们进去。这里的朋友们就是各个线程,餐厅就是CyclicBarrier。

代码


    private CyclicBarrier barrier;

    public CyclicBarrierDemo(CyclicBarrier barrier) {
        this.barrier = barrier;
    }


    /**
     * @Description: 实例场景
     * @Author: zhuyang 
     * @Date: 2022/1/12 18:36
     * @return: void
     **/
    public void example(){
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("线程: "+Thread.currentThread().getName()+"  到达栅栏A");
            barrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("线程: "+Thread.currentThread().getName()+"  到达栅栏B");
            barrier.await();
        } catch (Exception e) {
            e.printStackTrace();
        }

    }

    public static void demo(){
        //需要的线程数量
        int sum=5;
        CyclicBarrierDemo barrierDemo=new CyclicBarrierDemo(new CyclicBarrier(sum,()->{
            System.out.println(Thread.currentThread().getName() + " 完成最后任务");
        }));

        //循环开启线程
        for (int i = 0; i < 5; i++) {
            new Thread(barrierDemo::example,"线程"+i).start();
        }

        //主线程开始睡眠
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Exchanger

简介

Exchanger 是 JDK 1.5 开始提供的一个用于两个工作线程之间交换数据的封装工具类,简单说就是一个线程在完成一定的事务后想与另一个线程交换数据,则第一个先拿出数据的线程会一直等待第二个线程,直到第二个线程拿着数据到来时才能彼此交换对应数据。

代码

private static Exchanger<String> exchanger = new Exchanger<>();

    public void method(){
        //开启线程1
        new Thread(()->{
            for (int i = 1; i < 5; i++) {
                System.out.println(Thread.currentThread().getName()+"交换前:"+i);
                String exchange ="";
                try {
                    exchange =exchanger.exchange(i+"");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                System.out.println(Thread.currentThread().getName()+"交换后:"+exchange);
            }
        }).start();

        //开启线程2
        new Thread(()->{
            for (;;){
                    String data="0";
                    System.out.println(Thread.currentThread().getName()+"交换前:"+data);
                    String exchange ="";
                    try {
                        exchange =exchanger.exchange(data);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println(Thread.currentThread().getName()+"交换后:"+exchange);
            }
        }).start();
    }

    public void demo(){
        //调用方法
        method();
        try {
            TimeUnit.SECONDS.sleep(5);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //所有程序(方法,类等)停止,系统停止运行
        System.exit(-1);
    }

Phaser(阶段同步器)

简介

Phaser提供了动态增减parties计数,这点比CyclicBarrier类操作parties更加方便,通过若干个方法来控制多个线程之间同步运行的结果,还可以实现针对某一个线程取消同步运行的效果,而且支持屏障处等待,在等待时还支持中断或非中断等功能,使用Java并发类对线程进行分组同步控制时,Phaser比CyclicBarrier功能更加强大,推荐使用。

  • arriveAndAwaitAdvance()
    当前线程已经到达屏障,在此等待一段时间,等条件满足后继续向下一个屏障继续执行。
  • arriveAndDeregister()
    当前线程退出,并且使parties值减1。

代码

//产生一个随机数
    public static   Random random=new Random();

    static MarriagePhaser phaser = new MarriagePhaser();

     public static void milliSleep(int m){
         try {
             TimeUnit.MILLISECONDS.sleep(m);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }

     public void demo(){
         //设置阻塞队列个数
         phaser.bulkRegister(5);

         //进行开启线程

         for (int i = 0; i < 5; i++) {
             int nameIndex=i;
             new Thread(()->{
                 //创建用户
                 Person person=new Person("person"+nameIndex);
                 //安排动作
                 person.arrive();
                 //进行阻塞
                 phaser.arriveAndAwaitAdvance();
                 //安排动作
                 person.eat();
                 //进行阻塞
                 phaser.arriveAndAwaitAdvance();

                 //安排动作
                 person.leave();
                 //进行阻塞
                 phaser.arriveAndAwaitAdvance();

             }).start();
         }

         try {
             TimeUnit.SECONDS.sleep(5);
         } catch (InterruptedException e) {
             e.printStackTrace();
         }
     }

     public static class MarriagePhaser extends Phaser{
         @Override
         protected boolean onAdvance(int phase, int registeredParties) {
             switch (phase){
                 case 0:
                     System.out.println("所有人到齐了");
                     return false;
                 case 1:
                     System.out.println("所有人吃完了");
                     return false;
                 case 2:
                     System.out.println("所有人离开了");
                     System.out.println("婚礼结束!");
                     return true;
                 default:
                     return true;
             }
         }
     }

     /**
      * @Description: 创建实体
      * @Author: zhuyang
      * @Date: 2022/1/13 9:43
      * @return: null
      **/
     public static class Person{

         public String name;

         public Person(String name) {
             this.name = name;
         }

         public void arrive(){
             milliSleep(random.nextInt(1000));
             System.out.printf("%s 到达现场\n",name);
         }

         public void eat(){
             milliSleep(random.nextInt(1000));
             System.out.printf("%s 吃完了\n",name);
         }

         public void leave(){
             milliSleep(random.nextInt(1000));
             System.out.printf("%s 离开了\n",name);
         }

     }

ReentrantLock(公平锁)

简介

ReentrantLock是独占锁,加锁和解锁的过程需要手动进行,可以响应中断,可以实现公平锁机制。

代码

//创建一个公平锁 默认为非公平锁
    ReentrantLock lock=new ReentrantLock(true);


    public void reentrant(){
        System.out.println("当前线程名为----->"+Thread.currentThread().getName());
        System.out.println("--------------------------------------------------------------------");
        lock.lock();
        try {
            TimeUnit.SECONDS.sleep(1);
            System.out.println("当前线程名为: "+Thread.currentThread().getName());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            lock.unlock();
        }
    }



    public void demo(){
        ReentrantLockDemo lockDemo=new ReentrantLockDemo();
        new Thread(lockDemo::reentrant, "t1").start();
        new Thread(lockDemo::reentrant, "t2").start();
        new Thread(lockDemo::reentrant, "t3").start();
        new Thread(lockDemo::reentrant, "t4").start();
        new Thread(lockDemo::reentrant, "t5").start();
        try {
            TimeUnit.SECONDS.sleep(6);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

Semaphore(信号量(n个线程的限流))

简介

维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,实现一个文件允许的并发访问数。

代码

//初始化
    Semaphore semaphore=new Semaphore(2);

    public void createThread(){
        Thread t1=new Thread(()->{
            //获取一个许可
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始运行");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:"+Thread.currentThread().getName()+" 运行结束");
            semaphore.release();
        });
        Thread t2=new Thread(()->{
            try {
                semaphore.acquire();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:"+Thread.currentThread().getName()+" 开始运行");
            try {
                TimeUnit.SECONDS.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println("当前线程名:"+Thread.currentThread().getName()+" 运行结束");
            semaphore.release();
        });
        t1.start();
        t2.start();
        try {
            TimeUnit.SECONDS.sleep(3);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

ThreadLocal

简介

ThreadLocal是JDK包提供的,它提供线程本地变量,如果创建一乐ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本,在实际多线程操作的时候,操作的是自己本地内存中的变量,从而规避了线程安全问题。

原理

往ThreadLocal里面设置值时,其实是往ThreadLocalMap里面设置值,key值为当前线程的hash值,value为当前设置的对象值,取也同理,用当前线程作为key,取其value。

代码

//创建ThreadLocal对象
       ThreadLocal<Person> threadLocal=new ThreadLocal<>();
       Person person=new Person();
       public void threadLocal(){
           new Thread(()->{
               try {
                   TimeUnit.SECONDS.sleep(2);
                   threadLocal.set(new Person());
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+threadLocal.get());
           },"t1").start();

           new Thread(()->{
               //更改Person中的name值
               threadLocal.set(new Person());
               try {
                   TimeUnit.SECONDS.sleep(2);
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
               System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+threadLocal.get());
           },"t2").start();


       }

    public void ordinary(){
        new Thread(()->{
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+person.name);
        },"t3").start();

        new Thread(()->{
            //更改Person中的name值
            person.name="lisi";
            try {
                TimeUnit.SECONDS.sleep(2);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            System.out.println(Thread.currentThread().getName()+"输出对象name值为: "+person.name);
        },"t4").start();


    }

    /**
     * t1输出对象name值为: null
     * t3输出对象name值为: lisi
     * t2输出对象name值为: com.yxkj.juc.c_000.ThreadLocalDemo$Person@3d8f1de9
     * t4输出对象name值为: lisi
     **/
    public void demo(){
        threadLocal();

        ordinary();
    }




    public static class Person{
           public String name="zhangsan";
    }

Gitee地址

https://gitee.com/zhuayng/foundation-study/tree/develop/JavaBasis/JUC/src/main/java/com/yxkj/juc/c_000

posted @ 2020-03-10 13:40  晨度  阅读(341)  评论(0编辑  收藏  举报