1.并发编程概述
1.1并发编程目的
并发编程的目的
提高用户体验,提高程序效率
对单核处理器,并发编程使程序在逻辑上同时发生。
对多核处理器,除了逻辑上同时发生外,还可能使程序并行,提升程序效率。
并发与并行
并发:同时开始
并行:同时进行
关系:并行是并发的子集
其他并发并行的解释:
-
并发:一个处理器同时处理多个任务。
-
并行:多个处理器或者是多核的处理器同时处理多个不同的任务.
前者是逻辑上的同时发生(simultaneous),而后者是物理上的同时发生.
1.2并发编程的线程开销问题
线程开销包括:
-
上下文切换开销
1.2.1线程创建开销
线程创建开销演示
[CODE]:multi-thread-playground/multithread.artConcurrencyProgramming.chapter01.ConcurrencyTest
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;
}
System.out.println(a);
}
});
thread.start();
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
thread.join();
long time = System.currentTimeMillis() - start;
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);
}
}
创建消耗数据统计
测试机器性能
MacOs 10.12.3 2.2GHz Intel Core i7 16GB
count(循环次数) | serial(ms) | concurrency(ms) |
---|---|---|
1w | 0 | 2 |
10w | 3 | 4 |
100w | 6 | 5 |
1000w | 19 | 12 |
总结:线程创建的开销需要考虑在内
1.2.2上下文切换开销
上下文切换开销演示
public class ConcurrencySwitchTest {
/** 执行次数 */
private static final long count = 10000000l;
/** 任务数*/
static final int taskNum = 50;
static final int threadPoolNum = 6;
static ExecutorService executor = Executors.newFixedThreadPool(threadPoolNum);
public static void main(String[] args) throws InterruptedException {
//并发计算
concurrency();
}
private static void concurrency() throws InterruptedException {
long start = System.currentTimeMillis();
List<Thread> threads = new ArrayList<Thread>();
for(int i=0;i<taskNum;i++) {
final int j = i;
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
if(j%2==0) {
add();
}else{
minus();
}
}
});
threads.add(thread);
}
for (Thread t: threads) {
executor.execute(t);
}
executor.shutdown();
// Wait until all threads are finish
executor.awaitTermination(100, TimeUnit.SECONDS);
long time = System.currentTimeMillis() - start;
System.out.println("concurrency :" + time + "ms");
}
private static void add(){
int a = 0;
for (long i = 0; i < count; i++) {
a += 5;
}
}
private static void minus(){
int b = 0;
for (long i = 0; i < count; i++) {
b--;
}
}
}
上下文切换数据统计
threadPoolNum | 耗时(ms) |
---|---|
2 | 102 |
3 | 71 |
4 | 65 |
5 | 82 |
10 | 103 |
20 | 141 |
总结:使用最小线程来减少切换开销,使用无锁并发编程方式[https://www.jianshu.com/p/88b5138ec027](如CAS),使用协程[http://www.paralleluniverse.co/quasar/]
1.3并发编程的并发问题
章2节的并发工具用于解决并发问题