上下文切换:
· 首先即使是单核处理器也支持多线程的执行代码,CPU通过给每个线程分配CPU时间片来实现这个机制。
· CPU通过分配时间片算法来循环执行任务,当前任务执行一个时间片后会切换到下一个任务。但是在切换之前会保存上一个任务的状态,以便在次执行回这个任务时,可以再加载这个任务的状态。
简单说:就是几个线程在争夺CPU资源的过程就是上下文切换。
多线程就一定快吗?
Q: 并发执行一定比串行快吗?回答时不一定,当并发操作累计执行不超过百万次时,速度会比串行执行要慢。为什么呢?
A: 因为,线程有创建和上下文切换的开销。并发的情况下,上下文切换和创建的开销会变得更大,所以再一定的数量级下的并发操作效率其实是不高的。
就好比你现在要打印你自己写的一本书,而且你只要十本,那么你回去一个普通的打印店打印呢?(假设普通的打印店也可以把你的书做的很完美)还是会去一个印刷厂呢?你肯定会去那么普通的打印店么,除非你钱多,或者打印厂是你们自己家的!为什么呢?因为,1.打印店便宜;2.打印店快 3.印刷厂贵;(可能当印刷厂的那些大型的机器启动起来了,你的十本书就已经好了)
死锁
锁虽然是一个有用的工具,但是,再使用它的时候同样会带来一些困扰,那就是可能产生死锁问题,一旦产生死锁问题,就造成系统功能的不可用。
下面的代码就是一个经典的死锁的例子:
public class DeadLockDemo {
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
new DeadLockDemo().deadLock();
}
public void deadLock() {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
Thread.currentThread().sleep(20000); // 拿到A锁后睡眠 过程中不会释放锁
}catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) { // 等待线程2释放B锁
System.out.println("1");
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) { // 拿到B锁
synchronized (A) { // 等待线程1释放A锁
System.out.println("2");
}
}
}
});
thread1.start();
thread2.start();
}
}
上面的代码行后就会出现一个问题:thread1和thread2互相等待对方释放锁对象,然后出现了死锁的问题。
避免死锁问题的几个常见的方法:
1. 避免一个线程同时获得多个锁
2. 避免一个线程再锁内同时占用等多个资源,尽量保证每个锁只占用一个资源
3. 尝试使用定时锁,使用 lock.tryLock(timeout) 来代替使用内部锁机制(后面会详细分析定时锁)
4. 对于数据库锁,加锁和解锁必须在一个数据库连接中,否则会出现解锁失败的情况
资源限制问题对并发编程的影响:
1. 哪些方面的资源限制会影响到并发编程?
计算机的硬件资源或者计算机的软件资源,硬件资源限制有宽带的上传/下载速度,硬盘读写速度和CPU的处理速度。软件资源的额限制有:数据库的连接数和socket的连接数等等。
2. 这些资源的限制会引发的问题?
在并发编程中,将代码执行速度变快的原则是将原来的串行执行部分改为了并行执行,但是假如我们进行更改之后,由于资源限制问题,该代码还是在串行执行,那么这样相比之前效率非但没有增加反而是下降了,因为 在并行执行的过程中增加了创建线程和上下文切换的额外开支。
3. 怎么解决这个问题?
对于硬件限制:我们可以考虑使用集群并行执行程序。既然单机的资源有限,那么就让程序在多机上跑。
对于软件限制:我们可以考虑使用资源池将资源复用。