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之间

  • 相关阅读:
    编程基础——c 选择结构程序设计个人总结实例
    编程基础C——常量,变量,运算符个人总结
    编程基础C——格式化输入函数scanf的应用
    编程基础C——基本数据类型个人总结:
    django-base
    django-mvc
    OSI七层与TCP/IP五层网络架构详解
    tcp/ip三次握手及四次挥手
    网页浏览过程
    http协议
  • 原文地址:https://www.cnblogs.com/GeekDanny/p/12884843.html
Copyright © 2011-2022 走看看