第一章 并发编程的挑战
2020-04-21 本文已影响0人
巴巴11
The Art of Java Concurrency Programming.
从JDK source code、JVM、CPU等多角度分析。
并发编程遇到的挑战。会遇到的问题。如何解决。
目的:让程序运行的更快。
误区:并不是线程越多性能越好。(线程创建的开销,上下文切换的开销,死锁的问题,受限于硬件、网络带宽等软件资源问题)
上下文切换:
单核CPU或者多核都支持多线程执行任务,CPU为每个线程分配CPU时间片来执行。(时间片一般几十毫秒)(时间片分配算法)
CPU执行完一个时间片切换到另外一个时间片(切换前会保存上一个任务的状态)。
任务从保存到再加载的过程就是一次上下文切换。
如何减少上下文切换:
- 无锁并发编程方法。
多线程竞争锁,会引起上下文切换,可以避免使用锁,比如将数据的ID按照Hash算法取模分段。不同的线程处理不同段的数据。(concurrencyhashmap的原理)
- CAS算法。
Atomic包使用CAS算法来同步更新数据,不需要加锁。
- 使用最少的线程。
尽量避免创建更多的线程。
- 使用协程(golang)。
在单线程里实现多任务的调度。在单线程里维持多个任务的切换。
常用命令:
# jstack dump thread
$ sudo -u admin /opt/tools/java/bin/jstack 31777 > /home/wh/dump123
$ vi /home/wh/dump123
# 线程从Wating 到 Running状态都会进行一次上下文切换
死锁:
锁的使用不当可能会带来死锁问题。
避免死锁:
- 避免一个线程同时获得多个锁。
- 避免一个线程在锁内同时占用多个资源,尽量保证每个锁只占用一个资源。
- 尝试使用定时锁,使用lock.tryLock(timeout)来代替内部锁机制。
- 对于数据库锁,加锁和解锁必须在一个数据库连接里,否则会出现解锁失败的情况。
软件资源限制对并发的影响:
硬件资源限制有带宽的上传/下载速度、硬盘的读写速度、CPU的处理速度。
软件资源有数据库的连接数和socket连接数等。
如何解决资源限制的问题:
使用集群(搭建Hadoop集群,不同机器处理不同数据,数据ID%机器数得到机器编号)。
软件资源可以使用连接池技术和socket连接复用。
建议:
尽可能使用并发包提供的并发容器和工具类来解决问题。
多线程一定比单线程快吗?code:
package chapter1;
/**
* 多线程一定比单线程快吗?
*
* 在百万次规模内,多线程比串行要慢!!!
* 线程的创建开销;线程上下文切换的开销;
*
* Lmbench3可以测量上下文切换的时长
* vmstat可以测量上下文切换的次数
*/
public class ConcurrencyTest {
private static final long count = 10000l;
public static void main(String[] args) throws InterruptedException {
concurrency();
serial();
}
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);
}
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);
}
}
死锁code:
package chapter1;
/**
* 死锁问题
*/
public class DeadLock {
static String A = "A";
static String B = "B";
void deadLock() {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (A) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
synchronized (B) {
System.out.println("A");
}
}
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
synchronized (B) {
synchronized (A) {
System.out.println("B");
}
}
}
});
t1.start();
t2.start();
}
}