常用线程池
可重用固定线程集合的线程池,以共享的无界队列方式来运行这些线程
ExecutorService threadPool = Executors.newFixedThreadPool(3);// 创建可以容纳3个线程的线程池
根据需要创建新线程的线程池,但是在以前构造的线程可用时将重用它们
ExecutorService threadPool = Executors.newCachedThreadPool();// 线程池的大小会根据执行的任务数动态分配
使用单个 worker 线程的 Executor,以无界队列方式来运行该线程
ExecutorService threadPool = Executors.newSingleThreadExecutor();// 创建单个线程的线程池,如果当前线程在执行任务时突然中断,则会创建一个新的线程替代它继续执行任务
可安排在给定延迟后运行命令或者定期地执行的线程池
ScheduledExecutorService threadPool = Executors.newScheduledThreadPool(3);// 效果类似于Timer定时器
JVM内部同步机制和JVM外部同步机制
内部同步机制
- static的强制同步机制
- synchronized的同步机制:可以使用this、类字面常亮、特有锁对象(专门为加锁使用,无实际意义的对象,此种方式效率更高。)
外部同步机制
ReentrantLock(可重入的锁)
- synchronized关键字同步的时候,等待的线程将无法控制,只能死等。
解决方式:ReentrantLock可以使用tryLock(timeout, unit)方法去控制等待获得锁的时间,也可以使用无参数的tryLock方法立即返回,这就避免了死锁出现的可能性。
- synchronized关键字同步的时候,不保证公平性,因此会有线程插队的现象。
解决方式:ReentrantLock可以使用构造方法ReentrantLock(fair)来强制使用公平模式,这样就可以保证线程获得锁的顺序是按照等待的顺序进行的,而synchronized进行同步的时候,是默认非公平模式的,但JVM可以很好的保证线程不被饿死。
ReentrantLock有这样一些优点,当然也有不足的地方。最主要不足的一点,就是ReentrantLock需要开发人员手动释放锁,并且必须在finally块中释放。
JVM内部条件控制机制和JVM外部条件控制机制
JVM内部条件控制机制
在Object中,有一个wait的本地方法;它可以用来协调线程之间的协作。
wait一般情况下最常用的场景是构造一个花销非常大的对象的时候,比如JDK动态代理在生成代理类的时候就使用了这种方式。JDK6在生成一个代理类之前,会先检测一个是否正在生成中的标识,如果正在生成的话,JDK6就会在对象上等待,直到正在生成的代理类生成完毕,然后直接从缓存中获取。
wait,notify和notifyAll方法在使用前,必须获取到当前对象的锁,否则会告诉你非法的监控状态异常。还有一点,则是如果有多个线程在wait等待,那么调用notify会随机通知其中一个线程,而不会按照顺序通知。换句话说,notify的通知机制是非公平的,notify并不保证先调用wait方法的线程优先被唤醒。notifyAll方法则不存在这个问题,它将通知所有处于wait等待的线程。
JVM外部条件控制机制
JDK的类库中,有这样的一个类Condition,来弥补wait方法本身的不足。
- wait方法当使用带参数的方法wait(timeout)或者wait(timeout,nanos)时,无法反馈究竟是被唤醒还是到达了等待时间,大部分时候,我们会使用循环(就像上面的例子一样)来检测是否达到了条件。
解决方式:Condition可以使用返回值标识是否达到了超时时间。
- 由于wait,notify,notifyAll方法都需要获得当前对象的锁,因此当出现多个条件等待时,则需要依次获得多个对象的锁,这是非常恶心麻烦且繁琐的事情。
解决方式:Condition之需要获得Lock的锁即可,一个Lock可以拥有多个条件。
JVM外部 线程协作
CountDownLatch
这个类是为了帮助猿友们方便的实现一个这样的场景,就是某一个线程需要等待其它若干个线程完成某件事以后才能继续进行。
CyclicBarrier
这个类是为了帮助猿友们方便的实现多个线程一起启动的场景,就像赛跑一样,只要大家都准备好了,那就开始一起冲。比如下面这个程序,所有的线程都准备好了,才会一起开始执行。
Semaphore
这个类是为了帮助猿友们方便的实现控制数量的场景,可以是线程数量或者任务数量等等。
Exchanger
这个类是为了帮助猿友们方便的实现两个线程交换数据的场景,使用起来非常简单。
参考文档: