Java并发编程的艺术(读书笔记)
第一章 并发编程的挑战(0312)
即使是单核处理器也支持多线程执行代码,CPU会给每个线程分配CPU时间片来实现这个机制,CPU不断地切换线程执行
上下文切换
: 任务从上一个保存到再次加载这个任务的过程就是一次上下文切换
并发
不一定比串行
快,因为线程有创建和上下文切换的开销
Lmbench3可以测量上下文切换的时长
vmstat可以测量上下文切换的次数
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 41 42 43 44 45 46 47
| public class ConcurrentTest { private static final Long count=100000000L;
public static void main(String[] args) throws InterruptedException { concurrency(); serial(); }
private static void serial() { long start = System.currentTimeMillis(); int a = 0; for (int i = 0; i < count; i++) { a+=5; } int b = 0; for (int i = 0; i < count; i++) { b--; } long time=System.currentTimeMillis()-start; System.out.println("serial:"+time+"ms,b="+b); }
private static void concurrency() throws InterruptedException{ long start = System.currentTimeMillis();
Thread thread = new Thread(new Runnable() { @Override public void run() { int a = 0; for (int i = 0; i < count; i++) { a += 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+"ms,b="+b); }
}
|
如何减少上下文切换
无锁并发编程
多线程竞争锁,引起上下文切换. 所以多线程处理数据时候可以使用方法避免使用锁
eg: 将数据的ID按照Hash算法取模分段,不同的线程处理不同段的数据
CAS算法:
Java的Atomic包使用CAS算法来更新数据,而不需要加锁
使用最少线程
避免创建不需要的线程
协程
在单线程里实现多任务的调度,维持多个任务的切换
死锁
- 避免同一个线程同时获取多把锁
- 避免一个线程在锁内同时占用多个 资源,尽量保证每个锁只占用一个资源
- 尝试使用定时锁,使用lock.tryLock(timeout)来替代使用内部锁机制
- 对于数据库锁,加锁和解锁必须在同一个数据库连接里,否则会出现解锁失败的情况
资源限制
程序的执行速度受限于计算机硬件资源或软件资源
解决:
- 硬件资源限制: 集群并行执行
- 软件资源限制: 资源复用 eg:线程池将数据库和Socket连接复用
第二章 并发机制的底层实现原理
volatile的应用
volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的可见性.可见性的意思就是当一个线程修改一个共享变量时候,另外一个线程能读到这个修改的值.
如果volatile变量修饰符使用恰当的话,它比synchronized的使用和执行成本更低,因为他不会引起上下文的切换和调度