多线程的好处
在早期的计算机中,是没有操作系统这个东西的,它们从头到尾只执行一个程序,并且可以访问计算机的所有程序。这种工作方式不仅浪费计算机的资源,而且对于编写和运行程序也是一个很大的挑战
于是,多线程技术就应运而生。多线程技术的出现带来了以下的好处
发挥多处理器的功能
由于CPU的基本调度单位是线程,假设在程序中只有一个线程,在双处理器系统上面,单线程的程序只能使用一般的CPU资源,而如果在100处理器的系统上,将会有99%的资源无法使用。引进多线程技术,可以充分发挥多处理器的功能,大大提高资源的利用率和系统的吞吐率
更快的程序响应
假设一个有一个单线程应用,它在某一个端口监听进来的请求。当一个请求到来时,它去处理这个请求,然后再返回去监听。
如果处理的请求非常的耗时,在这段时间内新的客户端就无法发送请求给服务端。
如果我们把监听线程传递给工作线程,然后立刻返回去监听,而工作线程在处理完请求后,发送一个结果给客户端,使用这种方式,服务端可以迅速的返回去监听,因此客户端可以接受更多的请求,而且服务响应的也更加快
更加简单的程序设计
在单线程应用程序中,如果你想编写程序手动处理上面所提到的读取和处理的顺序,你必须记录每个文件读取和处理的状态。相反,你可以启动两个线程,每个线程处理一个文件的读取和操作。线程会在等待磁盘读取文件的过程中被阻塞。在等待的时候,其他的线程能够使用CPU去处理已经读取完的文件。其结果就是,磁盘总是在繁忙地读取不同的文件到内存中。这会带来磁盘和CPU利用率的提升。而且每个线程只需要记录一个文件,因此这种方式也很容易编程实现。
多线程的风险
事物总是具有两面性,多线程的出现带来了更加便捷的操作和使用体验,但是,使用多线程也会出现各种个样的问题。主要体现在以下几个方面
安全性问题
在进行单线程编程的时候,线程对资源拥有100%的控制权。而在进行多线程编程的时候,每个线程的操作顺序是不确定的,这就会出现以下奇怪的问题,比如执行下面的代码
public class UnsafeOperation {
private int value;
public int getNext() {
return value++;
}
}
假设启用两个线程去执行上面的代码,预期的结果是返回两个不同的值,但是如果执行的时机不对,就会得到两个相同的值。
为什么呢,因为value++这个操作看起来是单个操作,但是它实际包含三个操作,读取value的值,对value进行加一操作,然后在把value的值放回去,由于运行时可能将多个线程之间的的操作交替执行,就会出现不同的线程调用中,出现相同的值。
活跃性问题
在多线程环境中,会出现一些无法预料的事情,比如线程A在等待线程B释放它持有的资源,但是B却永远都不释放它所持有的资源,那么A就会永远的等待下去。这就出现了死锁的现象。线程在互相等待的过程中,其实什么事情都不能做,这就带来了活跃性的问题
性能问题
由于CPU的基本调度单位是线程,在进行单线程的编程的时候,线程全程持有CPU的资源,但是在多线程的编程中,当线程调度器临时挂起活跃线程,并转而运行另外一个线程的时候,就会频繁出现上下文的切换,这种操作将带来极大的开销。导致性能问题的出现。
如何解决多线程带来的风险
与多线程带来的好处相比,多线程带来的风险是可以忍受的,我们可以根据下面的手段,来尽最大可能的去规避这些问题
加锁
多线程的安全性出现的原因,是对资源的所属权的和操作顺序的不确定造成的,如果我们可以对在线程操作期间,声明资源属于哪个线程,当这个线程持有这个资源的时候,其他线程就不能对资源进行操作。那么对资源的所属权就明确下来了,这种技术在Java中被称为加锁。下图说明了Java中的锁的类型
7f749fc8图片来源美图技术团队博客
如何解决操作资源顺序不确定。在JVM中通过被称为happens-before原则隐式地保证顺序性。
不加锁
频繁的加锁和解锁可能会带来性能的损耗,于是通过CAS等操作,在对资源操作的时候,不进行加锁。
参考链接
- Java并发编程实战
- 并发编程网 多线程的优点
网友评论