JUC 粗汇总

JUC是什么

juc是java.util.concurrent包的缩写。

JUC包括什么

那么我们简要概括一下JUC的内容包括哪些呢?

1.LOCK框架、TOOLS类

2.Collections 集合。

3.Atomic 原子类。

4.Executors 线程池

1.TOOlS类解析

CountDownLatch

俗称闭锁。类似加强版的Join。作用是:让一组线程等待其他线程完成工作之后在进行工作。

举一个例子:Springboot启动类在启动的时候,主线程是需要环境线程初始化完之后才开始的。按照我的理解就是启动服务框架的时候,控制台开始打印哪些信息,但是要是其中你一个环境变量出了问题。按照经验启动服务框架就失败了。所以主线程是等待环境线程初始化完之后才开始工作的。这个过程可以利用CountDownLatch实现。

如何使用:(以下内容借鉴网上大佬博客)

1.首先在使用CountDownLatch的时候需要传入一个int值用于初始化。然后这个int扣减完之后,等待的线程就被唤醒了。如以下代码

/**
     * Constructs a {@code CountDownLatch} initialized with the given count.
     *
     * @param count the number of times {@link #countDown} must be invoked
     *        before threads can pass through {@link #await}
     * @throws IllegalArgumentException if {@code count} is negative
     */
    public CountDownLatch(int count) {
        if (count < 0) throw new IllegalArgumentException("count < 0");
        this.sync = new Sync(count);
    }

观察以下图

1.CountDownLatch(int)初始化方法

2.await()等待方法,带参数的是超时重载方法。

3.countDown()对初始化的int参数进行减1的操作。

4.getCount()获取当前值。

5.sync的话是一个内部类,以上所介绍的方法都是为了这个服务的。

按照网上大佬的代码复现了一下。

package JUC.TOOLS.CountDonwLatch;

import java.util.concurrent.TimeUnit;

public class SleepTools {
    public static final void second(int second){
        try {
            TimeUnit.SECONDS.sleep(second);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }

    public static final void ms(int ms){
        try{
            TimeUnit.MILLISECONDS.sleep(ms);
        }catch (InterruptedException e){
            e.printStackTrace();
        }
    }
}
package JUC.TOOLS.CountDonwLatch;

import java.util.concurrent.CountDownLatch;

public class UseCountDownLatch {
    static CountDownLatch countDownLatch = new CountDownLatch(6);

    /**
     * 初始化线程
     */
    private static class InitThread implements Runnable {
        @Override
        public void run() {
            System.out.println("初始化thread_" + Thread.currentThread().getId() + "ready init work ...");
            countDownLatch.countDown();
            for (int i = 0; i < 2; i++) {
                System.out.println("循环初始化thread_" + Thread.currentThread().getId() + ".....continue do its work");
            }
        }
    }

    /**
     * 业务线程:等初始化线程初始化之后再执行。
     */
    private static class BusiThread implements Runnable {

