- 当线程被调用 interrupt()时,该线程再调用isInterruptted()会返回True,但如果在调用interrupt()当,由于线程正处于Sleep,此时将会抛出InterruptedException,并会重置interrupt状态,此时isInterruptted()会返回False。需要注意代码中如果是根据isInterruptted来判断的场景
- wait和sleep的区别:wait方法需要已取得临界区内的锁并在调用后会释放该锁,而sleep不需要事先取得锁,如果在sleep前已获得锁的话,也不会释放锁
- Semaphore : 使用信号量来控制允许有限的线程数去访问资源
- ReentrantReadWriteLock: 区分读写操作,读与读之间的线程不互斥, 读与写、写与写则会相互阻塞
- CountDownLatch: 倒数器,声明一个初始数值,当countDown方法被调用次数等于或超过这个初始数值时,所有在await的线程将被唤醒向下执行
- CyclicBarrier: 循环栅栏
CyclicBarrier barrier = new CyclicBarrier(2, ()->{
System.out.println(Thread.currentThread().getName()+": 合并任务开始");
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+": 合并任务结束");
});
Runnable worker = ()->{
System.out.println(Thread.currentThread().getName()+": 分布任务开始");
try {
barrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+": 分布任务结束");
};
new Thread(worker).start();
new Thread(worker).start();
输出:
Thread-0: 分布任务开始
Thread-1: 分布任务开始
Thread-1: 合并任务开始
Thread-1: 合并任务结束
Thread-1: 分布任务结束
Thread-0: 分布任务结束
小结:
当线程await数量达到栅栏声明的数量时,其将首先触发barrierAction执行任务,barrierAction任务是占用其中的一个线程在执行,并在执行结束后,将会唤醒其他的线程继续往下执行
- LockSupport
- park()方法在无需锁的情况下就可直接调用,其在线程栈中体现为WAITING (parking)。其也支持中断,不过其不需要抛出InterruptedException,而是立马结束park()方法的阻塞,后续可通过Thread.isInterrupted来判断其是否已被中断
- park(object),可传入一个对象,让其在线程栈中体现是在哪里被阻断了
- parkNanos(), parkUntil()方法可用于限时的等待
- 可通过线程的interrupt、unpark来唤醒park(), parkNanos(), parkUntil()
- 需要注意的是,对于已进入临界区后调用的park方法,其不会去释放锁资源
- 调度线程池
- Executors.newSingleThreadScheduledExecutor() 返回一个线程的线程池方法
- Executors.newScheduledThreadPool() 可指定线程数量的线程池方法
- ScheduledExecutorService.schedule(Runnable command,long delay, TimeUnit unit) 用于一次性的调度
- ScheduledExecutorService.scheduleAtFixedRate(Runnable command,long initialDelay,long period,TimeUnit unit); 指的是从上一次的任务开始后,间隔固定时间后,开始下一次的任务
- ScheduledExecutorService.scheduleWithFixedDelay(Runnable command,long initialDelay,long period,TimeUnit unit); 指的是从上一次的任务结束后,间隔固定时间后,开始下一次的任务
- 如果没有空闲的线程也无法保证经过固定时间后能启动任务。比如单个线程的线程池若同时跑两个任务,也只能其中一个任务结束后马上跑下一个任务
- 如果任务的执行时间超过调度时间的话,线程池不会让同一个任务同时跑两次,只有当上一次的调度结束后开始下一次的调度
- 当一个任务的异常没被处理,那么其后的同一个任务将不会再被调度
- Fork/Join框架
- RecursiveAction, 用于没返回值的任务
- RecursiveTask, 用于有返回值的任务
- 是一个将大任务切分成小任务来并发执行,并最终汇聚小任务的结果
- ForkJoinTask, 是一个执行任务,它提供在执行任务时,可选的fork方法来拆分成子任务和将子任务的结果汇聚的join方法。正常我们不需要直接继承这个类,则是使用它的两个子类
- ForkJoinPool, ForkJoinTask需要通过ForkJoinPool来执行,任务分割出的子任务会添加到当前工作线程所维护的双端队列中,进入队列的头部。当一个工作线程的队列里暂时没有任务时,它会随机从其他工作线程的队列的尾部获取一个任务。
- ConcurrentHashMap
- ConcurrentLinkedQueue
- CopyOnWriteArrayList
- ConcurrentSkipListMap, 跳表
- AtomicReference
- AtomicStampedReference, 需要同时满足Stampe和Reference时,才可set生效
- AtomicIntegerArray、AtomicLongArray、AtomicReferenceArray
- SynchronousQueue
- Disruptor, 高效的无锁环形队列
- 并行流水线:当任务间有依赖关系时,则无法使用并发算法,不过有折衷的方式,即一个线程(池)执行依赖的任务的结果传递给下一个的线程(池)去执行,每一个线程(池)会执行和他相关算法的大量任务,以此来完成并行方式
- CompletableFuture(JDK8新增)
- StampedLock(JDK新增),读写锁的改进