本文源自Recently祝祝,创自Recently祝祝。转载请标注出处。

此处理办法在企业中有所运用,适合Java初级开发学习,参考。

本文字数与行数,耐心阅读必有收成。

初识并发编程【3】之JUC

1.JUC说明

JUC便是java的一个包java.util.concurrent的简称,java中用于并发编程常用的一个东西包,具有强壮的功能,能补偿synchronized缺点,synchronized不能够设置堵塞时长,JUC不仅仅能够设置超时时刻而且还能够主动的中止线程。而且在读多写少的场合JUC能够有多种处理办法,而避免了synchronized加锁带来的开支。

JUC是处理多线程安全一个东西包,供给了非常好用的东西以及类,比方接下来要讲的原子类(Automic),锁(Lock)等处理一些不满足多线程特性的问题,进步程序功能和安全性,跟方便的办理与调度。JUC是Java并发编程不可或缺的部分。

2.JUC运用在哪里

JUC用于支撑高效、安全的多线程编程。详细来说,JUC 包含线程池、原子类、锁(Lock)、并发调集、并发容器、堵塞行列

  1. 线程池:经过 Executor、ExecutorService、ThreadPoolExecutor 等类,完成线程池的创建、办理和调度,避免了线程频频创建和销毁的开支,进步了多线程程序的功率
  2. 原子类:Automic包,java.util.concurrent.automtic,经过 AtomicInteger(整数原子型)、AtomicLong(长整型原子类)、AtomicReference(引证类型原子类) 等类,供给线程安全的原子操作,用法简略功能高效,有效避免Volatie原子性操作变量的问题,避免了多线程并发状况下的数据竞赛和安全问题。
  3. 并发调集:经过 ConcurrentHashMap、ConcurrentLinkedQueue、ConcurrentSkipListMap 等类,供给线程安全的调集操作,避免了多线程并发状况下的数据竞赛和安全问题。
  4. 堵塞行列:经过 DelayQueue 类,完成基于时刻的任务调度,支撑在指定时刻后履行任务,避免了运用 Thread.sleep() 的功率问题。
  5. 锁和条件变量:经过 Lock、Condition 等接口和完成类,供给更灵活、更高效的线程同步机制,支撑更细粒度的锁操控。

初识并发编程【3】之JUC

3.JUC首要运用

3.1原子类

原子类能够供给线程安全的原子操作,避免多线程并发状况下的数据竞赛和安全问题。

Aotumtic包共13个类,4种类型的原子更新办法(原子更新基本类型、原子更新数组、原子更新引证和原子更新特点(字段))Atomic包里的类基本都是运用Unsafe完成的包装类。

举例:

public class VolatileNoAtomicity {
    public static void main(String[] args) throws InterruptedException {
        VolatileDemo demo = new VolatileDemo();
        for (int i = 0; i < 2; i++) {
            Thread t = new Thread(demo);
            t.start();
        }
        Thread.sleep(1000);
        System.out.println("count = "+demo.count);
    }
    static class VolatileDemo implements Runnable {
        public volatile int count;
        //public volatile AtomicInteger count = new AtomicInteger(0);
        public void run() {
            addCount();
        }
        public void addCount() {
            for (int i = 0; i < 10000; i++) {
                count++;//可是实际状况是三条汇编指令
            }
        }
    }
}

你会发现成果不是20000,由于volatile不具备原子性。

初识并发编程【3】之JUC

原因剖析:

  1. 线程1读取count的值为5

  2. 线程2读取count的值为5

  3. 线程2加1操作

  4. 线程2最新count的值为6

  5. 线程2写入值到主内存的最新值为6

  6. 线程1履行加1count=6,写入到主内存的值是6。

  7. 成果:对count进行了两次加1操作,主内存实际上仅仅加1一次。成果为6

  8. 这样的状况出现屡次之后,就会放成果与预期成果不一致。

处理运用原子操作:

初识并发编程【3】之JUC

初识并发编程【3】之JUC
你就会发现,成果变成了20000,这便是原子类的保证了原子性。

3.2什么是CAS

CAS(Compare and Swap:比较替换)一种无锁算法(达观锁机制),能够保证在多线程并发状况下对变量的操作是原子性的。同步组件中大量运用CAS技能完成了Java多线程的并发操作。整个AQS同步组件、Atomic原子类操作等等都是以CAS完成的,乃至ConcurrentHashMap在1.8的版别中也调整为了CAS+Synchronized。能够说CAS是整个JUC的柱石。

CAS 的基本思想是比较当前内存中的值和期望值是否持平,假如持平,则履行操作并更新内存中的值,否则不履行任何操作。实质一个办法调用一行CPU原子操作履行函数:CAS(V,E,N)当且仅当内存地址V中的值等于 预期值E 时,将内存V中的值改为N,进行暂停一瞬间。

3.3Lock锁

Lock 锁,锁所锁住的是一个目标显式锁机制,与 synchronized 关键字相对应,运用起来粒度更细致,而且更灵活,能够完成公正锁、非公正锁、可重入锁等,同时还能够完成多个条件变量等高级功能。能够完成异步变同步的转换。

3.4什么是AQS

