JUC
1. 概念
- JUC: java.utl.concrrent
- 进程和线程
- wait和sleep
- 并发和并行
- 管程
- Monitor监视器,也称锁,是一种同步机制,保证同一时间,只有一个线程访问被保护数据或代码
- JVM同步资源的进入和退出,是用管程对象实现的
- 用户线程和守护线程:
- 自定义线程,
- 后台的线程,比如垃圾回收
- 主线程结束,自定义线程仍在,如果没有用户线程,只有守护线程,JVM结束
2. 两种实现多线程的方法
synchronized关键字
- wait()
- notifyAll()
lock实现类
- await()
- signalAll()
3. 多线程编程的步骤
- 创建资源类,在资源类创建属性和操作方法
- 在资源类操作方法:
- 判断
- 干活
- 通知
- 创建多个线程,调用资源类的操作方法
- 防止虚假唤醒,判断使用while
- wait是在哪里睡了,就在哪里醒来
- 虚假唤醒:一个线程在唤醒等待的线程后,有一部分满足条件,有一部分不满足条件(
唤醒同类型判断的其他线程
),不满足条件的被唤醒的线程属于虚假唤醒,解决方法是使用while循环判断是不是满足条件,这样不满足条件的线程就会再次等待。
4. 线程键的定制通信
普通的线程通信,只是把通知启动
定制通信,修改标志位,做到定制通知启动
- flag=1 线程A Condition1
- flag=2 线程B Condition2
- flag=3 线程C Condition3
5. 集合的线程安全
ArrayList线程不安全,解决方法:
使用vector
Collections.synchronizedList(new ArrayList<>())
CopyOnWriteArrayList 并发读,独立写。写时复制
HashSet线程不安全,解决方法:
- CopyOnWriteHashSet
HashMap线程不安全,解决方法:
- ConcurrentHashMap
6. 多线程锁
synchronized八种类型的锁:
## 公平锁 ,非公平锁
默认是非公平锁:会造成进一个线程活动,造成线程饿死现象,但效率高
公平锁:阳光普照,但效率相对低
可重入锁
可重入锁:外层钥匙可打开内部所有层的门
synchronoized 隐式可重入锁,隐式,自动完成上锁解锁
lock 显式可重入锁,手动声明上锁解锁
死锁
什么是死锁: 两个或两个以上的线程在执行过程中,因为争夺资源而造成一种互相等待的现象,如果没有外力支持,他们无法再执行下去。
产生死锁的原因:
- 系统资源不足
- 进程运行推进顺序不合适
- 资源分配不当
验证是否是死锁
- jps
- jstack jvm自带的堆栈跟踪工具
手写死锁示例:
public class DeadLock {
static Object a = new Object();
static Object b = new Object();
public static void main(String[] args) {
new Thread(() -> {
synchronized (a) {
System.out.println(Thread.currentThread().getName() + " 持有锁a,试图获取锁b");
synchronized (b) {
System.out.println(Thread.currentThread().getName() + " 获取锁b");
}
}
},"A").start();
new Thread(() -> {
synchronized (b) {
System.out.println(Thread.currentThread().getName() + " 持有锁b,试图获取锁a");
synchronized (a) {
System.out.println(Thread.currentThread().getName() + " 获取锁a");
}
}
},"B").start();
}
}
7. Callable接口
创建线程的4种方式:
1. 继承Thread类
1. 实现Runnable接口
1. 实现Callable接口
1. 线程池
Runnale,线程终止时无法获取返回结果,因此出现了Callable接口
Callable,Runnable的区别:
- 是否有返回值
- 是否抛出异常
- 实现方法不同,void run() V call()
使用calllable接口,runnable接口有实现类FutureTask,此类构造方法可以传递callable参数
new Thread(new FutureTask(new MyThread2()),"BB").start();
FutureTask<Integer> futureTask2 = new FutureTask<>(()->{
System.out.println(Thread.currentThread().getName()+" come in callable");
return 1024;
});
8. JUC强大的辅助类
1. CountDownLatch 减少计数
设置初始值,每次调用会减一,执行等待,当计数器值为0时运行
例子:6个同学离开教室后,才会锁门。
public static void main(String[] args) throws InterruptedException {
CountDownLatch countDownLatch = new CountDownLatch(6);
// 6个同学陆续离开教师
for (int i = 0; i < 6; i++) {
new Thread(() -> {
countDownLatch.countDown();
System.out.println(Thread.currentThread().getName() + "号同学离开了教室");
},String.valueOf(i)).start();
}
countDownLatch.await();
System.out.println(Thread.currentThread().getName() + "班长锁门");
}
2. CyclicBarrier 循环栅栏
一个同步辅助类,它允许一组线程互相等待,直到达某个公共屏障点。
例子:集齐七颗龙珠就可以召唤神龙
// 创建固定值
private static final int NUM = 7;
public static void main(String[] args) {
// 创建CyclicBarrier
CyclicBarrier cyclicBarrier =
new CyclicBarrier(NUM,()->{
// 未达到7不会执行,一致等待,等达到7后执行下面语句
System.out.println("集群7颗龙珠就可以召唤神龙");
});
// 集齐7颗龙珠的过程
for (int i = 1; i <= 7; i++) {
new Thread(()->{
try {
System.out.println(Thread.currentThread().getName()+"星龙珠被收集到了");
// 等待
cyclicBarrier.await();
} catch (InterruptedException e) {
throw new RuntimeException(e);
} catch (BrokenBarrierException e) {
throw new RuntimeException(e);
}
},String.valueOf(i)).start();
}
}
3. Semaphore 信号灯
一个计数信号量。信号量维护一个许可集
例子:6辆汽车,停3个车位
//6辆汽车,停3个车位
public static void main(String[] args) {
// 创建Semaphore,设置许可数量
Semaphore semaphore = new Semaphore(3);
// 模拟6辆汽车
for (int i = 1; i <= 6 ; i++) {
new Thread(() -> {
try {
//抢占
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + " 抢到了车位");
//设置随机停车时间
TimeUnit.SECONDS.sleep(new Random().nextInt(5));
System.out.println(Thread.currentThread().getName() + "-------离开了车位");
} catch (InterruptedException e) {
throw new RuntimeException(e);
} finally {
semaphore.release();
}
},String.valueOf(i)).start();
}
}
9. 读写锁 ReentrantReadWriteLock
悲观锁和乐观锁
表锁和行锁,行锁会发生死锁
读锁和写锁,读锁是共享锁,写锁是独占锁,两者都会发生死锁
读写锁:一个资源可以被多个读线程访问,或者可以被一个写线程访问,但是不能同时存在读写线程,读写互斥,读读共享。
读写锁的演变过程:
锁降级:将写入锁降级为读锁
jdk8,先获取写锁,然后获取读锁,之后释放写锁,完成降级,最后释放读锁。
拥有写锁可以再次获取读锁,而拥有读锁,不能再获取写锁。
读锁不能升级为写锁。
10. 阻塞队列
分类:
方法:
11. 线程池ThreadPool
架构:
七个参数:
、
int corePoolSize,
int maximumPoolSize
long keepAliveTime
TimeUnit unit
BlockingQueue<Runnable> workQueue
ThreadFactory threadFactory
RejectedExecutionHandler handler
- corePoolSize
- 常驻线程数量(核心)
- maximumPoolSize
- 最大线程数量
- keepAliveTime
- 线程存活时间 值
- unit
- 线程存活时间 单位
- workQueue
- 阻塞队列
- threadFactory
- 线程工厂
- handler
- 拒绝策略,线程全部使用后的拒绝策略
底层工作流程:
自定义线程池:
ThreadPoolExecutor threadPool = new ThreadPoolExecutor(
2,
5,
2L,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(3),
Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
try {
for (int i = 1; i <= 10; i++) {
threadPool.execute(() -> {
System.out.println(Thread.currentThread().getName() + " 办理业务");
});
}
} finally {
threadPool.shutdown();
}
}
12. Fork/Join分支合并框架
class MyTask extends RecursiveTask<Integer> {
private static final Integer VALUE = 10;
private int begin;
private int end;
private int result;
public MyTask(int begin , int end){
this.begin = begin;
this.end = end;
}
@Override
protected Integer compute() {
if ((end-begin) <= 10){
for (int i = begin ; i < end ; i++) {
result += i;
}
} else {
int middle = (begin+end) >> 1;
MyTask task1 = new MyTask(begin,middle);
MyTask task2 = new MyTask(middle+1,end);
task1.fork();
task2.fork();
result = task1.join() + task2.join();
}
return result;
}
}
public class ForkJoinDemo {
public static void main(String[] args) throws ExecutionException, InterruptedException {
MyTask task = new MyTask(0,100);
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask<Integer> forkJoinTask = forkJoinPool.submit(task);
Integer res = forkJoinTask.get();
System.out.println(res);
forkJoinPool.shutdown();
}
}
13. 异步回调 CompletableFuture
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 异步调用,没有返回值
CompletableFuture<Void> completableFuture1 = CompletableFuture.runAsync(()->{
System.out.println(Thread.currentThread().getName()+"completableFuture1");
});
completableFuture1.get();
// 异步调用,有返回值
CompletableFuture<Integer> completableFuture2 = CompletableFuture.supplyAsync(()->{
System.out.println(Thread.currentThread().getName()+"completableFuture1");
return 1024;
});
completableFuture2.whenComplete((t,u)->{
System.out.println("----t="+t);
System.out.println("----u="+u);
}).get();
}