并发编程是每一个程序猿进阶的必修之课,想写一个安全稳定,性能强劲的并发程序可没那么easy。我将在未来的日子里,与大家分享一个并发小白成长路上的所思所想。并发编程的思想是通的,可是样例得要是具现的,在该系列中将使用java语言用以演示。
此文作为为漫谈并发编程系列的第一篇,探本溯源,以一篇对并发的文字描写叙述开头。
并发编程由来
早年的计算机中没有操作系统,在某个时间段内仅仅支持运行一个程序,而且这个程序能訪问计算机的全部资源。在这个程序全然运行完后,再运行下一个程序。
在此时,引入并发编程的优点:
- 高效性:计算机的各个部件不用忙等,比如一个程序在使用IO的时候,CPU能够给另外一个程序使用。这样便提高了设备的使用率。
- 公平性:计算机上运行的程序应该一视同仁,而不是某一个先运行完后,再让另外一个程序開始运行。
- 便利性:在计算多个任务时,应该编写多个程序,必要时,让其互相通信,这比让一个程序来实现多个任务要更easy实现。
并发编程最早的体现是多进程的并发编程,这是由操作系统的出现而引入的。多进程的并发编程攻克了进程独占计算机所有设备的问题,提升了计算机的吞吐量。能够想象在这样的情况下随意时刻的计算机设备与正在执行的进程的相应关系的是一对一,如图所看到的:。假设当前执行进程较少,系统的非常多设备依旧会处于空暇状态,此时,使用线程能够进一步提高计算机的处理效率,线程同意一个进程中有多个程序控制流,那么一个进程的多个线程就能同一时候执行在多个设备上,如图所看到的:,所以使用线程能进一步提升系统的吞吐量及处理效率。而如今,随着摩尔定律的实现重任逐渐由单cpu的性能添加转移到cpu数量的添加,怎样提高多cpu的资源利用率变得非常重要,这些原因促使线程的并发编程越发重要。(从以上分析来看,要从底层提升多线程程序的性能能够使多个线程能够同一时候执行在多个设备上。)
建模的简单性
线程除了可以提升性能,还可以将复杂而且异步的工作流进一步分解为一组简单而且同步的工作流,每一个工作流在一个单独的线程中执行,并在特定的同步位置进行交互。这一条实际影响了多线程技术的使用场景,即通常将逻辑上或物理上独立的任务用多线程技术来实现(如计算任务与IO任务),而非在程序里相互交错。
线程带来的风险
- 安全性问题:在非常多时候,线程间并非全然独立的,而是相互协作去共同完毕一个task,因此可能须要共同的去訪问一个或多个资源,当线程之间没有正确的进行协作时,可能会导致单个资源混乱或者多个资源间存在数据一致性的问题。
- 活跃性问题:有时往往须要控制多个线程的运行顺序,比如一个线程去读IO,还有一个线程从上一个线程中取,因此我们须要将线程进行同步的控制。线程间的协作,easy导致死锁、饥饿等问题。由此线程就会变得不"活跃"。
- 性能问题:频繁的线程切换、同步机制(常常抑制编译器优化,使内存缓存区的数据无效)。在这里提出一个问题:线程是能够运行一些复合操作的,在使用方式上与进程差异不大,为什么我们不在线程的基础上进一步的细分,比方搞出一个比线程更细的调度单位"小线程",这种话程序的运行效率不会更高吗?答:这样会进一步添加程序控制流的数量,会导致更加频繁的线程切换,事实上划分到线程这一层也许就是最好的折中了。
无所不在的线程
使用多线程的场景
最常见的须要编写多线程程序的场景是:1. 有多个逻辑或者物理的任务须要运行,用多个线程将其区分,而非在一个线程中完毕全部工作,在这里使用多线程的主要优点是使程序结构更加清晰、并简化了编程。2. 将一个任务拆分成多个较小的任务,或者将一个任务分为几部分,交给多个线程运行,比方计算任务,此时使用多线程的主要目的就是提升性能了。
须要编写线程安全代码的场景
除了主动编写多线程程序来"攻",我们还常常须要"防"多线程,防止多线程带来的一些衍生危害。
- Timer:我们自己写的定时任务,Timer管理的线程会调用。而其它线程也非常有可能,因此,在这要注意线程安全。
- Servlet和JSP:通常我们的Servlet和JSP会被多个线程同一时候所调用,webserver会为每个到来的http请求都从线程池中分配一个线程给它。因此在Servlet和JSP中我们须要保证线程安全,同理,在ServletContext和HttpSession等容器中保存的Servlet过滤器和对象,都必须是线程安全的。
- 远程方法调用(RMI):在RMI中,存在被多个线程重入的可能,所以必须确保它们自身的线程安全性。