zoukankan      html  css  js  c++  java
  • Java并发基础

    @

    1.Volatile

    volatile是一个关键字,用于在并发编程中修饰变量

    volatile:java提供的一种同步机制

    • 轻量的同步机制,用来确保将变量的更新通知到其他线程
    • 保证可见性(禁止指令重排)、不保证原子性

    如何保证可见性

    • 变量声明为volatile类型后,编译器与运行时都会注意到这个变量时共享的,不会将该变量
      上的操作和其他内存操作仪器重排序
    • volatile变量不会被缓存在寄存器

    指令重排
    指令重排

    多线程环境中,线程交替运行,编译器优化重排的存在,两个线程中使用的变量无法保持一致性

    JMM(Java内存模型)

    JMM描述的是一组规范,通过规范来定义程序中各个变量的访问方式

    jmm同步规定:

    • 1.线程解锁前,必须把共享变量的值刷新回主内存
    • 2.线程加锁前,必须读取主内存的最新值到自己的工作内存
    • 3.加锁解锁是同一把锁

    不保证原子性

    原子性:不可分割、完整性,即某个线程正在做某个具体业务,中间不可以被加塞或者被分割,需要整体完整,
    要么同时成功,要么同时失败

    使用AtomicInteger,synchronized 可以保证原子性

    2.CAS

    CompareAndSet-- 比较并设置(交换)

    CAS是一条CPU并发原语,体现在sun.misc.Unsafe类中各个方法

    public final boolean CompareAndSet(int expectedValue,int newValue){
        return U.CompareAndSetInt(this,VALUE,expectedValue,newValue);
    }
    
    private static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    

    原子变量是一种更好的“volatile”

    原子变量比锁的力度更细,量级更轻

    AtomicInteger,AtomicLong,AtomicBoolean,AtomicReference

    CAS的缺点

    • 1.循环时间长,开销大(CAS长时间不成功)
    • 2.只能保证一个共享变量的原子操作
    • 3.ABA问题

    解决ABA问题

    AtomicStampedReference

    并发容器类(ConcurrentHashMap,CopyOnWriteArrayList)代替同步容器类(Hashtable,Vector)

    CopyOnWriteArrayList的操作,读写分离

    
    public boolean add(E e){
        final ReentrantLock lock=this.lock();
        lokc.lock();
        try{
            Object[] elements=getArray();
            int len=elements.length;
            Object[] newElements=Arrays.copyof(elements,len+1);
            newElements[len]=e;
            setArray(newElements);
            return true;
        }finally{
            lock.unlock();
        }
    }
    

    3.锁

    公平锁,非公平锁

    ReentrantLock 默认是非公平锁,但是可以设置称为公平锁
    synchronized 是非公平锁

    public ReentrantLock() {
        sync = new NonfairSync();
    }
    
    // 通过两个静态内部类来实现
    public ReentrantLock(boolean fair) {
        sync = fair ? new FairSync() : new NonfairSync();
    }
    

    可重入锁(递归锁)

    递归锁是指同一个线程外层函数获得锁之后,内层递归函数仍然能获取该锁的代码

    线程可以进入任何一个它已经拥有的锁所同步着的代码块

    ReentrantLock/Synchronized 就是典型的可重入锁

    重入锁进一步提升了加锁行为的封装性

    独占锁(写锁)/共享锁(读锁)/互斥锁

    独占锁:该锁一次只能被一个线程持有(ReentrantLock,Synchronized)
    共享锁:该锁可以被多个线程所持有(ReentrantReadWriteLock的读锁)

    自旋锁(spinlock)

    尝试获取锁的线程不会立即阻塞,而是采用循环的方式去尝试获取锁

    synchronized lock
    原始构成 JVM,底层使用monitor对象完成 具体类(java.util.concurrent.locks)
    中断 不可中断 可以中断
    加锁公平性 非公平锁 默认是非公平,构造传入true可以实现公平
    条件 无条件 Condition

    4.AQS

    AbstractQueuedSynchronizer

    在java.util.concurrent

    • ReentrantLock

    • ReentrantReadWriteLock

    • SynchronousQueue

    • FutureTask

    • CountDownLatch

    • Semaphore

    • CyclicBarrier

    CountDownLatch

    • 1.它允许一个或多个线程一直等待,知道其他线程的操作执行完后再执行。例如,应用程序的主线程希望在负责 启动框架服务的线程已经启动所有的框架服务之后再执行
      1. CountDownLatch主要有两个方法,当一个或多个线程调用await()方法时,调用线程会被阻塞。其他线程调用 countDown()方法会将计数器减1,当计数器的值变为0时,因调用await()方法被阻塞的线程才会被唤醒,继续执行

    Semaphore

    可以代替Synchronize 和Lock

    用于多个共享资源的互斥作用,另一个用于并发线程数的控制

    CyclicBarrier

    可循环(Cyclic)使用的屏障。让一组线程到达一个屏障(也可叫同步点)时被阻塞,知道最后一个线程到达屏 障时,屏障才会开门,所有被屏障拦截的线程才会继续干活,线程进入屏障通过CycliBarrier的await()方法

    可以设置循环使用

    5.ThreadPool

    阻塞队列

    • ArrayBlockingQueue,一个基于数组结构的有界阻塞队列,FIFO
    • LinkedBlockingQueue,个基于链表结构的阻塞队列,FIFO,吞吐量高于ArrayBlockingQueue
    • SynchronousQueue,一个不存储元素的阻塞队列,每个插入操作必须等到另一个线程调用移除操作,否则插入操作一直处于阻塞状态,吞吐量较高

    阻塞队列

    在多线程领域:所谓阻塞,在某些情况下会挂起线程,一旦满足条件,被挂起的线程又会自动被唤醒

    使用阻塞队列BlockingQueue时我们不需要关心什么时候需要阻塞线程,什么时候需要唤醒线程,因为这一切BlockingQueue都可自行处理

    阻塞队列的各种方法

    方法类型 阻塞 抛出异常 特殊值 超时
    插入 put(e) add(e) offer(e) offer(e,time,unit)
    移除 take() remove() poll() poll(time,unit)
    检查 ~ element() peek() ~

    线程池

    线程池主要是用来控制线程的数量,处理过程中将任务放进队列,然后在线程创建后启动给这些任 务,如果线程数量超过了最大数量,超出数量的线程排队等候,等其他线程执行完毕,再从队列中取出任务来 执行

    • 线程复用,降低线程创建和销毁造成的的资源消耗
    • 控制最大并发数
    • 管理线程,提升响应速度,任务到达不需要等待线程创建的过程

    几个线程池的实现

    • Executors.newFixedThreadPool(int)
    • Executors.newSingleThreadExecutor()
    • Executors.newCachedThreadPool()

    Executor

    ThreadPoolExecutor

    public ThreadPoolExecutor(int corePoolSize,
                              int maximumPoolSize,
                              long keepAliveTime,
                              TimeUnit unit,
                              BlockingQueue<Runnable> workQueue,
                              ThreadFactory threadFactory,
                              RejectedExecutionHandler handler)
    
    • corePoolSize,线程池中常驻核心线程数
      在创建了线程池后,当有请求任务来之后,就会安排池中的线程去执行请求任务
      当线程池的线程数达到corePoolSize后,就会把到达的任务放到缓存队列当中

    • maximumPoolSize,线程池能够容纳同时执行的最大线程数,必须大于等于1

    • keepAliveTime,多余空闲线程的存活时间

    • unit, 存活的时间单位 (TimeUnit.)

    • workQueue,任务队列,被提交但是尚未被执行的任务

    • threadFactory,表示生成线程池中工作线程的线程工厂,默认即可

    • handler,拒绝策略

    RejectedExecutionHandler 拒绝策略

    当线程全部占用,等待队列已经全部排满,无法接纳新的任务,需要拒绝策略机制来合理的处理这个问题

    • AbortPolicy 默认,直接抛出RejectedExecutionException异常阻止系统正常运行

    • CallerRunsPolicy,将任务回退到调用者

    • DiscardOldestPolicy,抛弃队列中等待最久的任务,然后把当前任务加入队列中尝试再次提交当前任务

    • DiscardPolicy,直接丢弃任务

    线程池创建

    • 线程池不允许>使用Executors创建
    • 通过ThreadPoolExecutor的方式,规避资源耗尽风险
    • FixedThreadPool和SingleThreadPool允许请求队列长度为Integer.MAX_VALUE可能会堆积大量请求
    • CachedThreadPool和ScheduledThreadPool允许的创建线程数量为Integer.MAX_VALUE,可能会创建大量线程,导致OOM

    线程池的工作流程

    线程池的工作流程

    配置线程池

    • CPU密集型,大量的运算,基本不会出现阻塞,线程池=CPU核心数+1
    int num_cpu=Runtime.getRuntime().availableProcessors();  // cpu的核心数
    
    • IO密集型,会出现阻塞,尽可能多的配置多的线程
      估算出任务的等待时间与计算时间的比值

    CPU核心数/(1-阻塞系数) ,阻塞系数0.8~0.9之间

  • 相关阅读:
    UVA
    UVA
    模板——扩展欧几里得算法(求ax+by=gcd的解)
    UVA
    模板——2.2 素数筛选和合数分解
    模板——素数筛选
    Educational Codeforces Round 46 (Rated for Div. 2)
    Educational Codeforces Round 46 (Rated for Div. 2) E. We Need More Bosses
    Educational Codeforces Round 46 (Rated for Div. 2) D. Yet Another Problem On a Subsequence
    Educational Codeforces Round 46 (Rated for Div. 2) C. Covered Points Count
  • 原文地址:https://www.cnblogs.com/GeekDanny/p/12884843.html
Copyright © 2011-2022 走看看