        @Override
        public void run() {
            try {
                countDownLatch.await();
                for (int i = 0; i < 3; i++) {
                    System.out.println("蔡拯雨BusiThread " + Thread.currentThread().getId() + " do busiThread------");
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    public static void main(String[] args) {
        new Thread() {
            @Override
            public void run() {
                SleepTools.ms(1);
                System.out.println("thread_" + Thread.currentThread().getId() + " ready init work step 1st.....");
                countDownLatch.countDown();
                System.out.println("begin stop 2nd.....");
                SleepTools.ms(1);
                System.out.println("thread_" + Thread.currentThread().getId() + " ready init work step 2nd.....");
                countDownLatch.countDown();
            }
        }.start();

        new Thread(new BusiThread()).start();
        for (int i=0 ;i<=3;i++){
            new Thread(new InitThread()).start();
        }
        try{
            countDownLatch.await();
            System.out.println("Main do ites work.....");
        }catch (InterruptedException e){
            e.printStackTrace();
        }

    }
}

效果如图

能够很清楚的看到,虽然我业务线程先启动,但是在CountDownLatch的干涉下,其实业务线程还是在初始化线程结束之后才开始工作的。

CyclicBarrier

江湖俗称栅栏锁。意思是作用于一组线程。只有这一组线程里面的所有线程都到达栅栏锁的面前,这一组线程才能继续通行,少一个也不行。举一个例子:其实好比你在放羊,当所有的羊到宿舍门口的时候,宿舍门才能打开。少一只所有羊都不能进去睡觉。

我们来看一下他的两个构造函数。

很明显能看出是第一个调用第二个。那么我们来看一下它的参数:parties是你这一组线程的数量,不能多不能少,必须是刚刚好等于你这一组线程的数量。barrierAction是一个线程引用。这个线程的作用是,当你之前设置的一组线程全部达到这个屏障的时候,屏障打开,barrierAction开始工作。

/**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and
     * does not perform a predefined action when the barrier is tripped.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties) {
        this(parties, null);
    }
/**
     * Creates a new {@code CyclicBarrier} that will trip when the
     * given number of parties (threads) are waiting upon it, and which
     * will execute the given barrier action when the barrier is tripped,
     * performed by the last thread entering the barrier.
     *
     * @param parties the number of threads that must invoke {@link #await}
     *        before the barrier is tripped
     * @param barrierAction the command to execute when the barrier is
     *        tripped, or {@code null} if there is no action
     * @throws IllegalArgumentException if {@code parties} is less than 1
     */
    public CyclicBarrier(int parties, Runnable barrierAction) {
        if (parties <= 0) throw new IllegalArgumentException();
        this.parties = parties;
        this.count = parties;
        this.barrierCommand = barrierAction;
    }

实践一下。

package JUC.TOOLS.CyclicBarrier;

import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class UseCyclicBarrier {

    static CyclicBarrier cyclicBarrier = new CyclicBarrier(5,new SoutThread());

    private static class WorkThread implements Runnable{

        @Override
        public void run() {
            try{
                System.out.println("蔡拯雨thread_"+Thread.currentThread().getId()+"wait");
                cyclicBarrier.await();
                //Thread.sleep(2000);
                System.out.println("蔡拯雨thread_"+Thread.currentThread().getId()+"work");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (BrokenBarrierException e) {
                e.printStackTrace();
            }
        }
    }

    private static class SoutThread implements Runnable{

        @Override
        public void run() {
            System.out.println("开始开始开始");
        }
    }

    public static void main(String[] args) {
        for (int i=0;i<=4;i++){
            Thread thread = new Thread(new WorkThread());
            thread.start();
        }
    }
}

看一下效果:五个线程都等待了(到达屏障),初始化设置的barrierAction线程开始工作。

 

Semaphore

江湖俗称信号量。和操作系统中的功能一致,都是用来控制对某一资源访问的数量。比如控制数据库的连接数量。

一般适用semaphore的时候要设置两个。为什么呢?想想操作系统里面针对信号量的pv操作。

看一下构造函数。

public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    /**
     * Creates a {@code Semaphore} with the given number of
     * permits and the given fairness setting.
     *
     * @param permits the initial number of permits available.
     *        This value may be negative, in which case releases
     *        must occur before any acquires will be granted.
     * @param fair {@code true} if this semaphore will guarantee
     *        first-in first-out granting of permits under contention,
     *        else {@code false}
     */
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

没啥区别,只是默认的是非公平锁实现,也可以指定公平锁实现。

利用这个我们来实践一下。

package JUC.TOOLS.Semaphore;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;

public class UseSemaphore {

    static Semaphore s1,s2;

    //连接池
    static LinkedList<Integer> Pool = new LinkedList<>();

    //初始化连接池
    static{
        s1 = new Semaphore(10);
        s2 = new Semaphore(0);
        for (int i=0;i<10;i++){
            Pool.add(1);
        }
    }

    //连接数据库资源
    public void connect() throws InterruptedException {
        s1.acquire();
        synchronized (Pool){
            System.out.println(Pool.removeFirst());
        }
        s2.release();
    }

    //释放连接
    public void releaseConnect() throws InterruptedException {
        s2.acquire();
        synchronized (Pool){
            System.out.println(Pool.add(1));
        }
        s1.release();
    }

}

Exchanger

交换器,可以交换两个线程之间的数据。下面试一下三个线程会发生什么。

package JUC.TOOLS.Exchanger;

import com.sun.org.slf4j.internal.Logger;
import com.sun.org.slf4j.internal.LoggerFactory;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Exchanger;

public class UseExchanger {

    private static Exchanger<List<Integer>> exchanger = new Exchanger<>();
    Logger logger = LoggerFactory.getLogger(UseExchanger.class);
    public static void main(String[] args) {
        new Thread(){
            @Override
            public void run(){
                List<Integer> list = new ArrayList<>();
                list.add(1);
                list.add(2);
                try {
                    List<Integer> list1 = exchanger.exchange(list);
                    for (int i=0 ;i<list1.size();i++){
                        System.out.println("111:"+list1.get(i));
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();

        new Thread(){
            @Override
            public void run(){
                List<Integer> list = new ArrayList<>();
                list.add(3);
                list.add(4);
                try {
                    List<Integer> list1 = exchanger.exchange(list);
                    for (int i=0 ;i<list1.size();i++){
                        System.out.println("222:"+list1.get(i));
                    }
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }.start();

//        new Thread(){
//            @Override
//            public void run(){
//                List<Integer> list = new ArrayList<>();
//                list.add(5);
//                list.add(6);
//                try {
//                    List<Integer> list1 = exchanger.exchange(list);
//                    for (int i=0 ;i<list1.size();i++){
//                        System.out.println("333:"+list1.get(i));
//                    }
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                }
//
//            }
//        }.start();
    }
}

这个是两个线程的效果。但是你加入第三个之后会发现程序不会报错,但是线程无法结束。这是为什么有大佬知道请留言谢谢

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值