第一章 并发编程的挑战
第1章 并发编程挑战
并发编程的目的是为了使程序运行更快,但是,并不意味着启动更多线程就可让程序最大限度并发执行
1.1 上下文切换
CPU为不同线程分配不同的时间块来支持多线程执行代码(类似于时分复用),但是在切换到下一个任务之前,会将现在这个任务状态保存,以便下次可以再加载这个任务状态。任务从保存到再加载的过程就称为依次上下文切换
package com.chapter1;
import static java.lang.System.currentTimeMillis;
public class ConcurrentTest {
private static long count = 100000000;
public static void main(String[] args) throws InterruptedException {
concurrency();
serial();
}
//并发计算结果
private static void concurrency() throws InterruptedException {
long start = currentTimeMillis();
//开启一个线程计算结果
Thread thread = new Thread(()->{
long sum = 0;
for (int i = 0; i < count; i++) {
sum = sum + 5;
}
});
thread.start();
//主线程循环减
int b = 0;
for (int i = 0; i < count; i++) {
b--;
}
//插队
thread.join();
long time = System.currentTimeMillis() - start;
System.out.println("concurrency:" + time);
}
//普通单线程计算
private static void serial() {
long start = System.currentTimeMillis();
long sum = 0;
for (int i = 0; i < count; i++) {
sum = sum + 5;
}
int b = 0;
for (int i = 0; i < count; i++) {
b--;
}
long time = System.currentTimeMillis() - start;
System.out.println("serial:" + time);
}
}
通过例子发现,随着count的值增加,多线程优势逐渐显现出来,在count较小时,所用时间大于单线程时间。经过分析发现,这是因为上下文切换带来的时间损耗。
1.2 减少上下文切换
减少上下文切换方法有:无锁并发编程,CAS算法,使用最少线程和使用协程
- 无锁并发编程:多个线程竞争锁时,会引发上下文切换,因此使用一些方法来避免使用锁
- CAS算法:Java的Atomic包使用CAS算法更新数据,不需要加锁
- 使用最少线程:即尽量少使用线程,避免创建过多不需要的线程
- 协程:在单线程中实现多任务的调度
1.3 死锁
死锁产生的四个必要条件:
- 互斥条件:一个资源被一个进程占用
- 请求与保持条件:一个资源请求对方资源陷入等待,另一个线程不释放资源
- 不剥夺条件:进程获得的资源在未释放之前不可被强行剥夺
- 循环等待条件:若干进程间形成循环等待关系
package com.chapter1;
import java.util.concurrent.TimeUnit;
public class DeadLockTest {
//两个资源
private static String A = "A";
private static String B = "B";
public static void main(String[] args) {
deadlock();
}
private static void deadlock() {
//开启t1线程
new Thread(()->{
synchronized (A) {
System.out.println(Thread.currentThread().getName() + "get A");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println(Thread.currentThread().getName() + "get B");
}
}
}, "t1").start();
//开启t2线程
new Thread(()->{
synchronized (B) {
System.out.println(Thread.currentThread().getName() + "get B");
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (A) {
System.out.println(Thread.currentThread().getName() + "get A");
}
}
}, "t2").start();
}
}
避免死锁的几个常用方法:
- 避免一个线程获得多个锁
- 避免一个线程在锁内占用多个资源,尽量保证每个锁仅占用一个资源
- 使用定时锁(lock.tryLock(timeout))来代替内部锁
- 对于数据库锁,加锁和解锁必须在同一个数据库连接中
1.4 资源限制问题
什么是资源限制
资源限制是指在并发编程中,程序执行速度受限于计算机硬件资源或软件资源
资源限制引发的问题
并发编程的原则是将代码中串行执行的部分编程并发执行。但由于资源限制问题,本质上还是串行,同时由于上下文切换和资源调度问题,时间反而会增加。
如何解决资源限制问题
硬件资源限制:使用集群并行执行程序。让程序在多个计算机上执行,比如使用ODPS,Hadoop搭建服务器集群
软件资源限制:使用资源池将资源复用。
资源限制下的并发编程
在资源限制下,根据不同的资源限制调整程序的并发度。

浙公网安备 33010602011771号