第1章-并发编程的挑战

该文章主要介绍了多线程环境的上下文切换和死锁场景以及如何避免、解决死锁。

上下文切换

概念:任务从保存到再加载的过程就是一次上下文切换。在切换前会保存上一个任务的状态,以便下次切换回这个任务时,可以再加载这个任务的状态。

两个案例

多线程一定快吗?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
public class ConcurrencyTest {
private static final long count = 10000l;
public static void main(String[] args) throws InterruptedException {
concurrency();
serial();
}
private static void concurrency() throws InterruptedException {
long start = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
int a = 0;
for (long i = 0; i < count; i++) {
a += 5;
}
}
});
thread.start();
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
long time = System.currentTimeMillis() - start;
thread.join();
System.out.println("concurrency :" + time+"ms,b="+b);
}
private static void serial() {
long start = System.currentTimeMillis();
int a = 0;
for (long i = 0; i < count; i++) {
a += 5;
}
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
long time = System.currentTimeMillis() - start;
System.out.println("serial:" + time+"ms,b="+b+",a="+a);
}
}

结论:不一定,当并发执行累加操作不超过百万次时,速度会比串行执行累加操作要慢。
为什么并发执行慢:因为线程有创建和上下文切换的开销

测试上下文切换次数和时长

Alt text

死锁

是什么?

Alt text

产生原因

  • 系统资源不足
  • 进程间推进的顺序非法
  • 资源分配不当

    案例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    public class DeadLockDemo {
    privat static String A = "A";
    private static String B = "B";
    public static void main(String[] args) {
    new DeadLockDemo().deadLock();
    }
    private void deadLock() {
    Thread t1 = new Thread(new Runnable() {
    @Override
    publicvoid run() {
    synchronized (A) {
    try { Thread.currentThread().sleep(2000);
    } catch (InterruptedException e) {
    e.printStackTrace();
    }
    synchronized (B) {
    System.out.println("1");
    }
    }
    }
    });
    Thread t2 = new Thread(new Runnable() {
    @Override
    publicvoid run() {
    synchronized (B) {
    synchronized (A) {
    System.out.println("2");
    }
    }
    }
    });
    t1.start();
    t2.start();
    }
    }

如何避免死锁

  • 避免一个线程同时获取多个锁。
  • 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
  • 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制。
  • 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。

如何解决死锁

  • jps命令定位进程号(jsp -l)
  • jstack死锁查看(jstack 进程号)