并发概述

Java中实现高并发是基于多线程编程的,所谓并发,也就是多个线程同时工作,来处理我们的业务。线程是操作系统能够进行运算调度的最小单位,它被包含在进程之中,是进程中的实际运作单位。

优点

  • 充分利用多核CPU的计算能力
  • 方便进行业务拆分,提升应用性能

缺点

频繁的上下文切换

  • 无锁并发编程:可以参照concurrentHashMap锁分段的思想,不同的线程处理不同段的数据,这样在多线程竞争的条件下,可以减少上下文切换的时间
  • CAS算法,从Java5开始,利用Atomic下使用CAS算法来更新数据,使用了乐观锁,可以有效的减少一部分不必要的锁竞争带来的上下文切换
  • 使用最少线程:避免创建不需要的线程,比如任务很少,但是创建了很多的线程,这样会造成大量的线程都处于等待状态
  • 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换

线程安全问题

多个线程同时但以不同的顺序请求临界区锁的时候,容易引起死锁。可以通过jps,jstack验证死锁是否存在。避免死锁的方式:

  • 避免一个线程同时获得多个锁
  • 避免一个线程在锁内部占有多个资源,尽量保证每个锁只占用一个资源
  • 所有的线程都按照相同的顺序获得锁
  • 尝试使用定时锁,使用lock.tryLock(timeOut),当超时等待时当前线程不会阻塞,线程没有在给定的时限内成功获得所有需要的锁,则会进行回退并释放所有已经获得的锁,然后等待一段随机的时间再重试
  • 死锁检测,针对那些不可能实现按序加锁并且锁超时也不可行的场景,检测到死锁后:1、释放所有锁,回退,并且等待一段随机的时间后重试;2、给这些线程设置优先级,让一个(或几个)线程回退,剩下的线程就像没发生死锁一样继续保持着它们需要的锁。如果赋予这些线程的优先级是固定不变的,同一批线程总是会拥有更高的优先级。为避免这个问题,可以在死锁发生的时候设置随机的优先级。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况

并发工具类

  • Semaphore:流量控制,用于控制资源能够被并发访问的线程数量,以保证多个线程能够合理的使用特定资源
  • Exchanger:线程间协作,具体交换数据是通过exchange方法来实现的,如果一个线程先执行exchange方法,那么它会同步等待另一个线程也执行exchange方法,这个时候两个线程就都达到了同步点,两个线程就可以交换数据
  • CountDownLatch:倒计时器,等待其他多个线程完成任务之后,主线程才能继续往下执行业务功能。在这种的业务场景下,可以使用Thread类的join方法,让主线程等待被join的线程执行完之后,主线程才能继续往下执行;使用线程间消息通信机制也可以完成。
  • CyclicBarrier:多线程并发控制,当多个线程都达到了指定点后,才能继续往下继续执行。CountDownLatch一般用于某个线程A等待若干个其他线程执行完任务之后,它才执行;而CyclicBarrier一般用于一组线程互相等待至某个状态,然后这一组线程再同时执行;CountDownLatch强调一个线程等多个线程完成某件事情。CyclicBarrier是多个线程互等,等大家都完成,再携手共进。

参考:

并发编程的优缺点

避免死锁

大白话说java并发工具类-Semaphore,Exchanger

大白话说java并发工具类-CountDownLatch,CyclicBarrier