AQS是一个并发基础框架类,用于构建lock、同步器等的底层框架模型。AQS是一个行列,为了让线程来排队,其间每个节点表明一个等候线程,当一个线程请求锁资源时,假如锁已被占用,该线程将被加入到行列中等候,称为线程同步器。本身并不是一种详细的同步器,而是一个供给了同步器完成所需的底层机制的抽象类。完成自定义服务器,也能够运用AQS供给的一些模板来完成同步器的基本功能。

3.5JUC东西类

线程协作东西类,操控线程协作的东西类,让线程之间的协作变得更加简略

1) CountDownLatch倒数门栓

倒数完毕之前,一向处于等候状况,直到数到0完毕,此线程才持续作业。

场景:购物拼团,大巴人满发车,分布式锁

首要办法:

  • 结构函数:new CountDownLatch(int count):只要一个结构函数,参数count为需求倒数的数值。
  • await():当一个或多个线程调用await()时,这些线程会堵塞。
  • countDown():其他线程调用countDown()会将计数器减1,调用countDown办法的线程不会阻 塞。当计数器的值变为0时,因await办法堵塞的线程会被唤醒,持续履行
/**
* CountDownLatch事例:6个程序猿加班
* 当计数器的值变为0时,因await办法堵塞的线程会被唤醒,持续履行
*/
public class Demo11CountDownLatch {
    public static void main(String[] args) throws InterruptedException {
        CountDownLatch countDownLatch = new CountDownLatch(6);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try { TimeUnit.SECONDS.sleep(5); } catch
                    (InterruptedException e) {e.printStackTrace(); }
                System.out.println(Thread.currentThread().getName() + "\t上
                                   完班,离开公司");
                countDownLatch.countDown();
            }, String.valueOf(i)).start();
        }
        new Thread(()->{
            try {
                countDownLatch.await();//卷王也是有极限的,设置超时时刻
                System.out.println(Thread.currentThread().getName()+"\t卷王最
                                   后关灯走人");
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "7").start();
    }
}

2) Semaphore信号量:限流

用来限制或办理数量有限资源的运用状况。 (雪崩)

场景:Hystrix、Sentinel限流

信号量的作用便是保护一个”许可证”的计数,线程能够”获取”许可证,那信号量剩下的许可证就减少一个,线程也能够”开释”一个许可证,那信号量剩下的许可证就能够加一个。当信号量拥有的许可证数为0时,下一个还要要获取许可证的线程就需求等候,直到有别的的线程开释了许可证。

首要办法: 中心办法不多

  1. 结构函数:new Semaphore(int permits,Boolean fair):能够设置是否运用公正策略,假如传入true,则 Semaphore会把之前等候的线程放到FIFO行列里,以便有了新许可证能够分给之前等候时刻最长 的线程。
  2. acquire():获取许可证,当一个线程调用acquire操作时,他要么经过成功获取信号量(信号量减 1),要么一向等候下去,直到有线程开释信号量,或超时。
  3. release():开释许可证,会将信号量加1,然后唤醒等候的线程。
/**
* Semaphore事例:三辆小汽车抢车位
* Semaphore信号量首要作用:1.用于多个共享资源的互斥运用,2.用于并发线程数的操控
*6个线程抢3个车位
*/
public class Demo12Semaphore {
    public static void main(String[] args) {
        //模拟资源类,有3个空车位
        Semaphore semaphore = new Semaphore(3);
        for (int i = 1; i <= 6; i++) {
            new Thread(()->{
                try{
                    //占有资源
                    semaphore.acquire();
                    System.out.println(Thread.currentThread().getName()+"\t
                                       抢到车位");
                    try { TimeUnit.SECONDS.sleep(3); } catch
                        (InterruptedException e) {e.printStackTrace(); }
                    System.out.println(Thread.currentThread().getName()+"\t
                                       泊车3秒后离开车位");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    //开释资源
                    semaphore.release();
                }
            }, "Thread-Car-"+String.valueOf(i)).start();
        }
    }
}

3)CyclicBarrier循环栅门

线程会等候,直到线程到了事前规定的数目,然后触发履行条件进行下一步动作

场景:并行核算

当有大量线程互相配合,别离核算不同任务,而且需求最后一致汇总时,就能够用CyclicBarrier,它能够结构一个集结点,当某一个线程履行完,它就会到集结点等候,直到一切线程都到集结点,则该栅门 就被吊销,一切线程一致出再,持续履行剩下的任务。

首要办法:

  • 结构函数:new CyclicBarrier(int parties, Runnable barrierAction),设置集合的线程数量和集齐线程 数的成果之后要履行的动作。

  • await():堵塞当前线程,待凑齐线程数量之后持续履行

import java.util.concurrent.CyclicBarrier;
/**
* 事例:集齐7龙珠呼唤神龙
*/
public class Demo13CyclicBarrier {
    public static void main(String[] args) {
        CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
            System.out.println("======呼唤神龙");
        });
        for (int i = 1; i <= 7; i++) {
            final int tempInt = i;
            new Thread(()->{
                try {
                    System.out.println(Thread.currentThread().getName() +
                                       "\t收集到第" + tempInt + "颗龙珠");
                    cyclicBarrier.await();
                    System.out.println(Thread.currentThread().getName() +
                                       "\t第" + tempInt + "颗龙珠飞走了");
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }, "Thread-"+String.valueOf(i)).start();
        }
    }
}

文章全为个人了解,假如发现部分跟你所知道的有出入,欢迎在谈论区指出,欢迎探讨