本系列博文,自圆其说之——聊聊并发,是希望自己在学习并发的过程中,建立起一套属于自己的见解和知识体系,并能对其中的各种姿势能自圆其说,做到知其言,并知其所以言。
首先,我们要学习并发编程的目的,是为了要让程序跑得更加的快,但是在并发中,我们常常会面临,cpu的上下文切换,死锁,硬件和软件资源的限制问题,我们应该如何应对这些难点呢?
上下文切换
上过操作系统的课,我们都知道在单核的cpu中,我们的操作系统在进行任务切换的时候,是需要时间的,cpu处理器是通过时间轮片来实现这个机制。
CPU通过时间片分配算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务,但是,在切换前会保存上一个任务状态,以便下次切换回这个任务,在单核处理器中,并发其实也是串行执行,知识让人感觉是并行处理罢了。
测试上下文切换次数和时长
-
- 使用Lmbench3 可以测量上下文切换的次数。
-
- 使用vmstat 可以测量上下文切换的次数。
如何减少上下文切换
减少上下文切换的方法有 (1)无锁并发编程、(2) CAS算法、(3)使用最少线程和使用协程。
* 无锁并发编程。多线程竞争锁时,会引起上下文的切换,所以多线程处理数据时,可以用一些办法来避免使用锁,如将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据。
* CAS算法。Java的Atomic包使用CAS算法来更新数据,而不需要加锁。
* 使用最少线程。避免创建不需要的线程,比如任务很少,但创建很多线程来处理,这样会造成大量的线程都处于等待状态。
* 协程:在单线程里实现多任务的调度,并在单线程里维持多个任务间的切换
死锁
现在我们介绍避免死锁的几个长见方法:
* 避免一个线程同时获取对歌锁。
* 避免一个线程在锁内同时占用多个资源,尽量保证每一锁只是占用一个资源。
* 尝试使用定时锁,使用lock.tryLock(timeout)来代替使用内部的锁机制。
* 对于使用数据库锁,加锁和解锁必须在一个数据库连接里面,否则会出现解锁失败的情况。
public class DeadLockTest {
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new DeadLockTest().deadLock();
}
private void deadLock()
{
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
Thread.currentThread().sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.print("1");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
try {
Thread.currentThread().sleep(1000);
}catch(InterruptedException e) {
e.printStackTrace();
}
synchronized (A) {
System.out.print("1");
}
}
}
});
}
}
资源限制挑战
1)资源限制是指在进行并发编程时,计算机执行的速度受限于计算机的硬件资源和软件资源以及网络速度等。例如:服务器的带宽只有2Mb/s , 某个资源的下载速度是1Mb/s, 系统启动10个线程下载,也不见的有多么的快,速度也不会变成10Mb/s, 所以在进行并发编程的时候,要考虑这些资源限制。RPC的数据传输会受到带宽的影响,硬盘读写速度和CPU的上下文的切换会受到处理器的性能影响。还有数据库的连接数和socket连接数的影响等。
2)资源限制引发的问题
在并发编程中,将代码执行速度加快的原则是将代码的串行执行的部分变成并发执行。但是因为受限于资源,并发并不会加快,反而更加的慢,因为这样就增加了cpu的上下文切换和任务调度的花销了。
3)如何解决资源限制的问题
对于硬件资源的限制,可以考虑使用集群的方式,这也是业界常用的方案。既然单机有资源的限制,那么就在多机上执行。比如使用ODPS, Hadoop或者自己搭建服务器集群。不同的机器处理不同的数据。可以通过“数据ID%机器数”,计算出机器的编号,然后由这台机器来处理这笔数据,常见的如订单系统。
4)在资源限制的情况下进行并发编程
如何在资源限制地情况下,让程序执行的更加的快速?那就要根据资源的限制,调整并发度。