执行器
执行器 文字说明
执行器 - Callable与Futrue
执行器 演示代码
package com.performer.demo1;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
/**
* 执行器 调用类
* 演示目标
* 两条线程,分别计算 1 - 100 之间数字的和,100 - 100000 之间数字的和
* */
public class DemoCase {
public static void main(String[] args) throws Exception {
//创建ExecutorService对象,且2个线程的执行器
ExecutorService es = Executors.newFixedThreadPool(2);
/**
* Future 是callabel返回值对象
* es.submit()方法就是callabel提交到线程中执行
* */
Future<Integer> f1 = es.submit(new MC(1, 100)); // 提交任务
Future<Integer> f2 = es.submit(new MC(100, 100000));
//通过get()方法 获取 call接口返回值
System.out.println(f1.get() + ":" + f2.get());
es.shutdown(); // 停止执行器
}
}
// 1.实现 Callabel接口,并指定泛型的类型
class MC implements Callable<Integer> // Callable泛型 是指定执行结果的返回类型
{
// 2.创建共有变量
private int begin, end;
// 创建类的有参构造函数
public MC(int begin, int end) {
this.begin = begin;
this.end = end;
}
/**
* 3.实现了 callable接口方法 使用call方法计算 begin 到 end 数之间的和
* call方法 返回线程执行结果
* */
@Override
public Integer call() throws Exception {
int sum = 0;
for (int i = begin; i < end; i++) {
sum += i;
}
return sum;
}
}
执行器 - 运行结果
4950:704977754
执行器 使用总结
执行器 API介绍 和使用步骤:
1.在功能类中实现 Callable接口时,可以自定义线程的返回值类型。
2.Callable接口 提供call方法,用于返回线程执行结果,
3。在调用类中 首先创建 ExecutorService对象实例,使用ExecutorService 对象的 submit()方法 提交线程类。
5.提交后,获取到Future对象实例,通过它的 get()方法获取线程执行的返回值。
6.最后使用 ExecutorService线程管理对象的实例的shutdown() 方法,结束执行器。
ps:简单来说
Callable接口 的作用:功能类 实现 Callable接口 就是相当于实现了一个 有返回值的线程。
ExecutorService接口 的作用:控制线程执行和管理线程
Future接口 的作用:获取 Callable接口实现类 的返回值
锁与原子操作
锁 与 原子操作 - API
锁 与 原操作 - 演示代码
package com.lock.demo1;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
public class DemoCase {
public static void main(String[] args) {
new MT().start();
new MT().start();
new MT().start();
new MT().start();
new MT().start();
}
}
class Data {
static int i = 0;
static Lock lock = new ReentrantLock(); // 创建锁对象实例
// static AtomicInteger ai = new AtomicInteger(0); //原子操作
static void operate() {
lock.lock(); // 开始加锁,保护资源同一时间只能被一个线程操作
i++;
System.out.println(i);
lock.unlock(); // 关闭锁,因为后面没有资源需要保护
// System.out.println(ai.incrementAndGet()); //原子操作 获取方法
}
}
class MT extends Thread {
@Override
public void run() {
while (true) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Data.operate();
}
}
}
不加锁 或者不使用原子操作的 运行结果
3
3
4
3
5
6
9
10
8
7
11
15
14
13
13
加锁 或者使用原子操作后的结果
1
2
3
4
5
6
7
8
9
10
11
12
13
锁 与 原子操作 使用总结
锁 与 原子操作 的相同点:
1.保护共有资源的独立性,保证数据在同一时间只能被一条线程操作,从而保证数据的正确性和不重复性。
锁 与 原子操作 的不相同点:
1。使用锁而获取到数据是有序的,反之原子操作是无序的。
流编程
流编程 - 基本知识
流编程 - 流的基本操作
<>
流编程 - 演示代码
package com.stream;
import java.util.ArrayList;
import java.util.List;
public class DemoCase {
public static void main(String[] args) {
/**
* 1.List:是一个有序的集合,可以包含重复的元素。提供了按索引访问的方式。它继承 Collection。
2.List有两个重要的实现类:ArrayList 和 LinkedList
3.ArrayList:我们可以将其看作是能够自动增长容量的数组。
4.利用ArrayList的toArray()返回一个数组。
5.Arrays.asList()返回一个列表。
* */
// 6.下面这样写法的好处:List是接口继承于Collection接口。ArrayList是List接口的实现类。相当于一个动态数组
List<String> list = new ArrayList<>();
list.add("a");
list.add("c");
list.add("d");
list.add("b");
list.add("e");
list.add("e");
// ps:max是终端操作
// //通过 stream方法获取流 ,通过max方法求流中最大的值,String::compareTo是筛选器
// Optional<String> max = list.stream().max(String::compareTo);
//
// //使用get()方法回去返回值
// System.out.println(max.get());
//输出流中不重复的数据的数量
System.out.println("不重复的数据有:"+list.stream().distinct().count()+"个");
//下面使用中间操作
/**
* 运行步骤是获取流 -》进行排序,获得到了中间流 -》使用foreach 循环遍历 -》输出
* */
System.out.println("数据排序后的输出结果:");
list.stream().sorted().forEach(e ->System.out.println(e));
}
}
流编程 - 运行结果
不重复的数据有:5个
数据排序后的输出结果:
a
b
c
d
e
e
使用总结
1.首先 stream 是在JDK8才有的,要使用必须保证自己的jdk版本是 1.8。
2.eclispe的版本要在4.3以上,要支持1.8的语法。
3.流的编程模型
A.获取流:stream/parallelSteam
B.操作:sort/max/min...
4.流编程的作用:简化了我们处理数据,简单直接的达到了我们想要的结果。
Fork/Join 框架
Fork/Join - API
分而治之策略
Fork/Join - 演示代码
package com.forkJoin;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.Future;
import java.util.concurrent.RecursiveTask;
/**
* 任务目标: 计算 1-10w的和
* 实现步骤:分为两部分计算。分别计算1-5w的和,5w-10w的和
* */
public class DemoCase {
public static void main(String[] args) {
/*
* ForkJoinPool: 管理ForkJoinTask的线程池
* */
ForkJoinPool forkJoinPool = new ForkJoinPool();
//Future 表示结果返回值
Future<Long> result = forkJoinPool.submit(new MTask(0, 1000)); //提交
try {
System.out.println("总计:"+result.get()); //输出
} catch (InterruptedException | ExecutionException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
forkJoinPool.shutdown(); // 停止
}
}
/**
* 功能类:1.继承 RecursiveTask 抽象类,
* RecursiveTask 抽象类的特性:具有返回值
* */
class MTask extends RecursiveTask<Long>
{
//2.声明共有变量和常量
private int begin,end; //开始 和 结束变量
/**
* 根据 java API分而治之策略 建议临界点定义在100-1000个操作中的位置
*
* 限额1000,超过则继续划分成多个不超过1000的子任务
*
* */
static final int limit = 100;
//3.创建类的有参构造,传入参数
public MTask(int begin,int end)
{
this.begin = begin;
this.end = end ;
}
/**
*4. 实现 RecursiveTask抽象类的 compute方法,
* compute方法的作用是,任务执行后获取返回值
* */
@Override
protected Long compute() {
long sum = 0;
//判断是否超过限额1000
if(end - begin <= limit)
{
for (int i = begin; i < end; i++) {
sum += i;
}
}else //超过限额,把一个任务划分2个子任务
{
int mid = (begin + end) / 2;
//拆分
MTask left = new MTask(begin, mid);
left.fork();
MTask right= new MTask(mid + 1, end);
right.fork();
//分辨计算, 合并结果
long lr = left.join();
System.out.println(begin +"-"+mid+":"+lr);
long rr = right.join();
System.out.println(mid +"-"+end+":"+rr);
sum = lr + rr ;
}
return sum;
}
}
Fork/Join - 运行结果
251-313:17453
126-188:9703
0-62:1891
501-563:32953
63-125:5797
189-250:13359
314-375:20984
0-125:7688
564-625:36234
126-250:23062
251-375:38437
0-250:30750
376-438:25203
439-500:28609
751-813:48453
376-500:53812
501-625:69187
251-500:92249
814-875:51484
876-938:56203
0-500:122999
626-688:40703
939-1000:59109
751-875:99937
689-750:43859
876-1000:115312
626-750:84562
501-750:153749
751-1000:215249
501-1000:368998
总计:491997
Fork/Join - 使用总结
Fork/Join 框架的特点的:
1.分而治之的策略,让我们的程序可以最大、最优、最快的运行,特别适合大数据的计算,和统计。