高并发程序设计

Posted quinnsun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了高并发程序设计相关的知识,希望对你有一定的参考价值。

1、概念
1、同步(sync)、异步(async)
     同步方法一旦开始,调用者必须等到方法返回后,才能继续后续行为。
     异步方法调用后可以继续后续的操作
2、并发(concurren)、并行(parallelism)
     并发和并行都表示两个或多个任务一起执行。并发偏重于多个任务交替执行,而多个任务之间有可能是串行的。并行是指同时执行。
3、临界区
     临界区用来表示一种公共资源或是共享资源,可以被多个线程使用。但每次只能一个线程使用它,一旦临界区资源被占用,其他线程需要等待。
4、阻塞(blocking)、非阻塞(non-bloacking)
     阻塞和非阻塞通常用来形容多线程间的相互影响。由于等待导致线程刮起,这种情况就是阻塞。非阻塞强调没有一个线程可以妨碍其他线程执行。
5、死锁、饥饿、活锁
     死锁:线程相互占用其他线程等待的资源。
     饥饿:某一个或者多个线程因为某些原因无法获得所需要的资源,导致一致无法执行。
     活锁:线程间相互谦让,资源在线程间来回切换,而无法正常运行。
 
2、并发级别:阻塞、无饥饿、无障碍、无锁、无等待
     无饥饿:对于非公平锁来说,系统允许高优先级的线程插队,这样可能导致低优先级的线程产生饥饿。
     无障碍:最弱的非阻塞调度。阻塞是一种悲观策略,非阻塞是一种乐观策略。
     无锁:在无锁的情况下,所有的线程都能尝试对临界区进行访问,但无锁的并发保证必须有一个线程能够在有限步内完成操作离开临界区。
     无等待:在无锁的基础上,要求在有线程能够在有限步内完成。
 
为什么要使用并行程序:Amdahl定律(阿姆达尔)、Gustafson定律(古斯塔夫森)。
Amdahl定律:定义了串行系统并行化后的加速比计算公式和理论上限。
加速比 = 优化前系统耗时/优化后系统耗时
 
3、JMM:原子性、可见性、有序性
原子性:一个操作是不可中断的。
可见性:一个线程修改了某一个共享变量后,其他线程能够立即感知
有序性:线程执行时,可能会进行指令重排,重排后的顺序与原指令的顺序未必一致。
3.1 哪些指令不能重排(happen-before原则)
  •      程序顺序原则:一个线程内保证语义的串行性。
  •      volatile规则:volatile变量的写,先发生于读,这保证了volatile变量的可见性。
  •      锁规则:解锁必须发生在随后的加锁之前。
  •      传递性:A先于B,B先于C,那么A必然先于C。
  •      线程的start方法先于它的每一个动作。
  •      线程的所有操作先于线程的终结(thread.join)。
  •      线程的中断(interrupt)先于被中断线程的代码。
  •      对象的构造函数执行、结束先于finalize方法。
 
4、java并行程序
进程:计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。进程是线程的容器、程序是指令数据及组织形式的描述、进程是程序的实体。
线程的六种状态:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED
 
在虚拟机的Clinet模式下,由于JIT并没有做足够的优化,主线程修改共享面量后,子线程发现变动并退出程序。
Server模式下,由于优化的结果,子线程无法“看到”主线程中的修改,会继续运行。
 
线程组:如果线程数量很多,并且功能分配明确,就可以将相同功能的线程放置在一个线程组里。
守护线程:守护线程是后台线程。比如垃圾回收线程、JIT线程。
 
4.1 synchronized的用法
  •      指定加锁对象:对给定对象加锁,进入同步代码前要活的给定对象的锁。
  •      直接作用于实例方法:相当于对当前实例加锁,进入同步代码前要先获得实例的锁。
  •      直接作用于静态方法:相当于对当前类进行加锁,进入同步代码前要获得当前类的锁。
 
5、JDK并发包
5.1 同步控制
synchronized的功能扩展:重入锁(reentrantLock)
重入锁可以完全替代synchronized关键字。
重要的方法:
  • Lock() 获得锁,如果锁被占用,则等待。
  • lockInterruptible() 获得锁,但优先响应中断。
  • tryLock() 尝试获得锁,如果成功,返回true,失败返回false。该方法不等待立即返回。
  • tryLock(time,timeunit) 在给定时间内尝试获得锁。
  • unlock() 释放锁。
 
重入锁的实现
  • 原子状态,使用cas操作来存储当前锁的状态,判断锁是否已经被别的线程持有。
  • 等打死队列,所有没有请求到锁的线程,会进入等待队列进行等待。待有线程释放锁后,系统就能从等待队列中唤醒一个线程,继续工作。
  • 阻塞原语park()和unpark(),用来挂起和恢复线程。没有得到锁的线程会被挂起。
condition条件
  • await:使当前线程等待,同时释放当前锁,当其他线程中使用signal或者signalall时,线程会重新获得锁并继续执行。或者当线程被中断时,也能跳出等待。
  • awaitUninterruptibly和await方法基本相同,但它不会在等待过程中响应中断。
  • singal:用于唤醒一个在等待中的线程。相对的singalAll方法会唤醒所有等待中的线程。
关键字synchronized与wait和notify来实现等待/通知模式,reentrantLock与condition结合更加灵活,一个lock对象可创建多个condition实例,线程对象可以指定注册在哪个condition上,从而有选择的进行线程通知。
arrayblockingqueue的put方法和take方法使用重入锁和condition
 
允许多个线程同时访问:semaphore信号量
synchronized和重入锁一次只允许一个线程访问一个资源。信号量可以指定多个线程同时访问某个资源。
 
readWriteLock读写锁
读写分离可以有效的帮助减少锁竞争,以提升系统性能。
  • 读读不互斥:读读之间不阻塞
  • 读写互斥:读阻塞写,写阻塞读
  • 写写互斥:写写阻塞
 
countDownLatch倒计时器
     主线程在countDownLatch上等待,当所有检查任务全部完成后,主线程才能继续执行。
 
cyclicBarrier循环栅栏
     主线程上设置栅栏,当达到栅栏条件后执行下一步操作。
 
lockSupport线程阻塞工具
     它可以在线程内任意位置让线程阻塞。
     suspend()和resume()用park()和unpark()替换。(挂起与恢复)
 
线程池
  
 
 
 
 
 

以上是关于高并发程序设计的主要内容,如果未能解决你的问题,请参考以下文章

实战Java高并发程序设计 5让普通变量也享受原子操作

架构高可用高并发系统的设计原则

Java优化高性能高并发+高并发程序设计

Java优化高性能高并发+高并发程序设计视频教程

3 02 | 高并发架构设计方法:面对高并发,怎么对症下药?

高并发网站架构的设计方案是怎样的?