文章目录
1. 什么是JUC
java.util.concurrent
java.util.concurrent.locks
业务:普通的线程代码Thread
Runnable:没有返回值,效率相较于Callable较低.
2. 线程与进程
进程:一个程序的集合;一个进程可以包含至少一个或多个线程;
Java默认有几个线程?2个!一个main线程,一个GC线程。
线程:
Java真的可以开启线程吗?
答:不可以。
原因是Java没有权限开启一个线程,Java是通过native本地C++的方法调用来开启一个线程
public synchronized void start() {
if (threadStatus != 0)
throw new IllegalThreadStateException();
group.add(this);
boolean started = false;
try {
start0();
started = true;
} finally {
try {
if (!started) {
group.threadStartFailed(this);
}
} catch (Throwable ignore) {
}
}
}
//本地方法,调用C++
private native void start0();
并发、并行
并发(多线程操作同一个资源)
CPU单核,通过快速交换,模拟出来多个线程。
并行,多核CPU多个线程同时执行,线程池
public class Test01 {
public static void main(String[] args){
// 获取CPU的核心数,
//cpu密集型,io密集型
System.out.println(Runtime.getRuntime().availableProcessors());
}
}
并发编程的本质:充分利用CPU资源
多线程
线程有几个状态
public enum State {
//线程创建
NEW,
// 运行
RUNNABLE,
//阻塞
BLOCKED,
//等待
WAITING,
//超时等待
TIMED_WAITING,
//终止
TERMINATED;
}
wait和sleep的区别
- 来自不同的类: wait()来自Object类。sleep()来自Thread类。
- 关于锁的释放:wait()会释放锁,而sleep()不会释放锁。<记忆:因为睡着了不会释放锁>
- 使用的范围是不同的:wait必须在同步代码块中使用;sleep可以在任意地方使用。
- 是否需要捕获异常:wait()不需要捕获异常,而sleep需要捕获异常
3. Lock锁(!important)
传统的synchronized,解决并发中可能存在的问题
本质:队列,锁
锁:锁对象,锁class
Lock接口
lock()加锁;unlock()解锁;
实现类:
- ReentrantLock 可重入锁(常用)
- ReadLock 读锁
- WriteLock 写锁
公平锁:十分公平,先来后到
非公平锁:不公平的,可以插队(默认)
使用Lock锁的三步骤
- Lock lock = new ReentrantLock();
- lock.lock(); //加锁
- 在finally中使用lock.unlock()解锁
synchronized与lock的区别
- Synchronized 是Java内置的关键字,Lock 是一个Java类
- Synchronized 无法判断获取锁的状态,Lock可以判断是否获取到了锁
- Synchronized 会自动释放锁,而lock锁必须手动释放锁,若不释放锁则会发生死锁!
- Synchronized 线程1(获得锁)、线程2(等待);若线程1发生阻塞,则线程2将一直等待。而lock锁则不一定会一直等待。
- Synchronized:可重入锁,不可中断,非公平锁。Lock:可重入锁,可以判断锁,非公平(可以自己设置)
- Synchronized:适合锁少量的代码同步问题,Lock适合锁大量的代码同步问题。
锁是什么?如何判断锁的是谁?
4. 生产者消费者问题
Synchronized写法
package com.huathy.demo02_productConsumer;
/**
* @ClassPath: com.huathy.demo02_productConsumer.A
* @Author: Huathy
* @Description:
* @Date: 2021-01-07 20:13
*/
/**
* 线程之间的通信问题:生产者与消费者问题
* 线程交替执行,A B 操作同一个变量 num=0
* A num ++
* B num --
*/
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A" ).start();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B" ).start();
}
}
/**
* 数字:资源类
* 判断等待,业务,通知
*/
class Data{
private int num = 0;
public synchronized void increment() throws InterruptedException {
//这里写if判断会存在什么问题?虚假唤醒
if( num != 0 ){
//等待
this.wait();
}
num ++;
System.out.println(Thread.currentThread().getName() + " ==> " +num);
//通知其他线程
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
if( num == 0 ){
//等待
this.wait();
}
num --;
System.out.println(Thread.currentThread().getName() + " ==> " +num);
//通知其他线程
this.notifyAll();
}
}
虚假唤醒
当一个条件满足时,很多线程都被唤醒,但只有其中部分是有用的唤醒。
问题:存在A B C D四个线程,虚假唤醒!
解决方法:将上面代码中的if判断改为while判断
public class A {
public static void main(String[] args) {
Data data = new Data();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A" ).start();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B" ).start();
}
}
/**
* 数字:资源类
* 判断等待,业务,通知
*/
class Data{
private int num = 0;
public synchronized void increment() throws InterruptedException {
//这里写if判断会存在什么问题?虚假唤醒
while ( num != 0 ){
//等待
this.wait();
}
num ++;
System.out.println(Thread.currentThread().getName() + " ==> " +num);
//通知其他线程
this.notifyAll();
}
public synchronized void decrement() throws InterruptedException {
while ( num == 0 ){
//等待
this.wait();
}
num --;
System.out.println(Thread.currentThread().getName() + " ==> " +num);
//通知其他线程
this.notifyAll();
}
}
JUC写法的生产者与消费者问题
通过Lock 找到 Condition
condition.await(); //等待
condition.signalAll(); //唤醒全部
package com.huathy.demo02_productConsumer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassPath: com.huathy.demo02_productConsumer.B
* @Author: Huathy
* @Description:
* @Date: 2021-01-12 09:21
*/
public class B {
public static void main(String[] args) {
Data2 data = new Data2();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"A" ).start();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"B" ).start();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.increment();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"C" ).start();
new Thread( ()->{
try {
for (int i = 0; i < 20; i++) {
data.decrement();
}
} catch (InterruptedException e) {
e.printStackTrace();
}
},"D" ).start();
}
}
/**
* 数字:资源类
* 判断等待,业务,通知
*/
class Data2 {
private int num = 0;
Lock lock = new ReentrantLock();
Condition condition = lock.newCondition();
// condition.await(); //等待
// condition.signalAll(); //唤醒全部
public void increment() throws InterruptedException {
lock.lock();
try {
while (num != 0) {
//等待
condition.await();
}
num++;
System.out.println(Thread.currentThread().getName() + " ==> " + num);
//通知其他线程
condition.signalAll();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void decrement() throws InterruptedException {
lock.lock();
try {
while (num == 0) {
//等待
condition.await();
}
num--;
System.out.println(Thread.currentThread().getName() + " ==> " + num);
//通知其他线程
condition.signalAll();
}catch (Exception e){
e.printStackTrace();
}finally {
lock.unlock();
}
}
}
问题:现在的执行是随机的,如何做到精准的通知与唤醒线程?
Condition精准通知唤醒
使用多个监视器来实现精准的唤醒
package com.huathy.demo02_productConsumer;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @ClassPath: com.huathy.demo02_productConsumer.C
* @Author: Huathy
* @Description:
* @Date: 2021-01-12 09:48
*/
public class C {
public static void main(String[] args) {
Data3 data = new Data3();
new Thread( ()->{
for (int i = 0; i < 10; i++) {
data.printA();
}
} , "A" ).start();
new Thread( ()->{
for (int i = 0; i < 10; i++) {
data.printB();
}
} , "B" ).start();
new Thread( ()->{
for (int i = 0; i < 10; i++) {
data.printC();
}
} , "C" ).start();
}
}
class Data3{
Lock lock = new ReentrantLock();
Condition condition1 = lock.newCondition();
Condition condition2 = lock.newCondition();
Condition condition3 = lock.newCondition();
int num = 1;
public void printA(){
lock.lock();
try {
while (num != 1){
condition1.await();
}
System.out.println(Thread.currentThread().getName() + "==> AAA");
num = 2;
condition2.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printB(){
lock.lock();
try {
while (num != 2){
condition2.await();
}
System.out.println(Thread.currentThread().getName() + "==> BBB");
num = 3 ;
condition3.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
public void printC(){
lock.lock();
try {
while (num != 3){
condition3.await();
}
System.out.println(Thread.currentThread().getName() + "==> CCC");
num = 1;
condition1.signal();
} catch (Exception e) {
e.printStackTrace();
} finally {
lock.unlock();
}
}
}
5. 8锁现象
(为了理解什么是锁。称之为8锁现象)
如何判断锁?锁的是谁?
- Synchronized锁的对象是方法的调用者。
- 加了static的静态同步方法,锁的就是Class类模板。
6. 集合类不安全
List不安全
安全:CopyOnWriteArrayList
package com.huathy.unsafe;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
/**
* @ClassPath: com.huathy.unsafe.Test01
* @Author: Huathy
* @Description:
* @Date: 2021-01-12 14:18
*/
// java.util.ConcurrentModificationException 并发修改异常!
public class Test01 {
public static void main(String[] args) {
// List<String> list = Arrays.asList("1","2","3","4");
// list.forEach(System.out::println);
/**
* 在并发下ArrayList是不安全的
* 解决方案:
* 1. List<String> list1 = new Vector<>();
* 2. List<String> list1 = Collections.synchronizedList(new ArrayList<>());
* 3. List<String> list1 = new CopyOnWriteArrayList<>();
* //建议使用这种JUC的写法
* CopyOnWriteArrayList 比 Vector的效率要高
* 因为Vector使用的是synchronized锁,而CopyOnWriteArrayList使用的是Lock锁
*/
/**
* CopyOnWrite 写入时复制,COW 是计算机程序设计领域的优化策略
* 多个线程调用时,list在读取时是唯一的,在写入时可能会存在覆盖操作。
* 在写入时,为避免覆盖造成数据问题。先进行覆盖。
* 读写分离思想
*/
// List<String> list1 = new ArrayList<>();
// List<String> list1 = new Vector<>();
// List<String> list1 = Collections.synchronizedList(new ArrayList<>());
List<String> list1 = new CopyOnWriteArrayList<>();
for (int i = 0; i < 10; i++) {
new Thread( ()->{
list1.add(UUID.randomUUID().toString().substring(0,5));
System.out.println(list1);
},String.valueOf(i) ).start();
}
}
}
Set不安全
package com.huathy.unsafe;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.CopyOnWriteArraySet;
/**
* @ClassPath: com.huathy.unsafe.SetTest
* @Author: Huathy
* @Description:
* @Date: 2021-01-12 14:55
*/
//java.util.ConcurrentModificationException 并发修改异常
public class SetTest {
public static void main(String[] args) {
// Set<String> set = new HashSet<>();
// Set<String> set = Collections.synchronizedSet(new HashSet<>());
Set<String> set = new CopyOnWriteArraySet<>();
for (int i = 0; i < 20; i++) {
new Thread( ()->{
set.add( UUID.randomUUID().toString().substring(0,5) );
System.out.println(set);
} , String.valueOf(i) ).start();
}
}
}
HashSet底层原理
public HashSet() {
map = new HashMap<>();
}
//add set本质就是map的key,因为map的key是不能重复的
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
//常量
private static final Object PRESENT = new Object();
Map不安全
HashMap不安全
ConcurrentHashMap
package com.huathy.unsafe;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
/**
* @ClassPath: com.huathy.unsafe.MapTest
* @Author: Huathy
* @Description:
* @Date: 2021-01-13 11:53
*/
/**
* ConcurrentModificationException
*/
public class MapTest {
public static void main(String[] args) {
// map是这样用的嘛? 不是
// 默认等价于什么?
// Map<String , Object> map = new HashMap<>();
//加载因子,初始容量
//解决方案
Map<String, Object> map = new ConcurrentHashMap<>();
for (int i = 0; i < 30; i++) {
new Thread( ()->{
map.put(Thread.currentThread().getName(), UUID.randomUUID().toString().substring(0,5));
System.out.println(map);
} , String.valueOf(i) ).start();
}
}
}
7. Callable
类似于Runnable,都是为其实例可能由另一线程执行的类设计的。而Runnable不返回结果,也不能抛出被检查的异常。
- 可以有返回值
- 可以抛出异常
- 方法不同call()
package com.huathy.callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @ClassPath: com.huathy.callable.CallableTest
* @Author: Huathy
* @Description:
* @Date: 2021-01-13 14:09
*/
public class CallableTest {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread(new Runnable() {}).start();
// new Thread(new FutureTask<>()).start();
// new Thread(new FutureTask<>(Callable)).start();
FutureTask futureTask = new FutureTask(new MyThread()); //适配类
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start();
//获取Callable的返回结果
//这个get方法可能会产生阻塞,所以放到最后。或者使用异步通信来处理
Integer res = (Integer) futureTask.get();
System.out.println(res);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call");
return 1234;
}
}
细节:1. 有缓存 2.结果可能需要等待,会阻塞。
8. 常用的辅助类
CountDownLatch
减法计数器
package com.huathy.auxiliary;
import java.util.concurrent.CountDownLatch;
/**
* @ClassPath: com.huathy.auxiliary.CountDownLatch
* @Author: Huathy
* @Description:
* @Date: 2021-01-13 16:50
*/
public class CountDownLatchTest {
public static void main(String[] args) throws InterruptedException {
//总数是6,在有必须要执行任务的时候使用
CountDownLatch countDownLatch = new CountDownLatch(6);
for (int i = 0; i < 6; i++) {
new Thread( ()->{
System.out.println(Thread.currentThread().getName() + " go out");
countDownLatch.countDown(); //数量-1
} , String.valueOf(i) ).start();
}
//等待计数器归零后继续向下执行
countDownLatch.await();
System.out.println("go end");
}
}
原理:1. countDownLatch.countDown(); //数量-1
2. countDownLatch.await();
每次有线程调用countDown()数量-1,假设计数器数量变为0,countDownLatch.await()就会被唤醒,继续执行。
CyclicBarrier
加法计数器
package com.huathy.auxiliary;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @ClassPath: com.huathy.auxiliary.CyclicBarrierTest
* @Author: Huathy
* @Description:
* @Date: 2021-01-13 17:22
*/
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(7, ()->{
System.out.println("召唤神龙!");
} );
for (int i = 0; i < 7; i++) {
final int temp = i;
//lambda能拿到上面的i嘛? 不能。
new Thread( ()->{
System.out.println(Thread.currentThread().getName() + " 收集第 " + temp + " 龙珠");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
} ).start();
}
}
}
Semaphore
计数信号量。
package com.huathy.auxiliary;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @ClassPath: com.huathy.auxiliary.SemaphoreTest
* @Author: Huathy
* @Description:
* @Date: 2021-01-13 17:34
*/
public class SemaphoreTest {
public static void main(String[] args) {
//初始化:3个信号量。 使用场景:限流
Semaphore semaphore = new Semaphore(3);
for (int i = 0; i < 6; i++) {
new Thread( ()->{
try {
semaphore.acquire(); //获取
System.out.println(Thread.currentThread().getName() + " 获得信号量");
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println(Thread.currentThread().getName() + " 释放信号量");
semaphore.release(); //释放
}
},String.valueOf(i) ).start();
}
}
}
原理:
- semaphore.acquire(); //获取,假设已经满了,等待,等待被释放为止。
- semaphore.release(); //释放,会将当前的信号量释放,并+1.然后唤醒等待的线程。
使用场景:多个共享资源的互斥使用!并发限流,控制最大线程数量。
9. ReadWriteLock 读写锁
package com.huathy.rw;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* @ClassPath: com.huathy.rw.ReadWriteLockTest
* @Author: Huathy
* @Description:
* @Date: 2021-01-13 17:48
*/
/**
* 共享锁:多个线程可以同时占有
* 排他锁:一次只能被一个线程占有
* readWriteLock
* 读-读 共享锁
* 读-写 排他锁
* 写-写 排他锁
*/
public class ReadWriteLockTest {
public static void main(String[] args) {
MyCatchLock myCatch = new MyCatchLock();
//写入
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread( ()->{
myCatch.put(temp+"",temp+"");
} , String.valueOf(i) ).start();
}
//读取
for (int i = 0; i < 5; i++) {
final int temp = i;
new Thread( ()->{
myCatch.get(temp+"");
} , String.valueOf(i) ).start();
}
}
}
/**
* 自定义缓存
* 加锁的
*/
class MyCatchLock{
private volatile Map<Object,Object> map = new HashMap<>();
//读写锁:更加细粒度的控制
private ReadWriteLock readWriteLock = new ReentrantReadWriteLock();
//存 写 只希望一个线程操作
public void put(String key,Object val){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName() + " 写入 " + key);
map.put(key,val);
System.out.println(Thread.currentThread().getName() + " 写入完毕");
} finally {
readWriteLock.writeLock().unlock();
}
}
//取 读
public Object get(String key){
System.out.println(Thread.currentThread().getName() + " 读取 " + key);
return map.get(key);
}
}
/**
* 自定义缓存
* 没有加锁
*/
class MyCatch{
private volatile Map<Object,Object> map = new HashMap<>();
//存 写
public void put(String key,Object val){
System.out.println(Thread.currentThread().getName() + " 写入 " + key);
map.put(key,val);
System.out.println(Thread.currentThread().getName() + " 写入完毕");
}
//取 读
public Object get(String key){
System.out.println(Thread.currentThread().getName() + " 读取 " + key);
return map.get(key);
}
}
10. 阻塞队列
Queue 队列
Deque 双端队列
FIFO先进先出
写入:等待,阻塞
读取:if队列空,需要等待生产
阻塞队列:BlockingQueue
实现类
- SynchronousQueue:同步队列
- ArrayBlockingQueue
- LinkedBlockingQueue
使用队列
BlockingQueue的四组API
- 抛出异常
- 不抛异常
- 阻塞等待
- 超时等待
方式 | 抛出异常 | 不抛异常,有返回值 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add() | offer() | put() | offer() |
移除 | remove() | poll() | take() | poll() |
判断队列首 | element() | peek() | - | - |
package com.huathy.bq;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.TimeUnit;
/**
* @ClassPath: com.huathy.bq.Test01
* @Author: Huathy
* @Description:
* @Date: 2021-01-13 19:46
*/
public class Test01 {
public static void main(String[] args) throws InterruptedException {
// test1();
// test2();
// test3();
test4();
}
/**
* 抛出异常
*/
public static void test1(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.add("a"));
System.out.println(arrayBlockingQueue.add("b"));
System.out.println(arrayBlockingQueue.add("c"));
//已满再加:java.lang.IllegalStateException: Queue full
// System.out.println(arrayBlockingQueue.add("d"));
System.out.println("========================");
System.out.println(arrayBlockingQueue.element());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
System.out.println(arrayBlockingQueue.remove());
//已经没有元素:java.util.NoSuchElementException
// System.out.println(arrayBlockingQueue.element());
//没有再取:java.util.NoSuchElementException
// System.out.println(arrayBlockingQueue.remove());
}
/**
* 不抛异常,有返回值
*/
public static void test2(){
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(arrayBlockingQueue.offer("a"));
System.out.println(arrayBlockingQueue.offer("b"));
System.out.println(arrayBlockingQueue.offer("c"));
System.out.println(arrayBlockingQueue.offer("d"));
System.out.println("======================");
System.out.println("队首:" + arrayBlockingQueue.peek());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println("队首:" + arrayBlockingQueue.peek());
}
/**
* 阻塞等待
* @throws InterruptedException
*/
public static void test3() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
arrayBlockingQueue.put("a");
arrayBlockingQueue.put("b");
arrayBlockingQueue.put("c");
//队列没有位置,则一直等待
// arrayBlockingQueue.put("d");
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
System.out.println(arrayBlockingQueue.take());
//队列中没有元素,则一直等待
// System.out.println(arrayBlockingQueue.take());
}
/**
* 超时等待
* @throws InterruptedException
*/
public static void test4() throws InterruptedException {
ArrayBlockingQueue arrayBlockingQueue = new ArrayBlockingQueue(3);
arrayBlockingQueue.offer("a");
arrayBlockingQueue.offer("b");
arrayBlockingQueue.offer("c");
//超时等待3s退出
arrayBlockingQueue.offer("d",3, TimeUnit.SECONDS);
System.out.println("================");
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll());
System.out.println(arrayBlockingQueue.poll(3,TimeUnit.SECONDS));
}
}
同步队列 SynchronousQueue
package com.huathy.bq;
/**
* @ClassPath: com.huathy.bq.SynchronizedQueueDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 09:41
*/
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
/**
* 同步队列
* 和其他的BlockingQueue不一样,Synchronous不存储元素
* put了一个元素,必须先从里面take出来,否则不能put。
*/
public class SynchronousQueueDemo {
public static void main(String[] args) {
BlockingQueue<String> synchronousQueue = new SynchronousQueue<>();
new Thread( ()->{
try {
System.out.println(Thread.currentThread().getName() + " put 1");
synchronousQueue.put("1");
System.out.println(Thread.currentThread().getName() + " put 2");
synchronousQueue.put("2");
System.out.println(Thread.currentThread().getName() + " put 3");
synchronousQueue.put("3");
} catch (InterruptedException e) {
e.printStackTrace();
}
} ,"T1" ).start();
new Thread( ()->{
try {
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
TimeUnit.SECONDS.sleep(3);
System.out.println(Thread.currentThread().getName() + " " + synchronousQueue.take());
} catch (InterruptedException e) {
e.printStackTrace();
}
} ,"T2" ).start();
}
}
11. 线程池 !
JUC线程池有:3大方法、7个参数、4种拒绝策略。
池化技术
程序的运行,会占用系统内资源。池化技术可以优化资源的使用。线程池,连接池,内存池,对象池。
线程池的好处:线程复用、最大并发数可控、管理线程
- 降低资源开销
- 提高相应速度
- 方便管理
Executors
package com.huathy.poll;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* @ClassPath: com.huathy.poll.Demo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 15:29
*/
public class Demo01 {
public static void main(String[] args) {
// ExecutorService threadPool = Executors.newSingleThreadExecutor();//创建单个线程
// ExecutorService threadPool = Executors.newFixedThreadPool(5); //创建固定大小的线程池
ExecutorService threadPool = Executors.newCachedThreadPool(); //创建可伸缩的线程池
try {
for (int i = 0; i < 50; i++) {
threadPool.execute( ()->{
System.out.println(Thread.currentThread().getName() + " ok");
} );
}
} finally {
threadPool.shutdown();
}
}
}
7大参数
源码
public static ExecutorService newSingleThreadExecutor() {
return new FinalizableDelegatedExecutorService
(new ThreadPoolExecutor(1, 1,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>()));
}
public static ExecutorService newFixedThreadPool(int nThreads) {
return new ThreadPoolExecutor(nThreads, nThreads,
0L, TimeUnit.MILLISECONDS,
new LinkedBlockingQueue<Runnable>());
}
public static ExecutorService newCachedThreadPool() {
return new ThreadPoolExecutor(0, Integer.MAX_VALUE, //21亿 OOM
60L, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>());
}
本质是:new ThreadPoolExecutor
public ThreadPoolExecutor(int corePoolSize, //核心线程数
int maximumPoolSize, //最大核心线程池大小
long keepAliveTime, //超时释放时间
TimeUnit unit, //时间单位
BlockingQueue<Runnable> workQueue, //阻塞队列
ThreadFactory threadFactory, //线程工厂,创建线程的,一般不动
RejectedExecutionHandler handler //拒绝策略
) {
if (corePoolSize < 0 ||
maximumPoolSize <= 0 ||
maximumPoolSize < corePoolSize ||
keepAliveTime < 0)
throw new IllegalArgumentException();
if (workQueue == null || threadFactory == null || handler == null)
throw new NullPointerException();
this.acc = System.getSecurityManager() == null ?
null :
AccessController.getContext();
this.corePoolSize = corePoolSize;
this.maximumPoolSize = maximumPoolSize;
this.workQueue = workQueue;
this.keepAliveTime = unit.toNanos(keepAliveTime);
this.threadFactory = threadFactory;
this.handler = handler;
}
四种拒绝策略
ThreadPoolExecutor.AbortPolicy() //终止策略:拒绝服务,抛出异常
ThreadPoolExecutor.CallerRunsPolicy() //调用者运行策略:不抛异常
ThreadPoolExecutor.DiscardPolicy() //丢弃策略:不抛异常
ThreadPoolExecutor.DiscardOldestPolicy() //放弃最早的策略:尝试获取资源,不抛异常
自定义线程池
package com.huathy.poll;
import java.util.concurrent.*;
/**
* @ClassPath: com.huathy.poll.Demo01
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 15:29
*/
/**
* 四种拒绝策略:
* new ThreadPoolExecutor.AbortPolicy() //终止策略:拒绝服务,抛出异常
* new ThreadPoolExecutor.CallerRunsPolicy() //调用者运行策略:不抛异常
* new ThreadPoolExecutor.DiscardPolicy() //丢弃策略:不抛异常
* new ThreadPoolExecutor.DiscardOldestPolicy() //放弃最早的策略:尝试获取资源,不抛异常
*/
public class Demo02 {
public static void main(String[] args) {
ExecutorService executorService = new ThreadPoolExecutor(
2, //核心线程数
5, //最大核心线程池大小
3, //超时释放时间
TimeUnit.SECONDS, //时间单位
new LinkedBlockingQueue<>(3), //阻塞队列
Executors.defaultThreadFactory(), //线程工厂
// new ThreadPoolExecutor.AbortPolicy() //终止策略:拒绝服务,抛出异常
// new ThreadPoolExecutor.CallerRunsPolicy() //调用者运行策略:不抛异常
// new ThreadPoolExecutor.DiscardPolicy() //丢弃策略:不抛异常
new ThreadPoolExecutor.DiscardOldestPolicy() //放弃最早的策略:
);
try {
//最大承载:Queue + max
//如果超出承载,则抛出java.util.concurrent.RejectedExecutionException
for (int i = 1; i <= 100; i++) {
executorService.execute( ()->{
System.out.println(Thread.currentThread().getName() + " ok");
} );
}
} finally {
executorService.shutdown();
}
}
}
小结 & 扩展
最大线程数量到底如何定义:
- CPU密集型:几核心就定义多少。这样可以保持CPU效率最高。
- IO密集型:一个程序有n个大型任务,io消耗资源。则最大数可以定义比n大。可以是2倍。
12. 四大函数式接口 !
lambda表达式、链式编程、函数式接口、Stream流式计算。
FunctionInterface 函数式接口:只有一个方法的接口。
简化编程模型,在新版底层中大量使用。foreach(消费者类的函数式接口)
- Consumer:
- Function:函数式接口。参数:一个接收类型,一个返回类型。
- Predicate:断定型接口。参数:一个接收类型,返回Boolean值。
- Supplier:
代码示例:
- Function:函数式接口
package com.huathy.function;
import java.util.function.Function;
/**
* @ClassPath: com.huathy.function.FunctionDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 20:13
*/
/**
* 函数式接口
* 参数:一个输入类型,一个返回类型。
*/
public class FunctionDemo {
public static void main(String[] args) {
// Function function = new Function<Integer, String>() {
// @Override
// public String apply(Integer integer) {
// return (++ integer).toString();
// }
// };
Function<Integer,String> function = (i)->{return (++ i).toString();};
System.out.println(function.apply(1));
}
}
- PredicateDemo:断定型接口
package com.huathy.function;
import java.util.function.Predicate;
/**
* @ClassPath: com.huathy.function.PredicateDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 20:20
*/
/**
* 断定型接口。
* 参数:一个接收类型。返回Boolean值
*/
public class PredicateDemo {
public static void main(String[] args) {
// Predicate<Integer> predicate = new Predicate<Integer>(){
// @Override
// public boolean test(Integer i) {
// return i > 0;
// }
// };
Predicate<Integer> predicate = (i) -> { return i>0; };
System.out.println(predicate.test(2));
}
}
- Consumer消费型接口
package com.huathy.function;
import java.util.function.Consumer;
/**
* @ClassPath: com.huathy.function.ConsumerDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 20:34
*/
/**
* 消费型接口
*/
public class ConsumerDemo {
public static void main(String[] args) {
// Consumer<Object> consumer = new Consumer<Object>(){
// @Override
// public void accept(Object o) {
// System.out.println(o);
// }
// };
Consumer<Object> consumer = (o) -> {
System.out.println(o);
};
consumer.accept(123);
}
}
- Supplier供给型接口
package com.huathy.function;
import java.util.function.Supplier;
/**
* @ClassPath: com.huathy.function.SupplierDemo
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 20:38
*/
/**
* 供给型接口
*/
public class SupplierDemo {
public static void main(String[] args) {
// Supplier<Integer> supplier = new Supplier<Integer>() {
// @Override
// public Integer get() {
// return 1024;
// }
// };
Supplier<Integer> supplier = ()->{ return 1024; };
System.out.println(supplier.get());
}
}
13. Stream流式计算
什么是Stream流式计算。
package com.huathy.stream;
/**
* @ClassPath: com.huathy.stream.Test
* @Author: Huathy
* @Description:
* @Date: 2021-01-14 20:48
*/
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Arrays;
import java.util.List;
/**
* 题目要求:一分钟,一行代码,从5个用户中筛选
* 1. ID是偶数
* 2. 年龄>25
* 3. 用户名转为大写
* 4. 用户名字母倒序
* 5. 只输出一个用户
*/
public class Test {
public static void main(String[] args) {
User u1 = new User(1,"a",10);
User u2 = new User(2,"b",20);
User u3 = new User(3,"c",30);
User u4 = new User(4,"d",40);
User u5 = new User(6,"e",50);
//集合用来存储
List<User> list = Arrays.asList(u1, u2, u3, u4, u5);
//用stream流来计算
list.stream().
//过滤:id为偶数,且年龄>25
filter( u -> { return u.getId()%2==0 && u.getAge()>25; })
//转换:姓名转为大写
.map( u -> { return u.getName().toUpperCase(); } )
//排序 倒序
.sorted( (us1,us2) -> { return us2.compareTo(us1); } )
//分页:只输出一个
.limit(1)
.forEach(System.out::println);
}
}
@Data
@NoArgsConstructor
@AllArgsConstructor
class User {
private int id;
private String name;
private int age;
}
14. ForkJoin 分支合并
什么是ForkJoin。并发执行任务!提高效率。大数据量。
大数据:Map Reduce(将大任务拆分成小任务)
特点:工作窃取。线程B执行完任务后,将A线程没有执行的任务拿来执行。可以提高工作效率。
这个里面维护的都是双端队列。
ForkJoin使用步骤:
- ForkJoinPool 通过它来执行
- ForkJoinTask:实现类(RecursiveAction递归事件,没有返回值;RecursiveTask递归任务,有返回值)
15. 异步回调
Future 设计初衷:对将来的某个事件进行建模。
实现类:CompletableFuture
package com.huathy.future;
/**
* @ClassPath: com.huathy.future.Demo001
* @Author: Huathy
* @Description:
* @Date: 2021-01-15 23:32
*/
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
/**
* 异步调用:CompletableFuture
* 类比Ajax发起请求
* 1. 异步执行
* 2. 成功回调
* 3. 失败回调
*/
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// noReturnTest();
returnTest1();
}
public static void noReturnTest() throws ExecutionException, InterruptedException {
//没有返回值的runAsync异步回调。
CompletableFuture<Void> completableFuture = CompletableFuture.runAsync(() -> {
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + " runAsync => Void");
});
System.out.println("===========");
//阻塞获取执行结果
completableFuture.get();
}
public static void returnTest1() throws ExecutionException, InterruptedException {
//有返回值的supplyAsync异步回调。
CompletableFuture<Integer> completableFuture = CompletableFuture.supplyAsync( ()-> {
System.out.println(Thread.currentThread().getName() + " supplyAsync => Integer");
int i = 10 / 0; //测试exception
return 1024;
} );
CompletableFuture<Integer> exceptionally = completableFuture.whenComplete((t, u) -> {
System.out.println("t=>" + t);
System.out.println("u=>" + u);
}).exceptionally((e) -> {
System.out.println(e.getMessage());
return -1;
});
//阻塞获取执行结果
System.out.println( exceptionally.get() );
}
}