一文了解Java并发编程

一文了解Java并发编程,第1张

一文了解Java并发编程

并发编程会引发哪些问题,如何解决?
1.上下文切换,
    解决方案:1.使用java的atomic包的原子 *** 作类进行 *** 作,该 *** 作基于CAS算法,减少加锁。
            2.无锁并发  不同线程处理不同分段的数据
            3.减少不必要的线程
            4.协程,单线程里多任务调度
2.死锁
    解决方案:一个线程避免获取多个锁
            避免一个线程在一个锁内获取多个资源
            使用定时锁 lock.tryLock(timeout    )
3.资源限制
    解决方案:使用集群并发执行程序

并发机制的实现方式和原理
1.volatile 对有volatile修饰的变量进行 *** 作的代码被编译成字节码时会在后面增加一条 lock addl指令,lock指令会将缓存数据回写内存,其他线程中该变量的缓存数据失效,保证volatile变量的可见性。addl指令会插入一条内存屏障,防止指令重排序,保证有序性。

2.synchronized 以代码块同步为例,synchronized修饰的代码块被编译成字节码时会在前后插入一条monitorenter和monitorexit指令,这两条指令分别对应着java内存模型的lock和unlock *** 作,由java内存模型 *** 作的释义可以看出synchronized保证了线程安全的原子性、有序性、可见性。


3.Lock 显式的获取锁和释放锁
与synchronized区别:1.提供了lockInterruptly()方法,可被中断地获取锁
                    2.可以设置多个获取锁的条件
                    3.公平锁  最先申请获取锁的线程在锁被释放后先获得锁


ConcurrentHashMap实现原理
通过锁分段技术解决HashMap线程不安全和HashTable同步锁 *** 作效率低下的问题,给每段数据配一个锁,当一个线程锁定一段数据时,其他线程可以访问其他分段的数据。
它由Segment数组和HashEntry数组构成,Segment是一种ReentrantLock,每个Segment守护着一组HashEntry数组,当对HashEntry中的数据进行 *** 作时必须先获得它的Segment可重入锁。

ConcurrentlinkedQueue
它是一个非阻塞队列,由一个head节点和tail节点组成,每个节点由一个节点元素和指向下一节点的引用next组成,从而组成一张链表结构的队列
它的入队和出队 *** 作使用CAS算法保证线程安全

阻塞队列
当队列满时,向队列插入元素的线程会被阻塞直至队列不满
当队列为空时,从队列获取元素的线程会被阻塞直至队列非空
常用于生产消费场景
java阻塞队列:
ArrayBlockingQueue
linkedBlockingQueue
使用通知的方式实现 通过Condition的await()和signal()方法实现

原子 *** 作类     使用CAS算法 *** 作数值  将预期值与原始值相比较  如果相同则用新值更新原始值,如果不同则循环进行conpareAndSet *** 作
原子更新基本数据类型 AtomicInteger  
原子更新数组    AtomicIntegerArray
原子更新引用 AtomicReference
原子更新字段 AtomicIntegerFieldUpdater


java中的并发工具类
等待多线程完成的CountDownLatch
构造一个CountDownLatch对象,构造对参为并发执行的任务数
调用CountDownLatch的await方法阻塞当前线程,知道所有任务完成或者超过等待时间

CyclicBarrier 同步屏障  让一组线程到达一个屏障时被阻塞知道所有线程到达,可用于多线程计算最后合并结果的场景
CountDownLatch只能使用一次,CyclicBarrier可以通过reset方法重置计数器
cyclicBarrier.await()阻塞当前线程直至所有线程到达屏障

Semaphore 控制并发线程数 semaphore.accuire()获取许可证   semaphore.release() 归还许可证
Exchanger 线程数据交换  当一个线程调用exchange()方法,需要等待另一个线程执行exchange()方法   可用于数据校对

java中的线程池
线程池的实现原理:
当一个任务提交到线程池时
1.判断核心线程池是否已满,如果不是,则创建一个新的线程执行任务,否则进入下一流程
2.判断任务队列是否已满 ,如果不满则把当前任务加入任务队列,否则进入下一流程
3.判断线程池是否已满,如果不是,则创建一个线程来执行任务,否则将任务交给饱和策略
通过ThreadPoolExecutor创建线程池,指定
    核心线程数量corePoolSize、
    阻塞队列(用于存放任务)runnableTaskQueue、
    线程池最大数量maximumPoolSize、
    创建线程的工厂ThreadFactory、
    饱和策略RejectedExcutionHandler、
    线程空闲后存活的时间keepAliveTime

通过execute()方法(没有返回值)和submit()方法(有返回值)向线程池提交任务
通过shutdown或shutdownNow方法调用所有线程的interrupt方法来终端线程关闭线程池

CPU密集型配置尽可能小的线程池,N(cpu数量)+1个线程
IO密集型配置尽可能多的线程的线程池,2*cpu数量个线程
任务优先级不同的任务可以使用任务优先级队列PriorityBlockingQueue来让优先级高的任务优先执行,通过实现copareto方法或者自定义Comparetor来进行优先级排序

Executor框架
任务 Runnable Callable
任务的执行 Executor <-ExcutorService <-ThreadPoolExecutor和ScheduledThreadPoolExecutor
异步任务结果 Future和FutureTask

欢迎分享,转载请注明来源:内存溢出

原文地址:https://www.54852.com/zaji/5678095.html

(0)
打赏 微信扫一扫微信扫一扫 支付宝扫一扫支付宝扫一扫
上一篇 2022-12-17
下一篇2022-12-17

发表评论

登录后才能评论

评论列表(0条)

    保存