天天看点

JUC线程安全集合,辅助类与线程池一、集合线程安全问题二、多线程锁三、JUC辅助类四、线程池

文章目录

  • 一、集合线程安全问题
    • 1. ArrayList解决方案
        • CopyOnWriteArrayList原理
    • 2. HashMap解决方案
    • 3. HashSet解决方案
  • 二、多线程锁
    • 1. sync同步锁
    • 2. 公平锁和非公平锁
    • 3. 可重入锁
    • 4. 死锁
  • 三、JUC辅助类
    • 1. 减少计数 CountDownLatch
    • 2. 循环栅栏 CyclicBarrier
    • 3. 信号灯 Semaphore
  • 四、线程池
    • 1. 自定义线程池
        • 1. 4种拒绝策略
        • 2. 自定义线程池

提示:以下是本篇文章正文内容,下面案例可供参考

一、集合线程安全问题

几种常用的Java集合如ArrayList等,在插入数据时,均存在线程安全问题

1. ArrayList解决方案

  1. Vector:JDK1.0的解决方案

    List<String> list = new Vector<>();

  2. Collections中的同步链表方法

    List<String> list = Collections.synchronizedList(new ArrayList<>());

  3. List<String> list = new CopyOnWriteArrayList<>();

    比较常用的方法

CopyOnWriteArrayList原理

写时复制技术:读的时候支持并发读,写时支持独立写

在写入的时候先将原先的数据读出,然后修改数据,在其它线程读的时候直接读取写入的新内容

2. HashMap解决方案

  • ConcurrentHashMap

3. HashSet解决方案

  • 使用

    CopyOnWriteArraySet

    解决线程不安全问题

二、多线程锁

1. sync同步锁

  • 当sync加到方法上时,锁的是当前对象,也就是this,调用方法的那个对象
  • 当方法上同时加了

    static sync

    关键字时,这是锁的是Class对象了
  • 当代码块上加了sync,锁的是sync括号内配置的对象

2. 公平锁和非公平锁

  • 可重入锁默认为非公平锁
  • 非公平锁可能导致所有活都让一个线程干了,部分线程分不到活。 优点在于其执行的效率高
  • 公平锁效率相对低一点,每个线程都能分配到工作

3. 可重入锁

sync(隐式) Lock(显式)都是可重入锁

可以自由进出同步代码块,逐层进入

4. 死锁

什么是死锁 两个或两个以上的进程,因为争夺资源而造成的相互等待的现象

死锁的原因: 资源不足,资源分配不当,进程推进顺序不合适

死锁判定方法:

  1. 第一步:jps命令 类似Linux的ps -ef

    jps -l

  2. 第二步:jstack 查看堆栈信息 JVM自带的堆栈跟踪工具 jstack 10860 10860是线程号

三、JUC辅助类

1. 减少计数 CountDownLatch

static void testCountDownLatch() throws InterruptedException {
        //使用CountDownLatch时
        //设置减少计数的初始值
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName()+"离开教室");
                countDownLatch.countDown(); //计数器-1
            },String.valueOf(i)).start();
        }
        countDownLatch.await(); //当countDownLatch初始值减为0时,await()才终止并继续执行后续代码

        System.out.println(Thread.currentThread().getName()+"锁门");
    }
           

2. 循环栅栏 CyclicBarrier

//触发条件
    public static final int NUMBER = 7;
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(NUMBER,()->{
            System.out.println("CyclicBarrier条件触发");
        });

        //创建7个线程
        for (int i = 1; i <= NUMBER; i++) {
            new Thread(()->{
                System.out.println(Thread.currentThread().getName());
                try {
                    //如果没有达到触发条件,创建的线程就一直等待
                    // 也就是说await方法要等待7次之后CyclicBarrier条件才能触发
                    cyclicBarrier.await();
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
            },String.valueOf(i)).start();
        }

    }
           

3. 信号灯 Semaphore

//创建Semaphore对象,设置许可数量 (停车位)
        Semaphore semaphore = new Semaphore(3);

        //模拟6辆汽车争夺三个停车位
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try {
                    semaphore.acquire();//抢占信号灯资源
                    System.out.println(Thread.currentThread().getName()+"抢到了车位");

                    //随机生成0-5的休眠时间
                    TimeUnit.SECONDS.sleep(new Random().nextInt(5));

                    System.out.println(Thread.currentThread().getName()+"离开了车位**");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    //必须要释放,否则程序不结束
                    semaphore.release();
                } 
            },String.valueOf(i)).start();
        }
           

四、线程池

1. 自定义线程池

当线程池最大线程数和阻塞队列都满了,此时会直接执行拒绝策略

当常驻线程和阻塞队列都满了之后,如果再来请求,此次请求则会被优先处理

1. 4种拒绝策略

  • AbortPolicy:直接抛出异常,阻止系统正常运行
  • CallerRunsPolicy:将任务回退给调用者
  • DiscardOldestPolicy:抛弃队列中等待最久的任务,然后把当前任务加入到队列中,并尝试再次提交
  • DiscardPolicy:抛弃无法处理的任务

2. 自定义线程池

在实际开发中,一般使用自定义的线程池

// 线程池
public class ThreadPoolUtil {
    private static ExecutorService threadPool;
    private static int coreNum;
    private static int MAX_NUM = 150; //单核cpu最大承载量

    static {
        // 在项目尚未运行,JVM正在加载类的时候就获取到系统CPU核数
        coreNum = Runtime.getRuntime().availableProcessors();
    }

    public synchronized static ExecutorService getInstance(){
        if (threadPool == null){
            threadPool = new ThreadPoolExecutor(MAX_NUM,coreNum * MAX_NUM,5,
                    TimeUnit.SECONDS,new LinkedBlockingQueue<>(50),
                    Executors.defaultThreadFactory(),
                    new ThreadPoolExecutor.DiscardOldestPolicy());
        }
        return threadPool;
    }

}