今天看一下juc 总结。
1.volatile 关键字
内存可见性问题:每个线程有独立的缓存,两个线程操作共享数据是时,彼此不可见。
如何解决可见性问题:
使用同步锁解决,但效率低。
volatile关键字:两个线程操作共享数据是时,彼此可见的。每次向主存更新,每次读取主存。
区别:volatile 不具备互斥性,不保证原子性。
2原子性问题
int i =1;i=i++;结果为1
i++ 分为三步操作:读,改,写。
操作流程:int temp = i; i = i + 1; i = temp;
多线程操作时有线程安全问题,使用原子变量解决。
3.原子变量-cas算法
jdk1.5提供常用原子变量
使用volatile保证可见性
使用cas(campare-and-swap)算法保证数据原子性
cas算法是硬件对并发操作共享数据的支持。
cas包含3个操作数:
内存值 V(线程中的值)
预估值 A(主存中的直)
更新直 B
当且仅当V==A时 V=B,否则将不做任何操操作。
4.concurrentHashMap
hashMap 线程不安全,hashTable 线程安全,效率低。
java 1.5 采用“锁分段机制”, 默认有16个segment,每个段是一个链表。
jdk 1.8之后,concurrentHashMap 底层采用CAS算法。
此包还提供了设计用于多线程上下文中的 Collection 实现:
ConcurrentHashMap、ConcurrentSkipListMap、ConcurrentSkipListSet、CopyOnWriteArrayList 和 CopyOnWriteArraySet。
当期望许多线程访问一个给定 collection 时,ConcurrentHashMap 通常优于同步的 HashMap,ConcurrentSkipListMap 通常优于同步的 TreeMap。
当期望的读数和遍历远远大于列表的更新数时,CopyOnWriteArrayList 优于同步的 ArrayList。
5.CountDownLatch -闭锁
Java 5.0 在 java.util.concurrent 包中提供了多种并发容器类来改进同步容器的性能。
CountDownLatch 一个同步辅助类,在完成一组正在其他线程中执行的操作之前,它允许一个或多个线程一直等待。
6.实现callable接口
创建线程的方式有四种:继承thread,实现runnable,实现callable,线程池
Java 5.0 在 java.util.concurrent 提供了一个新的创建执行线程的方式。
Callable 接口类似于 Runnable,但是 Runnable 不会返回结果,并且无法抛出经过检查的异常。
Callable 需要依赖FutureTask 用来接收线程结果,FutureTask 也可以用作闭锁。
7.Lock 同步锁
解决多线程安全问题3种方法:
同步代码块: 隐式锁
同步方法:隐式锁
同步锁 :jdk1.5后出现,是一种显示锁,lock上锁,unlock解锁。
在 Java 5.0 之前,协调共享对象的访问时可以使用的机制只有 synchronized 和 volatile 。Java 5.0 后增加了一些新的机制,但并不是一种替代内置锁的方法,而是当内置锁不适用时,作为一种可选择的高级功能。
ReentrantLock 实现了 Lock 接口,并提供了与synchronized 相同的互斥性和内存可见性。但相较于synchronized 提供了更高的处理锁的灵活性。
使用lock 完成等待唤醒机制:使用wait和notify方法,为了避免虚假唤醒,wait方法通常在while循环里。
8.condition
在 Condition 对象中,与 wait、notify 和 notifyAll 方法对应的分别是await、signal 和 signalAll。
Condition 实例实质上被绑定到一个锁上。要为特定 Lock 实例获得Condition 实例,请使用其 newCondition() 方法。
与lock一起使用,实现wait和notify功能
与lock一起可以实现sync +wait、notify的线程通信功能
9.readwritelock 读写锁
写写/读写 需要互斥,读读不需要互斥。
读锁是共享的,写锁是独占的
10.线程八锁
八种情况
1. 两个普通同步方法,两个线程,标准打印, 打印? //one two
2. 新增 Thread.sleep() 给 getOne() ,打印? //one two
3. 新增普通方法 getThree() , 打印? //three one two
4. 两个普通同步方法,两个 Number 对象,打印? //two one
5. 修改 getOne() 为静态同步方法,打印? //two one
6. 修改两个方法均为静态同步方法,一个 Number 对象? //one two
7. 一个静态同步方法,一个非静态同步方法,两个 Number 对象? //two one
8. 两个静态同步方法,两个 Number 对象? //one two
结论:
非静态同步方法的锁是this,静态同步方法的锁是Class实例。
同一个类中,多个同步方法,多个线程操作时,同一时刻只有一个线程得到锁。
11.线程池和线程调度
为什么需要线程池:防止频繁创建、销毁线程
线程池提供一个线程队列,队列中保存所有等待状态的线程。
线程池体系结构:
java.util.concurrent.Executor 是负责线程使用和调度的根接口
|--ExecutorService 子接口 线程池的主要接口
|--ThreadPoolExecutor 线程池实现类
|--ScheduledExecutorService 子接口 负责线程调度
|--ScheduledThreadPoolExecutor 继承ThreadPoolExecutor,实现ScheduledExecutorService。
工具类 Executors
Executors.newCacheThreadPool 缓存线程池,线程池的数量不固定,可以根据需求自动的更改数量。
Executors.newFixedThreadPool 固定大小
Executors.newSingleThreadExecutor 一个线程
线程调度:Executors.newScheduledThreadPool 创建固定大小的线程池,延迟或定时执行任务。
13.ForkJoinPool 分支合并框架
Fork/Join 框架:就是在必要的情况下,将一个大任务,进行拆分(fork)成若干个小任务(拆到不可再拆时),再将一个个的小任务运算的结果进行 join 汇总。
为jdk1.7新增的功能
fork-join采用 “工作窃取”模式(work-stealing):当执行新的任务时它可以将其拆分分成更小的任务执行,并将小任务加 到线程队列中,如果一个线程队列任务执行完了,然后会从另一个随机线程的队列中偷一个并把它放在自己的队列中执行,增加执行效率。
与线程池的区别: 相对于一般的线程池实现,fork/join框架的优势体现在对其中包含的任务的处理方式上.在一般的线程池中,如果一个线程正在执行的任务由于某些原因无法继续运行,那么该线程会处于等待状态。而在fork/join框架实现中,如果某个子问题由于等待另外一个子问题的完成而无法继续运行。那么处理该子问题的线程会主动寻找其他尚未运行的子问题来执行.这种方式减少了线程的等待时间,提高了性能。
实现方法:继承 RecursiveTask 有返回值,RecursiveAction没有返回值。