并发编程会引发哪些问题,如何解决?
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