7.Callable(简单)
1、Callable 能够返回结果
2、能够报异常
3、方法与Runable 和Thread不同
代码:
package Callable;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* @Description:
* @Author Bowen
* @Date 2021/1/14 15:28
**/
public class DemoCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// new Thread().start();//怎么启动Callable
// new Thread(new FutureTask<V>()).start();
// new Thread(new FutureTask<V>(callable)).start();
// 因为想要实现调用callable就要实现runnable ,那么FutureTask就是Runnable的实现类
// 并且FutureTask的构造器方法中能够接收callable参数,所以即可完成调用
new Thread().start();
MyThread myThread = new MyThread();
FutureTask futureTask = new FutureTask(myThread);
new Thread(futureTask,"A").start();
new Thread(futureTask,"B").start(); //只打印一次Call(),因为有缓存
// 获取返回值,【可能会发生阻塞】:建议放在最后一行或者异步通信
Integer o = (Integer) futureTask.get();
System.out.println(o);
}
}
class MyThread implements Callable<Integer>{
@Override
public Integer call() throws Exception {
System.out.println("call()");
return 1024;
}
}
细节:
1、有缓存
2、可能会发生阻塞
8、常用的辅助类
8.1 CountDownLatch(重点)(减法计数器)
代码:
package SecUtils;
import java.util.concurrent.CountDownLatch;
/**
* @Description:
* @Author Bowen
* @Date 2021/1/14 16:37
**/
public class DemoCountDownLunch {
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 Bowen
},String.valueOf(i)).start();
}
countDownLatch.await();//等计数器归零才继续往下走;
System.out.println("close Door");
}
}
/**
结果为:
0go Out
2go Out
4go Out
3go Out
1go Out
5go Out
close Door
**/
方法:
0:初始化的时候规定初始数
1:countDown() 计数器-1
2:await() 当归零再向下执行
8.2 CyclicBarrier(加法计数器)
代码:
package SecUtils;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
/**
* @Description:
* @Author Bowen
* @Date 2021/1/14 16:44
**/
public class DemoCyclicBarrier {
public static void main(String[] args) {
// 集齐个数
CyclicBarrier cyclicBarrier = new CyclicBarrier(7,()->{
System.out.println("集齐7个");
});
for (int i = 0; i < 7 ; i++) {
// lamda表达式能够操作到i嘛 实际上是不能的
// 解决方法:将循环中的i赋值给一个final int 变量即可 不写final在1.8jvm中也会默认加上
int temp = i;
new Thread(()->{
try {
System.out.println("第"+temp+"个");
cyclicBarrier.await();// await0为共计有7个线程时,完成cyclicBarrier初始化中的lamda方法,并且阻塞
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
},i+"").start();
}
}
}
/**
结果:
第0个
第2个
第3个
第4个
第6个
第1个
第5个
集齐7个
**/
8.3 Semaphore(信号量,限流)
代码:
package SecUtils;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
/**
* @Description:
* @Author Bowen
* @Date 2021/1/15 8:39
**/
public class DemoSemaphore {
public static void main(String[] args) {
// 线程数量,限流
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(2);
System.out.println(Thread.currentThread().getName()+"离开位置");
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
semaphore.release();
}
},i+"").start();
}
}
}
/**
结果:
0进入位置
2进入位置
1进入位置
0离开位置
1离开位置
2离开位置
4进入位置
5进入位置
3进入位置
5离开位置
3离开位置
4离开位置
**/
原理:
Semaphore semaphore = new Semaphore(3); 初始化限制数
semaphore.acquire();记录线程数,如果达到上限,等待,等待释放为止
semaphore.release();释放线程数
作用:
多个共享资源互斥的使用!并发限流,控制最大的线程数。
9、读写锁
代码:
package lock;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
独占锁(写锁)只能一个线程占有
共享锁(读锁)多个线程可以同时占有
ReadWriteLock
读-读 可以共存
读-写 不能共存
写-写 不能共存
**/
public class ReadWriteLockDemo {
public static void main(String[] args) {
MyCache myCache = new MyCache();
// 存,
for (int i = 0; i < 100; i++) {
int temp = i;
new Thread(()->{
myCache.put(temp+"",temp+":value");
},i+"").start();
}
// 读取
for (int i = 0; i < 100; i++) {
int temp = i;
new Thread(()->{
myCache.get(temp+"");
},i+"").start();
}
}
}
class MyCache{
private volatile Map<String, Object> map = new HashMap<>();
// 读写锁,更加细粒度的操作
ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
// 存、写 写入的时候,只希望同时只有一个线程写入
public void put(String key, Object value){
readWriteLock.writeLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"写入…"+key);
map.put(key,value);
System.out.println(Thread.currentThread().getName()+"写入完成"+key);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.writeLock().unlock();
}
}
// 取、读 所有人都去读
public void get(String key){
readWriteLock.readLock().lock();
try {
System.out.println(Thread.currentThread().getName()+"读取…"+key);
Object o = map.get(key);
System.out.println(Thread.currentThread().getName()+"读取完成"+key);
} catch (Exception e) {
e.printStackTrace();
} finally {
readWriteLock.readLock().unlock();
}
}
}
阻塞队列
阻塞:
- 写入:如果队列满了,就必须阻塞
- 读取:如果队列是空的,必须阻塞,等待生产写入
队列:
- FIFO (First In Frist Out 模式)先进先出
什么情况下会使用阻塞队列:
多线程并发、线程池
学会使用队列:
添加、移除
四组API
方式 | 抛出异常 | 有返回值,不抛出异常 | 阻塞等待 | 超时等待 |
---|---|---|---|---|
添加 | add | offer() | put() | 重写offer(字段,值,TimeUnit单位) |
移除 | remove | poll() | take() | 重写poll(值,TimeUnit单位) |
判断队列首 | element | peek |
1、抛出异常
public static void test(){
// 初始化时要定义大小
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.add("a"));
System.out.println(blockingQueue.add("b"));
System.out.println(blockingQueue.add("c"));
// IllegalStateException:Queue full 抛出异常! 队列满了无法添加
System.out.println(blockingQueue.add("d"));
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
System.out.println(blockingQueue.remove());
// NoSuchElementException:队列中没有元素的 抛出异常! 队列空了无法删除
System.out.println(blockingQueue.remove());
}
2、不会抛出异常
public static void test02(){
// 初始化时要定义大小
ArrayBlockingQueue<Object> blockingQueue = new ArrayBlockingQueue<>(3);
System.out.println(blockingQueue.offer("a"));
System.out.println(blockingQueue.offer("b"));
System.out.println(blockingQueue.offer("c"));
// 与add方法不同,这个错误只会返回false不会报错
System.out.println(blockingQueue.offer("d"));
System.out.println(blockingQueue.poll());
// System.out.println(blockingQueue.remove());
}
3、阻塞等待
public static void test03() throws InterruptedException {
// 初始化
ArrayBlockingQueue<Object> bq = new ArrayBlockingQueue<>(3);
//等待、阻塞
bq.put("1");
bq.put("2");
bq.put("3");
//添加进不去之后就会一直阻塞等待
bq.put("4");
System.out.println(bq.take());
}
4、超时不等待
public static void test04() throws InterruptedException {
ArrayBlockingQueue<Object> bq = new ArrayBlockingQueue<>(2);
//超时不等待为重写offer/poll的方法
bq.offer("a",3, TimeUnit.SECONDS);
bq.offer("b",4,TimeUnit.SECONDS);
//如果添加失败 4秒后就放弃
bq.offer("b",4,TimeUnit.SECONDS);
bq.poll(2,TimeUnit.SECONDS);
}
SychronizedQueue
可以理解为一种特殊的阻塞队列,只能存储一个元素
put后需要take取出来之后才能继续put
11、线程池(重点)
三大方法,7大参数,4种拒绝策略
池化技术
程序运行的本质=》占用系统资源!应对=》优化资源的使用=》池化技术
例如:线程池、连接池、内存池、对象池
池化技术:实现配置准备好,有人用就在这里取,不会反复实例、销毁
线程池的好处:
1、降低资源的消耗
2、提高响应的速度
3、方便管理
线程复用
限制并发数
管理线程
三大方法
//使用线程池创建
public class Demo01 {
public static void main(String[] args) {
ExecutorService threadPool01 = Executors.newSingleThreadExecutor();//单个线程
ExecutorService threadPool02 = Executors.newFixedThreadPool(5);//创造一个固定的线程池大小
ExecutorService threadPool03 = Executors.newCachedThreadPool();//可伸缩的,根据情况改变大小
// 线程池用完,在程序结束前需要关闭线程池 Bowen
try {
for (int i = 0; i < 10; i++) {
threadPool03.execute(()->{
System.out.println(Thread.currentThread().getName()+"->OK");
});
}
} catch (Exception e) {
e.printStackTrace();
} finally {
threadPool03.shutdown();
}
// threadPool01
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// pool-1-thread-1OK
// threadPool02
// pool-1-thread-1->OK
// pool-1-thread-2->OK
// pool-1-thread-4->OK
// pool-1-thread-3->OK
// pool-1-thread-3->OK
// pool-1-thread-5->OK
// pool-1-thread-1->OK
// pool-1-thread-1->OK
// pool-1-thread-1->OK
// pool-1-thread-1->OK
// threadPool03
// pool-2-thread-1->OK
// pool-2-thread-2->OK
// pool-2-thread-3->OK
// pool-2-thread-1->OK
// pool-2-thread-1->OK
// pool-2-thread-3->OK
// pool-2-thread-4->OK
// pool-2-thread-2->OK
// pool-2-thread-6->OK
// pool-2-thread-5->OK
7大参数
// 本质ThreadPollExecutor
int corePoolSize,//核心线程池大小
int maxmumPoolSize // 最大核心线程池大小
long keepAliveTime // 超时了没有人调用会释放的线程数
TimeUnit unit,//超时单位
BlockingQueue<Runable> workQueue//阻塞队列
ThreadFactory threadFacotry //线程工厂,创建线程的,一般不会用
RejectedExecutionHandler handler // 拒绝策略
手动创建线程池
//最大线程数如何定义配置
// 1、CPU密集型 几核就 定义为几,可以保证CPU的效率最高 Runtime.getRuntime().avaliableProcessors();
// 2、IO密集型 判断程序中是否耗IO的线程,取两倍即可
// 程序中 15个大型任务 io是否占用资源!
ThreadPoolExecutor poolExecutor = new ThreadPoolExecutor(
2, //核心线程数2个,阻塞队列没满就一直是这两个线程在跑,阻塞队列满了就创建线程
5,//最大线程数为5,超过5个线程根据拒绝策略进行拒绝,最多只能创建5个线程
3,// 3秒超时自动删除核心线程外的线程
TimeUnit.SECONDS,//超时的时间单位
new LinkedBlockingDeque<>(4),//阻塞队列4可以最多容纳4个任务等待线程
Executors.defaultThreadFactory(),//默认线程创建工厂
new ThreadPoolExecutor.AbortPolicy());//队列满了,还有人进来就会不处理这个人,报异常
//由线程池创建线程方法:
poolExecutor.execute(()->{
})
4种拒绝策略
// 1 队列满了,还有任务进来就会不处理这个任务,报异常
new ThreadPoolExecutor.AbortPolicy()//
java.util.concurrent.RejectedExecutionException: Task poll.Demo02$$Lambda$1/990368553@7cca494b rejected from java.util.concurrent.ThreadPoolExecutor@7ba4f24f[Running, pool size = 5, active threads = 5, queued tasks = 4, completed tasks = 0]
at java.util.concurrent.ThreadPoolExecutor$AbortPolicy.rejectedExecution(ThreadPoolExecutor.java:2047)
at java.util.concurrent.ThreadPoolExecutor.reject(ThreadPoolExecutor.java:823)
at java.util.concurrent.ThreadPoolExecutor.execute(ThreadPoolExecutor.java:1369)
at poll.Demo02.main(Demo02.java:23)
// 2 哪里来的请求,由来源方处理,例如main的任务,就由main自己完成
new ThreadPoolExecutor.CallerRunsPolicy());
main->OK
main->OK
pool-1-thread-4->OK
pool-1-thread-4->OK
pool-1-thread-4->OK
//3 丢弃任务,不会抛出异常
new ThreadPoolExecutor.DiscardPolicy());//丢弃任务,不会抛出异常
// 4 丢弃阻塞队列中的任务,不会抛出异常
new ThreadPoolExecutor.DiscardOldestPolicy());
12、四大函数式接口(必须掌握)
新时代程序员,需要掌握1.8
- lamda表达式
- 链式编程
- 函数式接口
- Stream流式计算
函数式接口: Function 【输入一个值,处理后返回一个结果值】
Function function = str-> str;
System.out.println(function.apply("xxx"));
断定型接口: Predicate 【输入一个值,根据方法返回boolean类型】
Predicate<String> predicate = str ->{return str.isEmpty();};
System.out.println(predicate.test("str"));
消费型接口: Consumer 【只有参数,没有return】
Consumer consume = str -> System.out.println(str);
供给型接口: Supplier【只有返回值,没有参数】
Supplier supplier = () -> {return 1024;};
Stream流式计算
大数据:存储+计算
存储:MySQL 本质就是存储东西的;
计算都是应该交给流来操作的
package Stream;
import java.util.Arrays;
import java.util.List;
/**
* 流式计算
**/
public class Demo01 {
public static void main(String[] args) {
User u1 =new User(1,"a",21);
User u2 =new User(2,"b",22);
User u3 =new User(3,"c",23);
User u4 =new User(4,"d",24);
User u5 =new User(6,"e",26);
List<User> list = Arrays.asList(u1,u2,u3,u4,u5);
// 链式编程
list.stream()
.filter(u->{ return u.getId()%2==0 ;})
.filter(u->{return u.getAge()>23;})
.map(u->{return u.getName().toUpperCase();})
.sorted((uu2,uu1)->{return uu2.compareTo(uu1);})
.forEach(System.out::println);
}
}
class User{
@Override
public String toString() {
return "User{" +
"id=" + id +
", name='" + name + '\'' +
", age=" + age +
'}';
}
public User() {
}
public User(Integer id, String name, Integer age) {
this.id = id;
this.name = name;
this.age = age;
}
private Integer id;
private String name;
private Integer age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
}
Forkjoin
什么是Forkjoin
Forkjoin是1.7开始的,并行执行任务,提高效率!
大数据:Map Reduce(把大任务拆分为小任务再把小任务结果 汇总 return)
特点: 工作窃取
不让线程等待:例如AB两个线程,B先执行完任务,会把A未执行的任务偷走执行,顺序为双端队列的另一边
Demo类 需要继承RecursiveTask并重写compute方法
package Forkjoin;
import java.util.concurrent.RecursiveTask;
/**
通过ForkjoinPool 通过它来执行
forkjoinPool.execute(ForkjoinTask<?> task);
forkjoinTask
**/
public class Demo01 extends RecursiveTask<Long> {
private Long start;
private Long end;
private Long temp = 10000L;
public Demo01(Long start, Long end) {
this.start = start;
this.end = end;
}
@Override
protected Long compute() {
if((end-start)<temp){
Long sum = 0L;
for (Long i = start; i <= end; i++) {
sum += i;
}
return sum;
}else{
// forkjoin 递归
long middle = (start + end) / 2;//中间值
Demo01 task1 = new Demo01(start, middle);
task1.fork();
Demo01 task2 = new Demo01(middle, end);
task2.fork();
return task1.join() + task2.join();
}
}
}
测试类
package Forkjoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.LongStream;
/**
**/
public class test {
public static void main(String[] args) throws ExecutionException, InterruptedException {
test1();
}
// 1 Bowen
public static void test1(){
long sum = 0L;
long startTime = System.currentTimeMillis();
for (long i = 0; i <= 10_0000_0000; i++) {
sum += i;
}
long endTime = System.currentTimeMillis();
System.out.println(sum + ":" + (endTime-startTime));
}
public static void test2() throws ExecutionException, InterruptedException {
long startTime =System.currentTimeMillis();
ForkJoinPool forkJoinPool = new ForkJoinPool();
ForkJoinTask task = new Demo01(0L, 10_0000_0000L);
ForkJoinTask<Long> submit = forkJoinPool.submit(task);
Long sum = submit.get();
long endTime = System.currentTimeMillis();
System.out.println(sum + ":" + (endTime-startTime));
}
public static void test3(){
long sum = 0L;
long startTime = System.currentTimeMillis();
sum = LongStream.rangeClosed(0,10_0000_0000L).parallel().reduce(0,Long::sum);
long endTime = System.currentTimeMillis();
System.out.println(sum + ":" + (endTime-startTime));
}
}
15、异步回调
Future 设计的初衷是对将来的某个事件的结果进行建模
package Futrure;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
//异步执行
//成功回调
//失败回调
public class Demo01 {
public static void main(String[] args) throws ExecutionException, InterruptedException {
// 没有返回值的runAsync 异步回调
CompletableFuture<Void> objectCompletableFuture = CompletableFuture.runAsync(()->{
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println(Thread.currentThread().getName() + "runAsync => Void");
});
System.out.println("1111");
objectCompletableFuture.get();//获取阻塞执行结果
/*1111
ForkJoinPool.commonPool-worker-1runAsync => Void*/
// 有返回值的 supplyAsync 异步回调
CompletableFuture<Integer> uCompletableFuture = CompletableFuture.supplyAsync(() -> {
System.out.println(Thread.currentThread().getName() + "supplyAsync=>Integer");
return 1024;
});
// 可以理解为,上面的是异步请求的方法体,下面为ajax的success:function(){}和error:function(){}
uCompletableFuture.whenComplete((t,u)->{
System.out.println(t);//正常的返回结果
System.out.println(u);//报错的错误信息
}).exceptionally((e)->{
System.out.println(e.getMessage());
return 233;//可以获取到错误的返回结果
});
}
}