多线程的高并发编程提高了我们的CPU利用率及简化开发模式,但是又同时给我们带来了风险,使用不当就会有风险,那到底有什么风险?我们来看看
1,线程的安全性问题
假设,我现在编写了一个程序,来保存系统的访问量,如何实现呢?
很简单,我可以设置一个全局变量,然后,每次用户访问一次系统,我就自增1,那么这个程序应该是这样写的,如下:
public class System {
private int count;
public int getCount(){
return count++;
}
}
这段代码,大家觉得有没有问题?
如果在单线程的模式下,没问题,但是在多线程的模式下,会有问题,为什么?
因为其中一个关键的步骤,count++不是一个原子操作,这个操作实际会分为三个步骤,
分别是,读取count值,给count值+1,将结果写到count值上。
那么大家想想,当我们有多个线程同时访问这个资源的时候,会出现什么情况?
这就是我们说的线程安全问题。
如果解决此类问题,java给我们提供一种同步机制,你只需要这么做就可以解决问题:
public synchronized int getCount(){
return count++;
}
好了,接下来,我们来说会出现的第二类问题
2,活跃性问题
比如,死锁,饥饿,活锁,这些都是属于活跃性问题。当某个操作无法继续执行时,这就是活跃性问题,我举个死锁的例子,比如我老婆跟我分工家务活,要求我先拖地,她就洗碗,而我要求她先洗碗,我再拖地,那么这个时候,我们彼此手握对方进行下一步工作的开启条件,但都彼此不愿意先动手,于是就陷入了僵局,这就是死锁。
换成程序的说法,就是A线程需要等待的锁被B线程占用,而B线程需要的等待的锁被A线程占用,所以相互都不释放,于是就陷入了死锁。
更多问题,我们后续继续讲解
3,性能问题
性能问题,你肯定会有疑问,多线程不就是为了提高系统的吞吐量吗?怎么还有性能问题?
这个原因很简单,还是那句话,你没有正确使用
下面我们来看看多线程可能带来的性能问题有哪些?
当我们采用多线程编程时,每个线程会由CPU实现调度,分配一定的时间片,那这个时候,调度器就会频繁挂起一个活跃的线程转而运行另一个线程,这就是我们说的“上下文切换”,这种操作将会带来极大的开销。
还有一个问题,当CPU重新切换回之前活跃的线程时,线程是否要继续之前未完成的操作,答案是必须的,那么就需要做一件事,在CPU切换的时候,保存线程的执行状态,这又是一笔开销。
最后,还有一种情况,就是多线程共享有状态的数据时,需要使用同步机制来保证数据安全,而这些机制会抑制编译器的某些优化,比如使得内存缓冲区中的数据失效等等,以及增加共享内存总线的同步流量
所以,有个重要的问题,一般我们的程序开启多少线程合适?
当然不是越多越好,大家可以留言,说说你的答案