---Start
一、java中如何实现多线程?
1. 继承Thread类
重写run方法
run方法是线程体, 存放逻辑代码的位置
2. 实现Runnable 接口
3. 实现Callable接口------------main中用FutureTask接收call方法返回的值
4. 线程池(有的公司不用线程池)
二、线程的生命周期如下图
三、
同步锁:synchronized关键字------自动释放锁
JUC提供的Lock更灵活------手动释放锁等
同步代码块和同步方法,
线程之间的通信:wait,notify notifyAll
JUC提供的 Await,signal,signalAll
死锁-------哲学家就餐问题
虚假唤醒-------------解决-----------把wait方法 放到循环中while
守护线程
所有的用户线程完成任务后 jvm 就自动关闭, 即使有守护线程运行它也结束,
如果守护线程任务结束,用户线程还有没结束的,jvm虚拟机不退出
守护线程就是一个贴了标签的普通线程,处理一些无关紧要的任务
JUC提高并发功能----------java.util.concurrent
Lock接口
lock();上锁
unlock(); 释放锁
Lock lock = new ReentrantLock(); 非公平锁-------------synchronized也是非公平锁
Lock lock = new ReentrantLock(true); 公平锁
Condition condition = lock.newCondition();
condition.await(); 让线程进入到等待池
condition.signal(); 唤醒等待池中的任意线程
condition.signalAll(); 唤醒等待池中的所有线程,只有一个能抢到lock锁
线程池:
ExecutorService executorService = Executors.newSingleThreadExecutor();
无限制线程 Executors.newCachedThreadPool();
创建固定个数的线程,去执行任务 Executors.newFixedThreadPool(2);
定时任务 Executors.newScheduledThreadPool(10);
executorService.submit(); 可以执行Runnable,Callable接口实现的线程体
executorService.execute();可以执行Runnable的线程体 executorService.shutdown(); 关闭线程池
Thread中的sleep,join,yield区别?------思考一下,从以下几个方面来想
1、释放cpu资源吗,2、使用它们时,会让线程进入到生命周期的哪个部分?。
三、JUC包下的集合
写时复制技术: List接口: CopyOnWriteArrayList
Set接口: CopyOnWriteArraySet
Map接口: jdk1.7 分段锁机制: ConcurrentHashMap(); , 有限制
jdk1.8 数组 + 链表 + 红黑树 ,然后CAS + synchronized
不要搞混CAS和CAP哦
四、 线程八锁 -----------2种锁-----------8种现象
非静态锁--------------class反射的对象,new几个就几把锁,但要考虑static修饰的情况
静态锁--------static修饰的------全局就一把锁
五、JUC包下的三大辅助类
1.CountDownLatch 闭锁------------------替换 join 方法--------“-”减法
举个例子====集齐7个七龙珠后,才能召唤神龙
2. CyclicBarrier 循环阻塞类--也叫--循环栅栏 适用----一组线程的相互等待场景-----“+”加法
举个栗子--------某宝、某夕、某东,双十一双十二结束后,计算“数码区”,“汽车用品区”, “母婴品区”,它们各自的总流水账,以及它们三个总的流水账,那么比如数码区先获得cpu资源,先计算完后,得等待其他两个区计算结果后,才能计算“总”的流水账
3. Semaphore 信号量--------------适用 “限流” 场景
举个栗子=========6个车,抢3个车位
Semaphore semaphore = new Semaphore(3);
大家可以思考一下下面这个问题
六、 读写锁: Lock接口下的另一个实现类:
Lock:
ReetrantLock: 公平锁 , 非公平锁, 可重入锁
ReentrantReadWriteLock().writeLock(); 写锁
ReentrantReadWriteLock().readLock(); 读锁
写锁的优先级永远大于读锁
写互斥, 读共享的
七、Volitale 关键字:----------相关的名词: 内存,主存 线程
内存可见性问题:
Volatile: 保证数据可见性 保证不了原子性
synchronized : 每次加载同步代码块,同步方法数据时,都会强制刷新数据的所在位置
保证数据是最新的(数据可见)
Volatile + CAS 算法 ==== synchronized
多个线程对主存中的数据进行读取操作时,有些线程对内存中的数据更新不知道,导致数据脏读
为了解决这个问题,我们可以使用Volitale 关键字, 或 synchronized ,强制刷新内存,
让其它线程是被到被更新的数据,这就是内存可见性问题
但是Volitale 关键字能保证内存可见,保证不了原子性
如何弥补 Volatile 的原子操作,需要借助 CAS 算法:
CAS = Compare And Swap
八、AtomicStampedReference--------原子引用类----------无锁的线程安全操作====Volatile+CAS
**********************************
底层维护了一个 Volitale关键字修饰的value值,
方法中使用的时Unsafe的 CompareAndSwap方法,
九、BlockingQueue 阻塞队列
首先要了解:
Queue
Deque
Queue: 提供了6个方法
有异常的, 返回特殊值的 null
add(); offer
element() peek
remove(); poll()
Deque: 提供了2组方法
有异常的, 返回特殊值的:
add offer();
addFirst(); offerLast();
addLast(); offerFirst();
element(); peek();
getFirst(); peekFirst();
getLast(); peekLast();
remove(); poll();
removeFirst() pollFirst();
removeLast(); pollLast();
还提供了 push , pop 可以当作栈结构来用
juc:
BlockingQueue:
1. ArrayBlockingQueue:
put(); 向队列中添加元素 ,添加不进去,阻塞
take(); 从队列的头获取元素,获取不到怎么办 阻塞
2. LinkedBlockingQueue
基于链表结构实现的单向的阻塞队列
put(); 向队列中添加元素 ,添加不进去,阻塞
take(); 从队列的头获取元素,获取不到怎么办 阻塞
3. LinkedBlockingDeque
基于链表结构实现的一个双端阻塞队列
put(); 向队列中添加元素 ,添加不进去,阻塞
take(); 从队列的头获取元素,获取不到怎么办 阻塞
10、Fork/Join 分支合并
Fork 拆分: 把大事化小
Join 合并: 把小事儿 聚集到一起,整合
把一个大的任务,拆分成若干个小份 ,然后处理,最后在整合
ForkJoinTask: 拆分合并框架 ,任务类
1. RecursiveAction : 用于没有返回结果的任务,
2. RecursiveTask : 用于有返回结果的任务 例如: 1-----100000 的和
我们的任务类,必须继承上面的工具类,并重写compute方法
初始规则我们自定义
然后要调用fork 拆分
拆分后, 在调用 join 合并
测试类需要使用
ForkJoinPool: 拆分合并池
执行submit方法,提交,提交后get(); 返回