1、应用线程的一些教训
-
设置名称
无论何种形式,启动一个线程,就要给它一个名字!这对排错诊断系统监控有帮忙。否则诊断问题时,无奈直观晓得某个线程的用处。
-
响应中断
程序应该对线程中断作出失当的响应。
-
应用 ThreadLocal
它的功能非常简单,就是为每一个应用该变量的线程都提供一个变量值的正本,是每一个线程都能够独立地扭转本人的正本,而不会和其它线程的正本抵触。从线程的角度看,就如同每一个线程都齐全领有该变量。
留神:应用 ThreadLocal,个别都是申明在动态变量中,如果一直的创立 ThreadLocal 而且没有调用其 remove 办法,将会导致内存泄露。
2、Executor:ExecutorService 和 Future
为了不便并发执行工作,呈现了一种专门用来执行工作的实现,也就是 Executor。由此,工作提交者不须要再创立治理线程,应用更不便,也缩小了开销。
java.util.concurrent.Executors 是 Executor 的工厂类,通过 Executors 能够创立你所须要的 Executor。
ExecutorService executor = Executors.newSingleThreadExecutor();
Callable<Object> task = new Callable < Object > () {public Object call () throws Exception {
Object result = "...";
return result;
}
} ;
Future<Object> future = executor.submit(task);
future.get();
有两种工作:Runnable、Callable,Callable 是须要返回值的工作。
Task Submitter 把工作提交给 Executor 执行,他们之间须要一种通信伎俩,这种伎俩的具体实现,通常叫做 Future。Future 通常包含 get(阻塞至工作实现),cancel,get(timeout)(期待一段时间)等等。Future 也用于异步变同步的场景。
3、阻塞队列: put 和 take、offer 和 poll、drainTo
阻塞队列,是一种罕用的并发数据结构,罕用于生产者 - 消费者模式。
在 Java 中,有三种罕用的阻塞队列:
ArrayBlockingQueue
LinkedBlockingQueue
SynchronousQueue
应用阻塞队列:
Monitor 的实践模型
4、ReentrantLock 和 Synchronized
Synchronized 是 Lock 的一种简化实现,一个 Lock 能够对应多个 Condition,而 synchronized 把 Lock 和 Condition 合并了,一个 synchronizedLock 只对应一个 Condition,能够说 Synchronized 是 Lock 的简化版本。
在 JDK5,Synchronized 要比 Lock 慢很多,然而在 JDK6 中,它们的效率差不多。
5、Lock-free 算法
LockFree 算法,不须要加锁。
通常都是三个局部组成:
①循环
②CAS (CompareAndSet)
③break
进一步应用 Lock-Free 数据结构
class BeanManager {private ConcurrentMap<String, Object> map = new ConcurrentHashMap<String, Object>();
public Object getBean
(String key) {Object bean = map.get(key);
if (bean == null) {map.putIfAbsent(key, createBean());
bean = map.get(key);
}
return bean;
}
}
ConcurrentHashMap 并没有实现 Lock-Free,只是应用了拆散锁的方法使得可能反对多个 Writer 并发。ConcurrentHashMap 须要应用更多的内存。
同样的思路用于更新数据库 - 乐观锁。
Lock-Free 算法,能够说是乐观锁,如果非强烈竞争的时候,不须要应用锁,从而开销更小,速度更快。
适当应用 CopyOnWriteArrayList,可能进步读操作时的效率。
6、对于锁应用的教训介绍
应用反对 CAS 的数据结构,防止应用锁,如:AtomicXXX、ConcurrentMap、CopyOnWriteList、ConcurrentLinkedQueue
肯定要应用锁的时候,留神取得锁的程序,相同程序取得锁,就容易产生死锁。
死锁常常是无奈完全避免的,鸵鸟策略被很多根底框架所采纳。
通过 Dump 线程的 StackTrace,例如 linux 下执行命令 kill -3 <pid>,或者 jstack–l <pid>,或者应用 Jconsole 连贯下来查看线程的 StackTrace,由此来诊断死锁问题。
内部锁常被忽视而导致死锁,例如数据库的锁
7、并发流程管制伎俩:CountDownlatch、Barrier
并发流程管制 - 应用 CoutDownLatch
final int COUNT = 10;
final CountDownLatch completeLatch = new CountDownLatch(COUNT);
for (int i = 0; i < COUNT; ++i) {Thread thread = new Thread("worker thread" + i) {public void run() {
// do xxxx
completeLatch.countDown();}
};
thread.start();}
// 当你启动了一个线程,你须要等它执行完结,此时,CountDownLatch 兴许是一个很好的抉择。completeLatch.await();
final CountDownLatch startLatch = new CountDownLatch(1);
for (int i = 0; i < 10; ++i) {Thread thread = new Thread("worker thread" + i) {public void run() {
try {
// 当你启动很多线程,你须要这些线程等到告诉后才真正开始,CountDownLatch 兴许是一个很好的抉择。startLatch.await();} catch (InterruptedException e) {return;}
// do xxxx
}
};
thread.start();}
// do xxx
startLatch.countDown();
Barrier:
屏障是一种协调机制(一种算法),它迫使参加并发(或分布式)算法的过程期待,直到它们中的每一个都达到其程序中的某个点。这些协调点的汇合被称为屏障。一旦所有流程都达到了阻碍,它们都能够持续通过阻碍。
8、定时器
应用定时器 ScheduledExecutorService
java.util.concurrent.Executors 是 ScheduledExecutorService 的工厂类,通过 Executors,你能够创立你所须要的 ScheduledExecutorService。
JDK 1.5 之后有了 ScheduledExecutorService,不倡议你再应用 java.util.Timer,因为它无论性能性能都不如 ScheduledExecutorService。
大规模定时器 TimerWheel
存在一种算法 TimerWheel,实用于大规模的定时器实现。这个算法最早是被设计用来实现 BSD 内核中定时器的,起初被宽泛移植到诸如 ACE 等框架中,堪称 BSD 中经典算法之一,能针对定时器的各类常见操作提供靠近常数工夫的响应,且能依据须要很容易进行扩大。
9、并发三大定律:Amdahl、Gustafson、Sun-Ni
Amdahl 定律
Gene Amdahl 发现在计算机体系架构设计过程中,某个部件的优化对整个架构的优化和改善是有下限的。这个发现起初成为出名的 Amdahl 定律。
Gustafson 定律
Gustafson 假如随着处理器个数的减少,并行与串行的计算总量也是能够减少的。Gustafson 定律认为减速系数简直跟处理器个数成正比,如果现实情况合乎 Gustafson 定律的假如前提的话,那么软件的性能将能够随着解决个数的减少而减少。
Sun-Ni 定律
充分利用存储空间等计算资源,尽量增大问题规模以产生更好 / 更准确的解。